mmu36478
mmu36478

Reputation: 1355

$aggregation and $look up in the same collection- mongodb

The structure is more or less like;

[   
    {id: 1, name: "alex" , children: [2, 4, 5]},
    {id: 2, name: "felix", children: []},
    {id: 3, name: "kelly", children: []},
    {id: 4, name: "hannah", children: []},
    {id: 5, name: "sonny", children: [6]},
    {id: 6, name: "vincenzo", children: []}
]

I want to replace children id's with names when children array is not empty.

So the result of the query is expected as;

[   {id: 1, name: "alex" , children: ["felix", "hannah" , "sonny"]}
    {id: 5, name: "sonny", children: ["vincenzo"]}
]

What have I done to achieve this;

db.list.aggregate([
  {$lookup: { from: "list", localField: "id", foreignField: "children", as: "children" }},
  {$project: {"_id" : 0, "name" : 1, "children.name" : 1}},
])

filled children with its parent, which is not what I want :)

{ "name" : "alex", "parent" : [ ] }
{ "name" : "felix", "parent" : [ { "name" : "alex" } ] }
{ "name" : "kelly", "parent" : [ ] }
{ "name" : "hannah", "parent" : [ { "name" : "alex" } ] }
{ "name" : "sonny", "parent" : [ { "name" : "alex" } ] }
{ "name" : "vincenzo", "parent" : [ { "name" : "sonny" } ] }

What did I misunderstood?

Upvotes: 12

Views: 8330

Answers (2)

s7vr
s7vr

Reputation: 75934

With the current Mongo 3.4 version you can make use of $graphLookup.

$maxDepth set at 0 for a non-recursive lookup. You may want to add a $match stage before the lookup to filter the records with no children.

db.list.aggregate([{
    $graphLookup: {
        from: "list",
        startWith: "$children",
        connectFromField: "children",
        connectToField: "id",
        as: "childrens",
        maxDepth: 0,
    }
}, {
    $project: {
        "_id": 0,
        "name": 1,
        "childrenNames": "$childrens.name"
    }
}]);

Upvotes: 6

Shaishab Roy
Shaishab Roy

Reputation: 16805

Before use $lookup stage you should use $unwind for children array then $lookup for children. After $lookup stage you need to use $group to get children array with name instead of id

You can try it:

db.list.aggregate([
    {$unwind:"$children"},
    {$lookup: { 
        from: "list",
        localField: "children",
        foreignField: "id",
        as: "childrenInfo" 
      }
    },
    {$group:{
       _id:"$_id",
       children:{$addToSet:{$arrayElemAt:["$childrenInfo.name",0]}},
       name:{$first:"$name"}
      }
    }
]);

// can use $push instead of $addToSet if name can be duplicate

Why used $group ?

For example: your first document

{id: 1, name: "alex" , children: [2, 4, 5]}

after $unwind your document will be look like

{id: 1, name: "alex" , children: 2},
{id: 1, name: "alex" , children: 4},
{id: 1, name: "alex" , children: 5}

after $lookup

{id: 1, name: "alex" , children: 2,
  "childrenInfo" : [ 
        {
            "id" : 2,
            "name" : "felix",
            "children" : []
        }
    ]},
//....

then after $group

 {id: 1, name: "alex" , children: ["felix", "hannah" , "sonny"]}

Upvotes: 12

Related Questions