mongodbmongodb-querymongodb-update

Update multiple nested parameters at once without writing the full route at each


I have the following object in MongoDB.

{
  _id: "9839021321",
  forms: [
    {
      formName: "sample_form_name",
      version: [
        {
          version: "1",
          status: "Active"
        }
      ]
    }
  ]
}

And I want to update two of its properties. Add "Expiration Date" & update "Status".

{
  _id: "9839021321",
   forms: [
    {
      formName: "sample_form_name",
      version: [
        {
          version: "1",
          expirationDate: "2025-01-30",
          status: "Inactive"
        }
      ]
    }
  ]
}

I would normally do the following:

db["flows"].updateMany(
    { "_id": { "$regex":"9839021321" } },
    { 
      $set: { 
        "forms.$[f].version.$[v].expirationDate": "2025-06-26",
        "forms.$[f].version.$[v].expirationDate": { $type: "date" },
        "forms.$[f].version.$[v].status": "Inactive" 
      } 
    },
    { 
      "arrayFilters": [
        { "f.formName": { "$eq": "sample_form_name" } }, 
        { "v.status": { "$eq": "Active" } } 
      ] 
    });

Is there a way in which I can avoid having to copy paste the entire forms.$[f].version.$[v] section for each parameter? Something like this (it doesn't work):

db["flows"].updateMany(
    { "_id": { "$regex":"9839021321" } },
    { 
      "forms.$[f].version.$[v]": { 
        $set: { 
          "expirationDate": "2025-06-26", 
          "expirationDate": { $type: "date" }, 
          "status": "Inactive" 
        }
      }
    },
    {
      "arrayFilters": [
        { "f.formName": { "$eq": "sample_form_name"} },
        { "v.status": { "$eq": "Active" } }
      ]
    });

As well, can the same parameter be updated with multiple properties at once? Something like this:

db["flows"].updateMany(
    { "_id": { "$regex": "9839021321" } },
    { "forms.$[f].version.$[v]": {
      $set: {
        "expirationDate": ["2025-06-26", { $type: "date" }],   
        "status": "Inactive" 
      }
    },
    {
      "arrayFilters": [
        { "f.formName": { "$eq": "sample_form_name" } },
        { "v.status": { "$eq": "Active" } }
      ]
    });

Thanks.


Solution

    1. Concern: If you are only update the document with id: 9839021321, you should not use $regex and updateMany. Based on your current update query, you are updating document(s) for those _id containing 9839021321.

    2. In your first update query, you can't have the same key name to update forms.$[f].version.$[v].expirationDate for the value and type at the same time. Instead, why not just provide the Date value.

    3. In your second update query, the query is not executable due to the $set operator position. Even fixing the syntax, unfortunetely the MongoDB update query doesn't support such "shortcut" and the result returned will be the version object being overwritten as below:

    "version": [
      {
        "expirationDate": [
           "2025-06-26",
            {
              "$type": "date"
            }
          ],
        "status": "Inactive"
      }
    ]
    

    You will face the risk that the other field in the object(s) for version array get overwritten/lost.


    Approach 1: MongoDB update query

    You have to specify each nested field and provide the Date value as below:

    db.collection.update({
      "_id": {
        "$regex": "9839021321"
      }
    },
    {
      $set: {
        "forms.$[f].version.$[v].expirationDate": new Date("2025-06-26"),
        "forms.$[f].version.$[v].status": "Inactive"
      }
    },
    {
      "arrayFilters": [
        {
          "f.formName": {
            "$eq": "sample_form_name"
          }
        },
        {
          "v.status": {
            "$eq": "Active"
          }
        }
      ]
    })
    

    Demo Approach 1 @ Mongo Playground

    Approach 2: MongoDB aggregation query to update

    For aggregation query, it is allowed to update multiple fields in the object but you have to construct such long query for iterating both forms objects and f.versions conditionally.

    db.flows.aggregate([
      {
        "$match": {
          "_id": "9839021321"
        }
      },
      {
        "$set": {
          "forms": {
            "$map": {
              "input": "$forms",
              "as": "f",
              "in": {
                "$cond": {
                  "if": {
                    "$eq": [
                      "$$f.formName",
                      "sample_form_name"
                    ]
                  },
                  "then": {
                    "$mergeObjects": [
                      "$$f",
                      {
                        "version": {
                          "$map": {
                            "input": "$$f.version",
                            "as": "v",
                            "in": {
                              "$cond": {
                                "if": {
                                  "$eq": [
                                    "$$f.formName",
                                    "sample_form_name"
                                  ]
                                },
                                "then": {
                                  "$mergeObjects": [
                                    "$$v",
                                    {
                                      "expirationDate": new Date("2025-06-26"),
                                      "status": "Inactive"
                                    }
                                  ]
                                },
                                "else": "$$v"
                              }
                            }
                          }
                        }
                      }
                    ]
                  },
                  "else": "$$f"
                }
              }
            }
          }
        }
      },
      {
        "$merge": {
          "into": "9839021321",
          "on": "_id",
          "whenMatched": "replace"
        }
      }
    ])
    

    Demo Approach 2 @ Mongo Playground