1

I have a group collection that has the array order that contains ids. I would like to use updateOne to set multiple items in that order array.

I tried this which updates one value in the array:

db.groups.updateOne({
    _id: '831e0572-0f04-4d84-b1cf-64ffa9a12199'
  },
  {$set: {'order.0': 'b6386841-2ff7-4d90-af5d-7499dd49ca4b'}}
)

That correctly updates (or sets) the array value with index 0.

However, I want to set more array values and updateOne also supports a pipeline so I tried this:

db.slides.updateOne({
    _id: '831e0572-0f04-4d84-b1cf-64ffa9a12199'
  },
  [
    {$set: {'order.0': 'b6386841-2ff7-4d90-af5d-7499dd49ca4b1'}}
  ]
)

This does NOTHING if the order array is empty. But if it's not, it replaces every element in the order array with an object { 0: 'b6386841-2ff7-4d90-af5d-7499dd49ca4b1' }. I don't understand that behavior.

In the optimal case I would just do

db.slides.updateOne({
    _id: '831e0572-0f04-4d84-b1cf-64ffa9a12199'
  },
  [
    {$set: {'order.0': 'b6386841-2ff7-4d90-af5d-7499dd49ca4b1'}},
    {$set: {'order.1': 'otherid'}},
    {$set: {'order.2': 'anotherone'}},
  ]
)

And that would just update the order array with the values.

What is happening here and how can I achieve my desired behavior?

4
  • if you want to update more than one order ids at once, you don't need to use aggregation pipeline, just add key value pair after zero index in same $set, see { $set: { 'order.0': 'id1', 'order.1': 'id2' } } playground. Commented Aug 30, 2021 at 13:31
  • @turivishal Yes I could do that. As a matter of fact, I have all those updates in an array and in order to merge them to one update I would need to write logic that mongodb aleady performs in its optimization stage. In any case, whatever happens now, makes zero sense to me and it should work Commented Aug 30, 2021 at 13:38
  • in aggregation pipeline the meaning of $set is projection in root level properties, it will not update array element by position. so $set will perform dirrerently in both the ways. Commented Aug 30, 2021 at 13:47
  • @turivishal I am aware that pipeline $set is different. However, obviously it can update sub properties. Why not by array index? Commented Aug 30, 2021 at 13:58

2 Answers 2

2

The update by index position in the array is only supported in regular update queries, but not in aggregation queries,

They have explained this feature in regular update query $set operator documentation, but not it aggregation $set.

The correct implementation in regular update query:

db.slides.updateOne({
    _id: '831e0572-0f04-4d84-b1cf-64ffa9a12199'
  },
  {
    $set: {
      'order.0': 'b6386841-2ff7-4d90-af5d-7499dd49ca4b1',
      'order.1': 'otherid',
      'order.2': 'anotherone'
    }
  }
)

If you are looking for only an aggregation query, it is totally long process than the above regular update query, i don't recommend that way instead, you can format your input in your client-side language and use regular query.

Sign up to request clarification or add additional context in comments.

2 Comments

I don't get why updating multiple values with multiple sets is fine. Even setting multiple nested properties. But when it comes to arrays it's a problem all of a sudden. It's just another nested prop which happens to end with a numeric key. Guess I have to do all the work and merge the updates...
They have not mentioned why it is not supported in update with aggregation in documentation, but as per the documentation of aggregation $set is just for projection and alternate for $addFields and $project stage, and i think this is a support a feature level question, you need to ask to mongodb developers in JIRA or mongodb community forum.
1

If you have to use aggregation framework, try this (you will have to pass array of indexes and array of updated values separately):

  • $map and $range to iterate over the order array by indexes
  • $cond and $arrayElemAt to check if the current index is in the array of indexes that has to be updates. If it is, update it with the same index from the array of new values. If it is not, keep the current value.

NOTE: This will work only if the array of indexes that you want to update starts from 0 and goes up (as in your example).

db.collection.update({
  _id: '831e0572-0f04-4d84-b1cf-64ffa9a12199'
},
[
  {
    "$set": {
      "order": {
        "$map": {
          input: {
            $range: [
              0,
              {
                $size: "$order"
              }
            ]
          },
          in: {
            $cond: [
              {
                $in: [
                  "$$this",
                  [
                    0,
                    1,
                    2
                  ]
                ]
              },
              {
                $arrayElemAt: [
                  [
                    "b6386841-2ff7-4d90-af5d-7499dd49ca4b1",
                    "otherid",
                    "anotherone"
                  ],
                  "$$this"
                ]
              },
              {
                $arrayElemAt: [
                  "$order",
                  "$$this"
                ]
              }
            ]
          }
        }
      }
    }
  }
])

Here is the working example: https://mongoplayground.net/p/P4irM9Ouyza

7 Comments

See my first comment on the question. I need a solution with pipeline
I updated my answer. Check if it works for you.
Unfortunately I dont think it will work. My aggregation could also contain sets on other elements and not just the array e.g. $set: { otherfield: 6 }. I appreciate the effort, though
But you can also add $set to other fields. You can do that in the same stage actually.
Yes but I would need to know where I am updating an array and where I am just updating any other (nested) prop
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.