Search code examples
mongodbmongodb-querymongo-shell

How can I increment something inside an array in MongoDB?


I have a collection with documents structured like this

{
    _id: 140,
    first_list: [
        {
            _id: ObjectId('623db7e840651f2b7f107ee0'),
            second_list: [
                {
                    _id: ObjectId('6241808340651f2b7f108123'),
                    start_date: ISODate('2022-05-29T22:00:00.000Z'),
                    end_date: ISODate('2022-06-09T22:00:00.000Z'),
                },
                {
                    _id: ObjectId('6241809640651f2b7f108125'),
                    start_date: ISODate('2022-05-29T22:00:00.000Z'),
                    end_date: ISODate('2022-06-09T22:00:00.000Z'),
                },
                {
                    _id: ObjectId('624180ac40651f2b7f108127'),
                    start_date: ISODate('2022-05-29T22:00:00.000Z'),
                    end_date: ISODate('2022-06-09T22:00:00.000Z'),
                },
            ]
        }
        {
            _id: ObjectId('623db7e840651f2b7f107ee2'),
            second_list: [
                {
                    _id: ObjectId('6241808340651f2b7f108155'),
                    start_date: ISODate('2022-05-29T22:00:00.000Z'),
                    end_date: ISODate('2022-06-09T22:00:00.000Z'),
                },
                {
                    _id: ObjectId('6241809640651f2b7f108157'),
                    start_date: ISODate('2022-05-29T22:00:00.000Z'),
                    end_date: ISODate('2022-06-09T22:00:00.000Z'),
                },
                {
                    _id: ObjectId('624180ac40651f2b7f108159'),
                    start_date: ISODate('2022-05-29T22:00:00.000Z'),
                    end_date: ISODate('2022-06-09T22:00:00.000Z'),
                },
           ]
    ]
}

and I would like to increment all the end_dates (of all the documents in the collection!) by one hour.

If end_date was just an item of the outer document, I would have done like this:

db.collection.updateMany( {}, [{ $set: { end_date: { $add: ["$end_date", 1 * 60 * 60000] } } }])

but I'm having problems because of the array structure. I tried to read both here and here. What can I try next?


Solution

  • Here's one way to do it by using a pipeline in the update.

    db.collection.update({
      "first_list.second_list.end_date": {
        "$exists": true
      }
    },
    [
      {
        "$set": {
          "first_list": {
            // rewrite each element of first_list
            "$map": {
              "input": "$first_list",
              "as": "elem1",
              "in": {
                // keep everything from elem1 and rewrite second_list
                "$mergeObjects": [
                  "$$elem1",
                  {
                    "second_list": {
                      // rewrite each element of second_list
                      "$map": {
                        "input": "$$elem1.second_list",
                        "as": "elem2",
                        "in": {
                          // keep all from elem2 and rewrite end_date
                          "$mergeObjects": [
                            "$$elem2",
                            {
                              "end_date": {
                                "$dateAdd": {
                                  "startDate": "$$elem2.end_date",
                                  "unit": "hour",
                                  "amount": 1
                                }
                              }
                            }
                          ]
                        }
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      }
    ],
    {"multi": true}
    )
    

    Try it on mongoplayground.net.