Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB $graphLookup get children all levels deep - nested result

As presented in https://www.slideshare.net/mongodb/webinar-working-with-graph-data-in-mongodb, slide 50 it is possible to use $graphLookup on a View in order to get a 2 levels deep tree-structure in nested format.

I have a MongoDB collection with tree nodes as documents with the following format:

{ "_id" : { "$oid" : "5b1a952361c6fa3418a15660" }, 
"nodeId" : 23978995, 
"name" : "settings", 
"type" : "Node",
"parentId" : [ 23978893, 23979072, 23979081 ] }

I have created a View like:

db.createView("treeView", "node", [
{
 $graphLookup: {
    from: "node",
    startWith: "$nodeId",
    connectFromField: "nodeId",
    connectToField: "parentId",
    maxDepth: 0,
    as: "children"
 }
}
]);

And I execute graph lookups like:

db.node.aggregate([ 
{ $match: {"nodeId": 23978786 } },
{
 $graphLookup: {
    from: "treeView",
    startWith: "$nodeId",
    connectFromField: "nodeId",
    connectToField: "parentId",
    maxDepth: 0,
    as: "children"
 }
}
]);

My question is how can I get the whole hierarchy, all levels deep?

like image 820
kmandalas Avatar asked Dec 13 '22 16:12

kmandalas


2 Answers

Unfortunately, you can't get the full depth in a nested format. Using a view is a workaround which lets you perform that operation, but you would need to create a new view for each level of embedding that you need. Instead, I would consider performing a graphLookup without providing a depth, starting from the root level, fetching all the hierarchy in a single query, before computing the tree at the application level.

This would look like something like this:

db.node.aggregate([
    { $match: {
        parentId: null
    }},
    { $graphLookup: {
        from: "node",
        startWith: "$nodeId",
        connectFromField: "nodeId",
        connectToField: "parentId",
        depthField: "depth",
        as: "children"
    }}
]);

This should let you fetch the whole hierarchy in one go, so next, you need to calculate the tree in your application, from the information you will have in the "children" array.

like image 167
Charles Sarrazin Avatar answered Jun 05 '23 03:06

Charles Sarrazin


@kmandalas I'm facing this kind of issue from last 2 days, my collection is a bit different but the concept is the same as your I hope what I wrote will help you to get the result,(I use reference of SO answers)

My Collection Schema is like :

const Category = new Schema({
    sTitle: { type: String, trim: true },
    iParentId: { type: mongoose.Schema.Types.ObjectId, ref: 'Category' },
    bStatus: { type: Boolean, default: true },
    dUpdatedAt: { type: Date },
    dCreatedAt: { type: Date, default: Date.now }
});

First, I use $graphLookup, than I got all children into one appropriate parent, like:

{
  "_id": "5c6fa228c30bbf02cf12fe6c",
   "sTitle": "firstParent",
   "childrens":[{obj},{obj},{obj},{obj}]    // Childrens as well as grandChild
},
{
  "_id": "5c80d644ab57dd06d48cc474",
   "sTitle": "secondParent",
   "childrens":[]     //No Child
},
.....

After getting this kind of result I create a tree in node js without using any third-party lib. tree logic code is :(!note:docs is $graphlooup output nothing else)

function list_to_tree(list) {
            var map = {}, node, roots = [], i;
            for (i = 0; i < list.length; i += 1) {
                map[list[i]._id] = i;
                list[i].children = [];
            }
            for (i = 0; i < list.length; i += 1) {
                node = list[i];
                if (node.iParentId !== null && map[node.iParentId] !== undefined) {
                    var node2 = {         //Because i need only _id,Title & childrens
                        _id: node._id,
                        Title: node.sTitle,
                        children: node.children
                    }
                    list[map[node.iParentId]].children.push(node2); //You can push direct "node"
                } else {
                    var node2 = {
                        _id: node._id,
                        Title: node.sTitle,
                        children: node.children
                    }
                    roots.push(node2);
                }
            }
            return roots;
        }
        let final_result = []     //For Storing all parent with childs
        if (docs.length >= 0) {   
            docs.map(single_doc => {  //For getting all parent Tree
                var single_child = list_to_tree(single_doc.children)
                var obj = {
                    _id: single_doc._id,
                    Title: single_doc.sTitle,
                    children: single_child
                }
                final_result.push(obj)
            })
        }
        console.log("Final Tree is : ",final_result)

I Hope it'll help You

like image 41
vajid khokhar Avatar answered Jun 05 '23 03:06

vajid khokhar