Skip to content
Advertisement

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>
    }
  ],
}

Advertisement

Answer

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',
            } 
         } 
      }
  }
]
);
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement