mongodbmongodb-update

MongoDB - Increment a field using another array field


I have a collection with the document of this structure:

{
  "_id": {
    "$oid": "63e8af476a3674484ea14888"
  },
  "my_id": 321123,
  "version": 0,
  "parameters": [
    {"a": 1},
    {"a": 1, "b": 2}
  ]
}

Using a RESTful API the user can change the version field to any number between 0 and len(parameters). Another endpoint let the user push an object to parameters and set version to the new len(parameters) - 1. So using the previous example, after using this endpoint we will get:

{
  "_id": {
    "$oid": "63e8af476a3674484ea14888"
  },
  "my_id": 321123,
  "version": 2,
  "parameters": [
    {"a": 1},
    {"a": 1, "b": 2},
    {"a": 1, "b": 2, "c": 3}
  ]
}

I tried many things, but nothing seems to work. Here are examples of what I tried:

db.parameters.findOneAndUpdate({"my_id": 321123}, {"$inc":{version: parameters}, "$push":{"a": 1, "b": 2, "c": 3}}, upsert=true)
db.parameters.findOneAndUpdate({"my_id": 321123}, {"$inc":{version: {"$size": parameters}}, "$push":{"a": 1, "b": 2, "c": 3}}, upsert=true)

Solution

  • As your update requires referring to another field, you have to use the update query with the aggregation pipeline.

    For the first scenario,

    1. Add a new parameter object by combining it with the current parameters array via $concatArrays.

    2. I don't think that increment is what you need, but rather update the version value via $set stage. An example as below is to compare the version param between the range of 0 and len(parameters), if true, then update with the version param, else update with the size of the parameters array.

    db.parameters.update({
      "my_id": 321123
    },
    [
      {
        "$set": {
          parameters: {
            "$concatArrays": [
              "$parameters",
              [
                {
                  "a": 1,
                  "b": 2,
                  "c": 3
                }
              ]
            ]
          }
        }
      },
      {
        "$set": {
          version: {
            $cond: {
              if: {
                $and: [
                  {
                    $gt: [
                      2,
                      // version
                      0
                    ]
                  },
                  {
                    $lt: [
                      2,
                      // version
                      {
                        $size: "$parameters"
                      }
                    ]
                  }
                ]
              },
              then: 2,
              else: {
                $size: "$parameters"
              }
            }
          }
        },
        
      }
    ],
    {
      upsert: true
    })
    

    Demo (1st scenario) @ Mongo Playground


    For the second scenario,

    1. Add a new parameter object by combining it with the current parameters array via $concatArrays.

    2. Set the version with the size of parameters array minus 1.

    db.parameters.update({
      "my_id": 321123
    },
    [
      {
        "$set": {
          parameters: {
            "$concatArrays": [
              "$parameters",
              [
                {
                  "a": 1,
                  "b": 2,
                  "c": 3
                }
              ]
            ]
          }
        }
      },
      {
        "$set": {
          version: {
            "$subtract": [
              {
                $size: "$parameters"
              },
              1
            ]
          }
        },
        
      }
    ],
    {
      upsert: true
    })
    

    Demo (2nd scenario) @ Mongo Playground