Recursive search on a collection in MongoDB

后端 未结 2 1630
自闭症患者
自闭症患者 2020-12-18 21:15

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 a

相关标签:
2条回答
  • 2020-12-18 22:09

    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" }
                        } 
                    } 
                }
            }}
        ]
    )
    
    0 讨论(0)
  • 2020-12-18 22:18

    If you are open to use client side javascript, you can use recursion on the mongo shell to achieve this:

    var pushAncesstors = function (name, doc) {
      if(doc.parent) {
        db.collection.update({name : name}, {$addToSet : {"ancesstors" : {name : doc.parent}}});
        pushAncesstors(name, db.collection.findOne({name : doc.parent}))
      }
    }
    
    db.collection.find().forEach(function (doc){
      pushAncesstors(doc.name, doc);
    })
    

    This will give you complete hirearchy for all products. Sample output:

    { "_id" : "1", "name" : "A", "parent" : "" }
    { "_id" : "2", "name" : "B", "parent" : "A", "ancesstors" : [ { "name" : "A" } ] }
    { "_id" : "3", "name" : "C", "parent" : "B", "ancesstors" : [ { "name" : "B" }, { "name" : "A" } ] }
    { "_id" : "4", "name" : "D", "parent" : "C", "ancesstors" : [ { "name" : "C" }, { "name" : "B" }, { "name" : "A" } ] }
    

    If your requirement is not to update the correct collection, insert the data in a diffferent collection and update there. The pushAncesstors function will change to:

    var pushAncesstors = function (name, doc) {
      if(doc.parent) {
        db.outputColl.save(doc)
        db.outputColl.update({name : name}, {$addToSet : {"ancesstors" : {name : doc.parent}}});
        pushAncesstors(name, db.collection.findOne({name : doc.parent}))
      }
    }
    
    0 讨论(0)
提交回复
热议问题