Update a single item in array using aggregations

Hi all!

Lately I’m using the aggregations functionality to update a document using the update command. I’m using MongoDB 4.2 version.

My doc look like this:

{
  "_id" : 2,	
  "items" : [
    {
      "id": 1,
      "name": "test",
      "value": 5
    },
    {
      "id": 2,
      "name": "test2",
      "value": 10
    },
    {
      "id": 3,
      "name": "test3",
      "value": 15
    }
  ],	
};

I want to update a specific value in the array items filtering by id, but I have the next requirement: different users can update this items and it can changes the position of them in any time, so after an user’s change the items can look like this:

{
  "_id" : 2,	
  "items" : [
    {
      "id": 2,
      "name": "test2",
      "value": 10
    },
    {
      "id": 1,
      "name": "test",
      "value": 5
    },        
    {
      "id": 3,
      "name": "test3",
      "value": 15
    }
  ],	
};

So, if two users launch a concurrent update about the same item, like item with id 2, one of them to update value and the other to change the position in the array, both operations must be success.

Reviewing the documentation, I can get this behavior using the array expression operator, but the problem is I cannot update a single item in this array (or I have not found the way to do it), I must use the $map (or $reduce) operator to loop though the array, update the single item, and set the whole array to the field “items”.

So the question is: there is any way to update only a single item in the array using aggregation expressions???

Update the item using an expression like this:

{"items.0": <expression for update item with id equal to 0>}

will not work because other user can change the position in the array previously, so it’s not guaranteed the item with id equal to 0 is going to be in position 0.

To update a single item (an embedded document’s field(s)) in the array, based on a condition(e.g., the id field), you can use arrayFilters or positional $ update operator with any of the update methods (e.g., updateOne).

But, you can use an aggreation pipeline with the update operation as you are using the MongoDB version 4.2, but in this case it is much simpler using the arrayFilters or $ update operator.

Thanks for you reply.

Ok, I wrote that example because it’s simple, so in this case it’s more logical to use your answer. But in the case where we must use aggregations, like use conditional updates based on current field values, we have not other way to update the array items than replace it, or is there any?

So, why is there not a positional operator like $ to update an element in the array using aggregations?

But in the case where we must use aggregations, like use conditional updates based on current field values, we have not other way to update the array items than replace it, or is there any?

Pipeline provides additional functionality that cannot be achieved using the update operators. The pipeline allows only some of the aggregation pipeline stages - $set (or $addFields), $unset (or $project) and $replaceRoot (or $replaceWith). Though the update operator $set and the pipeline stage $set are named the same, they work in different ways and hence they have different purposes.

The pipeline allows to transform the data and the resulting update happens upon the transformed data. There is no replacing operation happening in this case.


So, why is there not a positional operator like $ to update an element in the array using aggregations?

The array update operators, like the $, are specific to update operations. The aggregation pipeline allows different set of operators, which are more comprehensive and cover the functionality that is not available with the update operators.

Reference: Updates with Aggregation Pipeline

Ok, thanks for the info. Only one thing more.

When you say this:

The pipeline allows to transform the data and the resulting update happens upon the transformed data. There is no replacing operation happening in this case.

What do you mean exactly? If in the pipeline I add/remove an element to the array, only the added/removed element is updated in Mongo, without touch the rest of element in the array?

Thanks.

Rest of the array remains same - only changes are updated. And, the update is atomic irrespective of the kind of update (change an element, or delete an element, or add elements, etc.) on the array.

2 Likes