Home > Net >  insert multiple objects into nested arrays with condition
insert multiple objects into nested arrays with condition

Time:11-14

I'm a mongo beginner and struggling to insert multiple objects into multiple nested array in one document.

The document looks like this:

[
  {
    "id": 1,
    "name": "myObject",
    "sections": [
      {
        "id": "section1",
        "items": [
          {
            "id": 1,
            "name": "item1.1",
            "scores": [
              {
                "userId": 13,
                "score": 10
                
              }
            ]
          },
          {
            "id": 2,
            "name": "item1.2",
            "scores": [
              {
                "userId": 66,
                "score": 10
              }
            ]
          }
        ]
      },
      {
        "id": "section2",
        "items": [
          {
            "id": 3,
            "name": "item2.1",
            "scores": [
              {
                "userId": 13,
                "score": 20
              }
            ]
          }
        ]
      }
    ]
  }
]

I now want to insert new scores for userId=10 for every item in every section.
The score is of course different for every item.
let's assume scores like this (all for userId=10)

[
   {
     "sectionId": "section1"
     "itemName: "item1.1"
     "score: 10,
     "userId": 10
   },
   {
     "sectionId": "section1"
     "itemName: "item1.2"
     "score: 15,
     "userId": 10
   },
   {
     "sectionId": "section2"
     "itemName: "item2.1"
     "score: 33,
     "userId": 10
   }
]

Added for clarification

the updated document should look like the following.

{
    "id": 1,
    "name": "myObject",
    "sections": [
      {
        "id": "section1",
        "items": [
          {
            "id": 1,
            "name": "item1.1",
            "scores": [
              {
                "userId": 13,
                "score": 10
              },
              { // <-- newly added score
                "userId": 10,
                "score": 10
              }
            ]
          },
          {
            "id": 2,
            "name": "item1.2",
            "scores": [
              {
                "userId": 66,
                "score": 10
              },
              { // <- newly added score
                "userId": 10,
                "score": 15
              }
            ]
          }
        ]
      }
// the remaining document is omitted for brevity but the above should also be applied to this sections

so far I have been able to achieve what I want for one single score like this

db.collection.update({
  id: 1
},
{
  $push: {
    "sections.$[item].items.$[score].scores": {
      "userId": 10,
      "score": 13
    },
  }
},
{
  arrayFilters: [
    {
      "score.userId": {
        $ne: 10
      }
    },
    {
      "item.name": {
        $eq: "item1.1"
      }
    }
  ]
})

This inserts a score for userId=10 in itemName=item1.1 if no score for userId=10 exists.

But I'm struggling on how to insert multiple scores into multiple items.
I saw that you can merge objects together, so maybe this would be an option although it kinda fells like an overkill.

So how can I insert all my scores for the different items in one atomic operation?

EDIT: Added clarification about the desired result.

CodePudding user response:

Query

  • pipeline update, requires MongoDB >= 4.2
  • reduce on the data that you want to insert, with initial value the sections
  • nested 3 maps that always do the same
    • if its not the key-value i want, keep the old value
    • else (merge {:newkey (map ...)})
  • if userId exists updates its score, else insert new userID and score
  • query assumes that there is a score array even if empty, i mean it only creates new userId score, not sections items etc

*you can avoid the set/unset and use driver variables in all the places where data is used

Test code here

db.collection.update({},
[
  {
    "$set": {
      "data": [
        {
          "sectionId": "section1",
          "itemName": "item1.1",
          "userId": 10,
          "score": 20
        },
        {
          "sectionId": "section1",
          "itemName": "item1.1",
          "userId": 13,
          "score": 30
        },
        {
          "sectionId": "section2",
          "itemName": "item2.1",
          "score": 33,
          "userId": 10
        }
      ]
    }
  },
  {
    "$set": {
      "sections": {
        "$reduce": {
          "input": "$data",
          "initialValue": "$sections",
          "in": {
            "$let": {
              "vars": {
                "data": "$$this"
              },
              "in": {
                "$map": {
                  "input": "$$value",
                  "in": {
                    "$cond": [
                      {
                        "$ne": [
                          "$$section.id",
                          "$$data.sectionId"
                        ]
                      },
                      "$$section",
                      {
                        "$mergeObjects": [
                          "$$section",
                          {
                            "items": {
                              "$map": {
                                "input": "$$section.items",
                                "in": {
                                  "$cond": [
                                    {
                                      "$ne": [
                                        "$$item.name",
                                        "$$data.itemName"
                                      ]
                                    },
                                    "$$item",
                                    {
                                      "$mergeObjects": [
                                        "$$item",
                                        {
                                          "scores": {
                                            "$let": {
                                              "vars": {
                                                "user_exist": {
                                                  "$in": [
                                                    "$$data.userId",
                                                    "$$item.scores.userId"
                                                  ]
                                                }
                                              },
                                              "in": {
                                                "$cond": [
                                                  {
                                                    "$not": [
                                                      "$$user_exist"
                                                    ]
                                                  },
                                                  {
                                                    "$concatArrays": [
                                                      "$$item.scores",
                                                      [
                                                        {
                                                          "userId": "$$data.userId",
                                                          "score": "$$data.score"
                                                        }
                                                      ]
                                                    ]
                                                  },
                                                  {
                                                    "$map": {
                                                      "input": "$$item.scores",
                                                      "in": {
                                                        "$cond": [
                                                          {
                                                            "$ne": [
                                                              "$$score.userId",
                                                              "$$data.userId"
                                                            ]
                                                          },
                                                          "$$score",
                                                          {
                                                            "$mergeObjects": [
                                                              "$$score",
                                                              {
                                                                "score": "$$data.score"
                                                              }
                                                            ]
                                                          }
                                                        ]
                                                      },
                                                      "as": "score"
                                                    }
                                                  }
                                                ]
                                              }
                                            }
                                          }
                                        }
                                      ]
                                    }
                                  ]
                                },
                                "as": "item"
                              }
                            }
                          }
                        ]
                      }
                    ]
                  },
                  "as": "section"
                }
              }
            }
          }
        }
      }
    }
  },
  {
    "$unset": [
      "data"
    ]
  }
])
  • Related