1

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!

1 Answer 1

1

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

  • match on id=p00
  • map each member($$c variable) of the children array
    if $$c.id in the list ["c00","c01"]
    then member merge $$c with {"value" : false} (its like add key value pair)
    else dont change $$c
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")))))
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks! I'm trying to understand this in order to convert to Java, using the Aggregations tab in Compass.
i added some more on how it works, but as far as i know the java query builder doesn't support operators like $map (offers support for stages mostly), so i guess you will construct the Document by hand.
Would I be able to skip the map stage if I simply used an AND match on both IDs to begin with?
i added one solution with arrayFilters,It finds the members that satisfy the condition id in the list, and makes the value false, using update operators(not aggregation) and in java-driver
flatten schema might cause more joins, or more group by, schema choices can be complicated , you must see all collection, the data sizes and mostly the queries , and to have experience to decide, i am into nest unless there is a reason to not to, see this free course sometime from MongoDB it was good.There is also MongoDB community forum with very experienced people there to ask them also if needed here
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.