Search code examples
javascriptarraysmongodbaggregation-frameworkmongodb-update

MongoDB - How to use arrayFilters in pipeline-style for update


I use Mongo 4.4 and I try to perform an update with the aggregation pipeline using updateOne that updates the nested array elements. In order to update the specific array member I use an arrayFilter in the option but for some reason, I get the error message:

arrayFilters may not be specified for pipeline-style updates.

The query is something like this:

updateOne(
{ _id: <some_id>},
[
  {
    $set: { 'arr.$[element]': <new_value> }
  },
  {
    $set: { somefield: { '$cond': { <the condition content> } } }
  }
],
{
  arrayFilters: [ {'element.id': <new_value>.id } ]
}
);

How to fix it?

EDIT 1:

An example of a document is:

{
  _id: 932842242342354234,
  lastUpdateTime: <old time>,
  comments: [
    {
      id: 390430,
      content: "original",
      lastUpdated: <sime time>
    }
  ],
}

The query I want to do is update a comment and at the same time update the main object's field lastEditTime only if the content lastUpdated has a time that is after the current lastEditTime of the current document. So the update is:

updateOne(
{ _id: documentId},
[
  {
    $set: { 'comments.$[element]': newComment }
  },
  {
    $set: { lastUpdateTime: { 
          '$cond': { 
              if: { $gte: [newComment.lastUpdated, '$lastUpdateTime'] },
              then: newComment.lastUpdated,
              else: '$lastUpdateTime',
            } 
         } 
      }
  }
],
{
  arrayFilters: [ {'element.id': newComment.id } ]
}
);

So for example after an update with the comment:

{
  id: 390430,
  content: "new content",
  lastUpdated: <new time>
}

I want my main object to be:

{
  _id: 932842242342354234,
  lastUpdateTime: <new time>,
  comments: [
    {
      id: 390430,
      content: "new content",
      lastUpdated: <new time>
    }
  ],
}

Solution

  • I think the arrayFilters is not suitable for your scenario. Instead, using the aggregation pipeline.

    With $map to iterate every element in comments array. If the element is matched with the id to be updated, then update with newComment object, else remain the existing value.

    updateOne(
    { _id: documentId },
    [
      {
        $set: { 
          'comments': {
            $map: {
              input: "$comments",
              in: {
                $cond: {
                  if: { $eq: [ "$$this.id", newComment.id ] },
                  then: newComment,
                  else: "$$this"
                }
              }
            }
          }
        }
      },
      {
        $set: { lastUpdateTime: { 
              '$cond': { 
                  if: { $gte: [newComment.lastUpdated, '$lastUpdateTime'] },
                  then: newComment.lastUpdated,
                  else: '$lastUpdateTime',
                } 
             } 
          }
      }
    ]
    );