Reputation: 7662
Let's say that I have the following documents in the association collection:
{
"id" : 1,
"parentId" : 1,
"position" : {
"x" : 1,
"y" : 1
},
"tag" : "Beta"
},
{
"id" : 2,
"parentId" : 2,
"position" : {
"x" : 2,
"y" : 2
},
"tag" : "Alpha"
},
{
"id" : 3,
"parentId" : 1,
"position" : {
"x" : 3,
"y" : 3
},
"tag" : "Delta"
},
{
"id" : 4,
"parentId" : 1,
"position" : {
"x" : 4,
"y" : 4
},
"tag" : "Gamma"
},
{
"id" : 5,
"parentId" : 2,
"position" : {
"x" : 5,
"y" : 6
},
"tag" : "Epsilon"
}
I would like to create an aggregate query to produce the following result:
{
"_id" : 2,
"position" : {
"x" : 2,
"y" : 2
},
"tag" : "Alpha",
"children" : [
{
"position" : {
"x" : 5,
"y" : 6
},
"tag" : "Epsilon"
}
]
},
{
"_id" : 1,
"position" : {
"x" : 1,
"y" : 1
},
"tag" : "Beta"
"children" : [
{
"position" : {
"x" : 3,
"y" : 3
},
"tag" : "Delta"
},
{
"position" : {
"x" : 4,
"y" : 4
},
"tag" : "Gamma"
}
]
}
However, I was able only to create the following grouping query which puts "all-the-related" documents in children array:
db.association.aggregate([{
$group : {
_id : "$parentId",
children : {
$push : {
position : "$position",
tag :"$tag"
}
}
}
}])
I don't know how to filter out "position" and "tag" specific to "parent" points and put them at the top level.
Upvotes: 1
Views: 700
Reputation: 13113
By making sure the documents are sorted (parent - children 1 - children 2 ... - children n), we can merge grouped document
with the 1st child (which is parent
). In the last step, we need to remove parent
from children
array.
Try this one:
db.association.aggregate([
{
$sort: {
parentId: 1,
id: 1
}
},
{
$group: {
_id: "$parentId",
children: {
$push: {
position: "$position",
tag: "$tag"
}
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$$ROOT",
{
$arrayElemAt: [
"$children",
0
]
}
]
}
}
},
{
$addFields: {
children: {
$slice: [
"$children",
1,
{
$size: "$children"
}
]
}
}
}
])
Upvotes: 1
Reputation: 4363
Although Valijon's answer is working, it needs to be sorted before. Here's a solution without the need of sorting, but using graphLookup stage (which is perfect to achieve what you need)
db.collection.aggregate([
{
$graphLookup: {
from: "collection",
startWith: "$id",
connectFromField: "id",
connectToField: "parentId",
as: "children",
}
},
{
$match: {
$expr: {
$gt: [
{
$size: "$children"
},
0
]
}
}
},
{
$addFields: {
children: {
$filter: {
input: "$children",
as: "child",
cond: {
$ne: [
"$id",
"$$child.id"
]
}
}
}
}
}
])
Upvotes: 2