mongodbmongo-java-driver

Mongo update multiple nested objects when their ID exists in a list


I'm trying to update many objects within a nested array when their IDs exist in a list.

The document structure is like so:

{
    id: p00,
    children: [{
            id: c00,
            value: true
        },
        {
            id: c01,
            value: true
        },
        {
            id: c02,
            value: true
        }
    ]
}

I would like to set value=false if id=p00 and children.id in [c00, c01].

This is my attempt, however it's clearly updating making only 1 update to the parent record, meaning only the c00 would be modified with the false value.

collection
    .updateMany(and(
        eq("id", "p00"),
        in("children._id", [c00, c01])), 
    combine(
        set("children.$.value", false)
));

Is this possible, or do I need to move the children up to make a more flatend structure? I'd prefer not to as that introduces it's own problems.

Thanks!


Solution

  • You can do it with pipeline update.

    Pipeline updates allows us to use all aggregate operators, so we can do complicated updates (in your case maybe its possible with update operators also, but with pipeline its easy)

    Pipelines updates require >= MongoDB 4.2.

    Test code here

    Query

    db.collection.update({
      "id": {
        "$eq": "p00"
      }
    },
    [
      {
        "$set": {
          "children": {
            "$map": {
              "input": "$children",
              "as": "c",
              "in": {
                "$cond": [
                  {
                    "$in": [
                      "$$c.id",
                      [
                        "c00",
                        "c01"
                      ]
                    ]
                  },
                  {
                    "$mergeObjects": [
                      "$$c",
                      {
                        "value": false
                      }
                    ]
                  },
                  "$$c"
                ]
              }
            }
          }
        }
      }
    ])
    

    If you dont want to use update pipeline(your update is simple and it can be done with array filters also), and in Java would be like bellow.

    coll.updateOne(new Document().append("id","p00"),
                   new Document().append("$set",new Document().append("children.$[item].value",false)),
                   new UpdateOptions().arrayFilters(
                     Collections.singletonList( Filters.in("item.id",Arrays.asList("c00","c01")))))