I have a list of documents in MongoDB with tree structure, where Model Tree Structures with Parent References pattern used. I want a single aggregation query which returns ancestor list(till the root), given the 'name' property.
Structure:
{
'_id': '1',
'name': 'A',
'parent': '',
},
{
'_id': '2',
'name': 'B',
'parent': 'A',
},
{
'_id': '3',
'name': 'C',
'parent': 'B',
},
{
'_id': '4',
'name': 'D',
'parent': 'C',
}
Aggregation result:(Given, name = 'D')
{
'_id': '4',
'name': 'D',
'ancestors': [{name:'C'}, {name:'B'}, {name:'A'}]
}
Note:
I can't change the document structure now. It will cause many problems. I saw many solutions which suggest to use Model Tree Structures with an Array of Ancestors. But I cannot use it now. Is there any way to achieve it with the above pattern using single aggregation query? Thank you
Starting from MongoDB 3.4, we can do this with the Aggregation Framework.
The first and most important stage in our pipeline is the $graphLookup
stage. $graphLookup
allows us to recursively match on the "parent" and "name" field. As result, we get the ancestors of each "name".
The next stage in the pipeline is the $match
stage where we simply select the "name" we are interested in.
The final stage is the $addFields
or $project
stage where we apply an expression to the "ancestors" array using the $map
array operator.
Of course with the $reverseArray
operator we reverse our array in order to get the expected result.
db.collection.aggregate(
[
{ "$graphLookup": {
"from": "collection",
"startWith": "$parent",
"connectFromField": "parent",
"connectToField": "name",
"as": "ancestors"
}},
{ "$match": { "name": "D" } },
{ "$addFields": {
"ancestors": {
"$reverseArray": {
"$map": {
"input": "$ancestors",
"as": "t",
"in": { "name": "$$t.name" }
}
}
}
}}
]
)