Using the following documents, we are able to do what we need to do which is essentially “re-parenting” a document that stores an array of ancestors like so:
db.getCollection("myarrays").insertOne({
"myid": "A",
"ancestors": [
{
"_id": "parent1",
"type": "type1"
},
{
"_id": "parent2",
"type": "type1"
},
{
"_id": "parent3",
"type": "type1"
}
]
})
db.getCollection("myarrays").aggregate(
[
{
"$match" : {
"ancestors._id" : "parent2"
}
},
{
"$project" : {
"ancestors" : {
"$concatArrays" : [
[
{
"_id" : "parent4",
"type" : "type1"
},
{
"_id" : "parent5",
"type" : "type1"
}
],
{
"$slice" : [
"$ancestors",
{
"$add" : [
{
"$indexOfArray" : [
"$ancestors._id",
"parent2"
]
},
1.0
]
},
{
"$size" : [
"$ancestors"
]
}
]
}
]
}
}
}
],
{
"allowDiskUse" : false
}
);
This effectively finds any documents that are grandchildren of “parent2” and re-parents them to “parent5” (which itself is a child of “parent4”). The resulting document looks like this:
{
"_id" : ObjectId("61e9860d97f54a3cebb47e3d"),
"ancestors" : [
{
"_id" : "parent4",
"type" : "type1"
},
{
"_id" : "parent5",
"type" : "type1"
},
{
"_id" : "parent3",
"type" : "type1"
}
]
}
This works great. However, I’m struggling to do the exact same thing in a case where ancestors
is nested within another array:
db.getCollection("myarraysnested").insertOne(
{
"references": [
{
"myid": "A",
"ancestors": [
{
"_id": "parent1",
"type": "type1"
},
{
"_id": "parent2",
"type": "type1"
},
{
"_id": "parent3",
"type": "type1"
}
]
},
{
"myid": "B",
"ancestors": [
{
"_id": "parent4",
"type": "type1"
},
{
"_id": "parent5",
"type": "type1"
},
{
"_id": "parent3",
"type": "type1"
}
]
},
{
"myid": "C",
"ancestors": [
{
"_id": "parent0",
"type": "type1"
},
{
"_id": "parent1",
"type": "type1"
},
{
"_id": "parent2",
"type": "type1"
},
{
"_id": "parent6",
"type": "type2"
}
]
}
]
}
)
The desired outcome would be that 2 of the 3 references
elements that match an ancestors
array containing an element with _i
= to “parent2” would have their ancestors
array updated to look just as the non-nested sample above looks resulting in the following output:
{
"_id" : ObjectId("61e9860d97f54a3cebb47e3c"),
"references" : [
{
"myid" : "A",
"ancestors" : [
{
"_id" : "parent4",
"type" : "type1"
},
{
"_id" : "parent5",
"type" : "type1"
},
{
"_id" : "parent3",
"type" : "type1"
}
]
},
{
"myid" : "B",
"ancestors" : [
{
"_id" : "parent4",
"type" : "type1"
},
{
"_id" : "parent5",
"type" : "type1"
},
{
"_id" : "parent3",
"type" : "type1"
}
]
},
{
"myid" : "C",
"ancestors" : [
{
"_id" : "parent4",
"type" : "type1"
},
{
"_id" : "parent5",
"type" : "type1"
},
{
"_id" : "parent6",
"type" : "type2"
}
]
}
]
}
I’ve tried several iterations of using $filter
and $map
but can’t quite get things correct for at the very least, finding the index of the element in the nested array that matches. I would prefer to avoid $unwind
if possible but if not possible, that could work.
Note: The pipeline is/will be used as part of an update statement to actually change the document so any limitations of the update pipeline style apply.