skip to Main Content

I would like to update a property of the last objet stored in a list in mongo. For performance reasons, I can not pop the object from the list, then update the property, and then put the objet back. I can not either change the code design as it does not depend on me. In brief am looking for a way to select the last element of a list.

The closest I came to get it working was to use arrayFilters that I found doing research on the subject (mongodb core ticket: https://jira.mongodb.org/browse/SERVER-27089):

db.getCollection("myCollection")
    .updateOne(
    {
        _id: ObjectId('638f5f7fe881c670052a9d08')
    },
    {
       $set: {"theList.$[i].propertyToUpdate": 'NewValueToAssign'}
    },
    {
        arrayFilters: [{'i.type': 'MyTypeFilter'}]
    }
)

I use a filter to only update the objets in theList that have their property type evaluated as MyTypeFilter.

What I am looking for is something like:

db.getCollection("maCollection")
    .updateOne(
    {
        _id: ObjectId('638f5f7fe881c670052a9d08')
    },
    {
       $set: {"theList.$[i].propertyToUpdate": 'NewValueToAssign'}
    },
    {
        arrayFilters: [{'i.index': -1}]
    }
)

I also tried using "theList.$last.propertyToUpdate" instead of "theList.$[i].propertyToUpdate" but the path is not recognized (since $last is invalid)

I could not find anything online matching my case.

Thank you for your help, have a great day

2

Answers


  1. One option is to use update with pipeline:

    db.collection.update(
      {_id: ObjectId("638f5f7fe881c670052a9d08")},
      [
        {$set: {
          theList: {
            $concatArrays: [
              {$slice: ["$theList", 0, {$subtract: [{$size: "$theList"}, 1]}]},
              [{$mergeObjects: [{$last: "$theList"}, {propertyToUpdate: "NewValueToAssign"}]}]
            ]
          }
        }}
      ]
    )
    

    See how it works on the playground example

    Login or Signup to reply.
  2. You want to be using Mongo’s pipelined updates, this allows us to use aggregation operators within the update body.

    You do however need to consider edge cases that the previous answer does not. (null list, empty list, and list.length == 1)

    Overall it looks like so:

    db.collection.update({
      _id: ObjectId("638f5f7fe881c670052a9d08")
    },
    [
      {
        $set: {
          list: {
            $concatArrays: [
              {
                $cond: [
                  {
                    $gt: [
                      {
                        $size: {
                          $ifNull: [
                            "$list",
                            []
                          ]
                        }
                      },
                      1
                    ]
                  },
                  {
                    $slice: [
                      "$list",
                      0,
                      {
                        $subtract: [
                          {
                            $size: "$list"
                          },
                          1
                        ]
                      }
                    ]
                  },
                  []
                ]
              },
              [
                {
                  $mergeObjects: [
                    {
                      $ifNull: [
                        {
                          $last: "$list"
                        },
                        {}
                      ]
                    },
                    {
                      propertyToUpdate: "NewValueToAssign"
                    }
                  ]
                }
              ]
            ]
          }
        }
      }
    ])
    

    Mongo Playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search