Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$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?

like image 686
mmu36478 Avatar asked Jan 11 '17 11:01

mmu36478


People also ask

What is lookup aggregation in MongoDB?

The MongoDB $lookup aggregation stage The aggregation pipeline stage $lookup makes it possible to join data from an input collection (the collection you’re running the query on) and a lookup collection (the collection you want data from), as long as both collections are on the same database.

Is there a cheat sheet for MongoDB aggregations?

Below is a MongoDB Aggregation cheat sheet that can come in very handy. Alright, we have learned some of the most important operations and stages for MongoDB Aggregations, feel free to ask any question in the comments section, I will be very happy to answer those.

What are $merge and $out in MongoDB?

With the introduction of $merge in version 4.2, MongoDB provides two stages, $merge and $out, for writing the results of the aggregation pipeline to a collection. The following summarizes the capabilities of the two stages:

What are multiple join conditions in MongoDB?

Multiple join conditions. Correlated and uncorrelated subqueries. In MongoDB, a correlated subquery is a pipeline in a $lookup stage that references document fields from a joined collection. An uncorrelated subquery does not reference joined fields.


2 Answers

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"
    }
}]);
like image 116
s7vr Avatar answered Sep 20 '22 15:09

s7vr


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"]}
like image 34
Shaishab Roy Avatar answered Sep 17 '22 15:09

Shaishab Roy