javascriptnode.jsdatabasemongodb

Updating objects from multiple documents when element matches but push when there is no match in MongoDB


I have these 2 documents. I need to update the "booksBought" arrays of the 2 documents at the same time with updateMany / update of MongoDB.

Here's the scenario: My query as of now is I select both users Josh and Carl with $in, then increment the quantity of a specific booktitle, in this example "harry potter". I filter the object using the [$] identifier and the arrayFilter condition to select the object. However, the other user isn't included in the filter since he doesn't have an object with "harry potter" in it.

How do I push it on the other user's "booksBought" array if the object doesn't exist although the user is part of the selection under $in?

Query

db.collection.update({
  "userId": {
    $in: [
      "10000001",
      "10000002"
    ]
  }
},
{
  $inc: {
    "booksBought.$[elem].quantity": 10
  }
},
{
  arrayFilters: [
    {
      "elem.booktitle": "harry potter"
    }
  ]
})

Document

[
  {
    "userId": "10000001",
    "name": "Josh",
    "booksBought": [
      {
        "booktitle": "harry potter",
        "quantity": 1
      },
      {
        "booktitle": "game of thrones",
        "quantity": 2
      }
    ]
  },
  {
    "userId": "10000002",
    "name": "Carl",
    "booksBought": [
      {
        "booktitle": "lord of the rings",
        "quantity": 4
      }
    ]
  }
]

Expected

[
  {
    "userId": "10000001",
    "name": "Josh",
    "booksBought": [
      {
        "booktitle": "harry potter",
        "quantity": 11
      },
      {
        "booktitle": "game of thrones",
        "quantity": 2
      }
    ]
  },
  {
    "userId": "10000002",
    "name": "Carl",
    "booksBought": [
      {
        "booktitle": "lord of the rings",
        "quantity": 4
      },
      {
        "booktitle": "harry potter",
        "quantity": 10
      }
    ]
  }
]

Playground link: https://mongoplayground.net/p/Aby5lhRckJT


Solution

  • Using update with aggregation pipeline, you can always pad the harry potter entry with 0 quantity first. Then, use $map to iterate through the array and $add 10 to the quantity.

    db.collection.update({
      "userId": {
        $in: [
          "10000001",
          "10000002"
        ]
      }
    },
    [
      {
        "$set": {
          "booksBought": {
            "$cond": {
              "if": {
                "$in": [
                  "harry potter",
                  "$booksBought.booktitle"
                ]
              },
              "then": "$booksBought",
              "else": {
                "$concatArrays": [
                  "$booksBought",
                  [
                    {
                      "booktitle": "harry potter",
                      "quantity": 0
                    }
                  ]
                ]
              }
            }
          }
        }
      },
      {
        "$set": {
          "booksBought": {
            "$map": {
              "input": "$booksBought",
              "as": "bb",
              "in": {
                "$cond": {
                  "if": {
                    $eq: [
                      "harry potter",
                      "$$bb.booktitle"
                    ]
                  },
                  "then": {
                    "$mergeObjects": [
                      "$$bb",
                      {
                        "quantity": {
                          "$add": [
                            "$$bb.quantity",
                            10
                          ]
                        }
                      }
                    ]
                  },
                  "else": "$$bb"
                }
              }
            }
          }
        }
      }
    ],
    {
      multi: true
    })
    

    Mongo Playground