2

I have many records in a MongoDB database, like this:

[
  {
    rootName: "AAA",
    outerItem: {
      innerItems: [
        {
          name: "A"
        },
        {
          name: null
        },
        {
          name: "C"
        }
      ]
    }
  }
]

I would like to copy the rootName field of each record into an outerItem.innerItems[1].newField only, using a named index position of 1 (or looking up the correct index in some other way).

What I expect is for each record to look like this, after the code is run:

[
  {
    "rootName": "AAA",
    "outerItem": {
      "innerItems": [
        { // 0
          "name": "A",
        },
        { // 1
          "name": null,
          "newField": "AAA" // New item should be added here only (array index 1)
        },
        { // 2
          "name": "C",
        }
      ]
    }
  }
]

I've tried using this code:

db.collection.update({},
[
  {
    "$set": {
      "outerItem.innerItems": {
        $map: {
          input: "$outerItem.innerItems",
          in: {
            $mergeObjects: [
              "$$this",
              {
                newField: "$rootName"
              }
            ]
          }
        }
      }
    }
  }
],
{
  "multi": false,
  "upsert": false
})

MongoDB Playground Link: https://mongoplayground.net/p/QhN1qAbWFwZ

But I end up with this:

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "outerItem": {
      "innerItems": [
        {
          "name": "A",
          "newField": "AAA"
        },
        {
          "name": null,
          "newField": "AAA"
        },
        {
          "name": "C",
          "newField": "AAA"
        }
      ]
    },
    "rootName": "AAA"
  }
]

The problem is that the newField is copied to all the array items, when I only want it copied to 1 specific array item of index 1.

I tried to modify the code to use a condition of array index 1, but the following code does not work. It produces an error:

db.collection.update({},
[
  {
    "$set": {
      "outerItem.innerItems": {
        $map: {
          input: "$outerItem.innerItems",
          in: {
            $cond: [
              {
                $eq: [
                  "$$item.id",
                  1
                ]
              }
            ],
            $mergeObjects: [
              "$$this",
              {
                newField: "$rootName"
              }
            ]
          }
        }
      }
    }
  }
],
{
  "multi": false,
  "upsert": false
})

MongoDB Playground link: https://mongoplayground.net/p/ckgDCUN1XrR

This version also doesn't work:

db.collection.update({
  "outerItem.innerItems": {
    $elemMatch: {
      "name": null
    }
  }
},
[ ... ]

I can't figure out how to get it working. I have used the following guides so far, but I still cannot find a working solution for this scenario.

How do I get it working? The final code is intended to be used in Node.js TypeScript code, probably using something like:

await queryRunner.updateMany('name_items', {}, [ { $set: ... } ] );
3
  • Btw, is the goal to update "array element at index=1" or just "array element with name: null" ? Commented May 1 at 18:01
  • @aneroid The goal is to "update array element at index 1". But I wasn't sure if it was easier to implement it by using an array index, or by looking for the item "name": null. By the way, thanks for your great answer. It's appreciated a lot, and I was impressed by how quickly you answered it. Commented May 1 at 21:37
  • You're welcome. If that helped, read: What should I do when someone answers my question?, and about accepting and voting. Commented May 2 at 10:53

1 Answer 1

1

You can do this with some $slice'ing and $concatArrays:

  1. Get the first element with slice(<array>, 0, 1) (start index=0, n=1) (or use $first and wrap it in [...])
  2. Get the second element with slice(<array>, 1, 1) (start index=1, n=1) and just the first of this single-element slice; and merge it with newField.
  3. Get the rest of the array with slice(<array>, 2, size-of-array-or-large-numebr)
db.collection.update({},
[
  {
    $set: {
      "outerItem.innerItems": {
        $concatArrays: [
          { $slice: ["$outerItem.innerItems", 0, 1] },
          [
            {
              $mergeObjects: [
                { $first: { $slice: ["$outerItem.innerItems", 1, 1] } },
                { newField: "$rootName" }
              ]
            }
          ],
          { $slice: ["$outerItem.innerItems", 2, { $size: "$outerItem.innerItems" }] }
        ]
      }
    }
  }
])

Mongo Playground

Sign up to request clarification or add additional context in comments.

Comments

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.