Deep Sheth
Deep Sheth

Reputation: 3

Mongoose - update last element in array (update document with an aggregator)

My question builds off this: Get only the last element of array mongoose. However, I'm using Mongoose and want to also update a field in the last element of the array, not just retrieve it.

How can I modify the last element/object in the rounds array to change from status: started to status: terminated?

Document to modify

{
    "_id" : "8844d3f2d25f45df8105db1ab058d7d6",
    "rounds" : [ 
        { "status" : "offline" },
        { "status" : "paused" },
        { "status" : "started" }
    ],
    "updatedAt" : ISODate("2020-07-05T21:21:58.823Z")
}

Seems like using negative indices would make this easy, allowing the n-1 element to be modified. I've seen many questions mention aggregation, but none modify the underlying data. I want to combine aggregation with updating a documenting.

Is it possible to use $set with a negative index?

Upvotes: 0

Views: 1162

Answers (1)

thammada.ts
thammada.ts

Reputation: 5245

As of v4.2 MongoDB does not have a straightforward way to update an array element by index.

However starting from v4.2 you can use updates with aggregation pipeline, which allow you to construct a new array based on the current array and replace the current array.

Instead of modifying the last element, you can construct a new array consisting of the first top n - 1 elements combining with a new element { status: "terminated" }.

You can achieve this by using $slice to get the top n - 1 elements and combine with the new element using $concatArrays

This assumes the elements only contains the field status you you want to update a field of an array elements that contains multiple fields, you'll need to merge the updated field with the current element using $mergeObjects

Model.updateOne({ // or updateMany with your condition
  _id: "8844d3f2d25f45df8105db1ab058d7d6"
}, [{
  $set: {
    rounds: {
      $concatArrays: [ // combine arrays
        {
          $slice: [ // get the top n - 1 elements
            "$rounds",
            { $subtract: [{ $size: "$rounds" }, 1] }
          ]
        },,
        [{ status: "terminated" }] // a new element to replace the last element
      ]
    }
  }
}])

Upvotes: 1

Related Questions