Vishal
Vishal

Reputation: 175

MongoDB - Update a parent array field using another child array field

I've a collection like this

db.aa1.insertMany([
    { parentArr: [] },
    { parentArr: [
        {childArr: [ {childField: 2}, {childField: 4} ]}
    ] },
    { parentArr: [
        {childArr: []}
    ] },
    { parentArr: [
        {childArr: [ {childField: 3}, {childField: 5} ]}
    ] },
])

Now I want the end result to be like

[
    { parentArr: [] },
    { parentArr: [ { childArr: [] } ] },
    { parentArr: [
        { 
          childArr: [ {childField: 2}, {childField: 4} ],
          parentField: [2, 4]
        },
    ] },
    { parentArr: [
        { 
          childArr: [ {childField: 3}, {childField: 5} ],
          parentField: [3, 5]
        }
    ] },
]

Here I've copied the childArr.childField values in the parentArr.parentField.

Now in plain JS, I could do something like this

parentArr.forEach(p => p.parentField = p.childArr ? p.childArr.map(c => c.childField) : [])

How can I achieve this using a MongoDB Query?

I've tried the following $push $set combinations, of course, one at a time.

For the example sake, I've written all push and set together.

db.myCollection.update(
    {
        "parentArr.childArr.0": {$exists: true}
    }, 
    {
        $set: {"parentArr.$[].parentField": ["$parentArr.$[].childArr.$[].childField"]}

        $set: {"parentArr.parentField": ["$parentArr.childArr.childField"]}

        $push: {
            "parentArr.$[].parentField": {$each: ["$parentArr.$[].childArr.$[].childField"]}
        }

        $push: {
            "parentArr.parentField": {$each: ["$parentArr.childArr.childField"]}
        }
    },
    {
        upsert: true,
        multi: true
    }
)

Upvotes: 1

Views: 278

Answers (1)

Tom Slabbaert
Tom Slabbaert

Reputation: 22276

If you're using Mongo version 4.2+ they have introduced pipeline'd updates meaning we now have more power when updating:

db.aa1.updateMany(
    {
        "parentArr.childArr.childField": {$exists: true}
    },
    [
        {
            $set: {
                "parentArr.parentField": {
                    $reduce: {
                        input: {
                            $map: {
                                input: "$parentArr",
                                as: "parent",
                                in: {
                                    $map: {
                                        input: "$$parent.childArr",
                                        as: "child",
                                        in: "$$child.childField"
                                    }
                                }
                            }
                        },
                        initialValue: [],
                        in: {$setUnion: ["$$value", "$$this"]}
                    }
                }
            }
        }
    ]
)

If you're on an older Mongo version then you'll have to do it in code, as you already posted a relevant snippet I have no more to add.

Upvotes: 1

Related Questions