Search code examples
mongodbmongoosesubdocument

Mongodb Update subdocument values matching subdocument and without altering other fields


I have a schema like this

{
    "_id": "5f86da4b5bb9a62742371409",
    "status" : true,
    "doc": [
      {
        "_id": "5f86cacbe4d7c423f21faf50",
        "a": 4,
        "b": null
      },
      {
        "_id": "5f86cb01a1ca5124063299c1",
        "a": 6,
        "b": null

      }
    ]
}

And I have an update in the form of


[
      {
        "_id": "5f86cacbe4d7c423f21faf50",
        "b": 90
      },
      {
        "_id": "5f86cb01a1ca5124063299c1",
        "b": 45

      }
    ]

How can I update the collection to end up like this?

{
    "_id": "5f86da4b5bb9a62742371409",
    "status" : true,
    "doc": [
      {
        "_id": "5f86cacbe4d7c423f21faf50",
        "a": 4,
        "b": 90
      },
      {
        "_id": "5f86cb01a1ca5124063299c1",
        "a": 6,
        "b": 45

      }
    ]
}

Basically I want to update the subdocument with specific keys only (leaving other keys intact)


Solution

  • You can use update with aggregation pipeline from MongoDB 4.2,

    • $map to iterate loop of array doc and inside it iterate through updateDocs array, it will return matching b, and then $mergeObjects will return updated document,
    let updateDocs = [
      { "_id": mongoose.Types.ObjectId("5f86cacbe4d7c423f21faf50"), "b": 90 },
      { "_id": mongoose.Types.ObjectId("5f86cb01a1ca5124063299c1"), "b": 45 }
    ];
    
    db.collection.updateMany({},
    [
      {
        $set: {
          doc: {
            $map: {
              input: "$doc",
              as: "d",
              in: {
                $mergeObjects: [
                  "$$d",
                  {
                    $reduce: {
                      input: updateDocs,
                      initialValue: {},
                      in: {
                        $cond: [
                          { $eq: ["$$this._id", "$$d._id"] },
                          { b: "$$this.b" },
                          "$$value"
                        ]
                      }
                    }
                  }
                ]
              }
            }
          }
        }
      }
    ]
    )
    

    Playground