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.
$concatArrays
with a $cond
(condition) on the arrayHow 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: ... } ] );
You can do this with some $slice
'ing and $concatArrays
:
slice(<array>, 0, 1)
(start index=0, n=1) (or use $first
and wrap it in [...]
)slice(<array>, 1, 1)
(start index=1, n=1) and just the first of this single-element slice; and merge it with newField
.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" }] }
]
}
}
}
])