Update element of an array using aggregation pipeline in `update` operation

Viewed 1604

Scenario

For the below document, I want to update the status of a specific message in the messages array:

[{
    "_id" : ObjectId("6079bab4f297df39a44609cb"),
    "title" : "Test messages of single user",
    "messages" : [ 
        {
            "_id" : ObjectId("6079bab4f297df39a44609cc"),
            "body" : "Test 1",
            "status" : 1
        }, 
        {
            "_id" : ObjectId("6079bab4f297df39a44609cd"),
            "body" : "Test 2",
            "status" : 1
        }, 
        {
            "_id" : ObjectId("6079bcf7c041b00ec4cebb9d"),
            "body" : "Hello I'm Sam",
            "status" : 1
        }
    ]
}]
  1. Query without aggregation pipeline in update operation updates the status in single message.

Run below query here

db.update(
  { 
    "_id": ObjectId("6079bab4f297df39a44609cb"), 
    "messages": { $elemMatch: { _id: ObjectId("6079bcf7c041b00ec4cebb9d") } }
  }, 
  {
    $set: { "messages.$.status": 2 }
  }
)
  1. Query with aggregation pipeline in update operation updates the status in all messages

Run below query here

db.update(
  { 
    "_id": ObjectId("6079bab4f297df39a44609cb"), 
    "messages": { $elemMatch: { _id: ObjectId("6079bcf7c041b00ec4cebb9d") } }
  }, 
  [
    {
      $set: { "messages.status": 2 }
    }
  ]
)

MongoDB Version: 4.4

Note: For some reason, I need to use the second query but I am not able to update the status of single message. It always update all the messages

Question

  1. Why is the second query updating status in all messages when I've selected the message that I want to update?
  2. How can I update status of single message with aggregation pipeline (second query)?

It'll be helpful if you can share link for the query

1 Answers

Demo - https://mongoplayground.net/p/VZHes8--O5O

Use $[]

The filtered positional operator $[] identifies the array elements that match the arrayFilters conditions for an update operation

db.collection.update(
 { "_id": ObjectId("6079bab4f297df39a44609cb") },
 { $set: { "messages.$[m].status": 2 } },
 { arrayFilters: [ { "m._id": ObjectId("6079bcf7c041b00ec4cebb9d") } ]
})

Update

Demo - https://mongoplayground.net/p/GyBxksYKpmf

Use $map

db.collection.update({
  "_id": ObjectId("6079bab4f297df39a44609cb"),
  "messages": {
    $elemMatch: {
      _id: ObjectId("6079bcf7c041b00ec4cebb9d")
    }
  }
},
[
  {
    $set: {
      "messages": {
        $map: {
          input: "$messages",
          as: "m",
          in: {
            $cond: [
              { $eq: [ "$$m._id", ObjectId("6079bcf7c041b00ec4cebb9d") ] }, // condition
              { $mergeObjects: [ "$$m", { status: 2 } ] }, // true
              "$$m" // false
            ]
          }
        }
      }
    }
  }
])

$cond

$mergeObjects

Note- it's overkill in this case

Related