MongoDB尝试获取树结构的$ graphLookup

时间:2017-06-07 14:35:26

标签: mongodb

我正在尝试使用新的MongoDB v3.4 $ graphLookup聚合管道。我有这个简单的树集合,有一些节点和父DBRef:

{ "_id" : ObjectId("59380657bbdbfb36c18a80f2"), "name" : "Root node 1" },
{ "_id" : ObjectId("5938068abbdbfb36c18a80f5"), "name" : "Child 1.1", "parent" : ObjectId("59380657bbdbfb36c18a80f2") },
{ "_id" : ObjectId("593806b0bbdbfb36c18a80f7"), "name" : "Subchild 1.1.1", "parent" : ObjectId("5938068abbdbfb36c18a80f5") },
{ "_id" : ObjectId("5938068abbdbfb36c18a80f6"), "name" : "Child 1.2", "parent" : ObjectId("59380657bbdbfb36c18a80f2") },
{ "_id" : ObjectId("59380657bbdbfb36c18a80f3"), "name" : "Root node 2" }

我想得到这种树形结构:

- Root node 1
    - Child 1.1
        - Subchild 1.1.1
    - Child 1.2
- Root node 2

所以,我尝试使用新的$ graphLookup聚合管道,如下所示:

db.getCollection('tree').aggregate([
    { $match: { parent: { $exists: false } } },
    {
        $graphLookup: {
            from: "tree",
            startWith: "$_id",
            connectFromField: "_id",
            connectToField: "parent",
            as: "children"
        }
   },
   { $sort: { name: 1 } }
])

但我的问题是我得到了#34; Root node 1"在一个集合中:

{
    "_id" : ObjectId("59380657bbdbfb36c18a80f2"),
    "name" : "Root node 1",
    "children" : [
        { "_id" : ObjectId("593806b0bbdbfb36c18a80f7"), "name" : "Subchild 1.1.1", "parent" : ObjectId("5938068abbdbfb36c18a80f5") },
        { "_id" : ObjectId("5938068abbdbfb36c18a80f6"), "name" : "Child 1.2", "parent" : ObjectId("59380657bbdbfb36c18a80f2") },
        { "_id" : ObjectId("5938068abbdbfb36c18a80f5"), "name" : "Child 1.1", "parent" : ObjectId("59380657bbdbfb36c18a80f2") }
    ]
},
{
    "_id" : ObjectId("59380657bbdbfb36c18a80f3"),
    "name" : "Root node 2",
    "children" : [ ]
}

我不知道如何递归地查找子项以获得" Subchild 1.1.1"在儿童集合中,儿童1.1"。 我正在寻找任何建议。谢谢:))

3 个答案:

答案 0 :(得分:5)

$graphLookup不生成依赖关系的层次结构 - 它对连接的文档执行递归搜索,但结果被展平为单维数组。以下是文档中的引用:

  

对于每个匹配的文档,$ graphLookup取值    _id 并检查集合中的每个文档   匹配的值。对于每个匹配,$ graphLookup添加   将from集合中的文档与数组 children 匹配。此步骤以递归方式继续,直到不再存在   找到匹配的文件,或直到操作到达   maxDepth参数指定的递归深度。

即。它以递归方式搜索依赖文档,但无论子项的位置有多深,每个找到的文档都会添加到父文档的相同子数组中。

注意 - 您没有看到Child 1.1已连接Subchild 1.1.1,因为您要在match阶段过滤掉这些文档:

{ $match: { parent: { $exists: false } } }

仅选择没有父级的文档 - "Root node 1""Root node 2"。如果要删除此过滤器,则将返回具有其依赖项层次结构的所有其他文档:

{
    "name" : "Child 1.1",
    "children" : [ 
        { "name" : "Subchild 1.1.1" }
    ]
},
{
    "name" : "Child 1.2"
    "children" : []
},
{
    "name" : "Root node 1",
    "children" : [ 
        { "name" : "Subchild 1.1.1" }, 
        { "name" : "Child 1.2" }, 
        { "name" : "Child 1.1" }
    ]
},
{
    "name" : "Root node 2",
    "children" : []
},
{
    "name" : "Subchild 1.1.1"
    "children" : []
}

如果您不想在单个子数组中混合来自不同“深度”树的子项,那么请查看文档中的有趣注释

  

将maxDepth字段设置为0等同于非递归   $ lookup搜索阶段。

这意味着每个文档都会将其所有直接子项都放入子数组中,然后查找将停止而不进行任何进一步的递归搜索。输出将是

{
    "name" : "Child 1.1",
    "children" : [ 
        { "name" : "Subchild 1.1.1" }
    ]
},
{
    "name" : "Child 1.2"
    "children" : []
},
{
    "name" : "Root node 1",
    "children" : [ 
        { "name" : "Child 1.2" }, 
        { "name" : "Child 1.1" }
    ]
},
{
    "name" : "Root node 2",
    "children" : []
},
{
    "name" : "Subchild 1.1.1"
    "children" : []
}

答案 1 :(得分:0)

还没有找到使用maxDepth参数来解决该问题的方法,但是depthField一个参数(在我的情况下,简单地设置为'depth')对您有所帮助,{{3 }}重新计算结果。

这是一个递归重建$graphLookup结果的函数。

def list_tree_lookup(keys, data, depth=0):
    result = []
    for a in data:
        if a['depth'] + depth == 0 or (a['depth'] == depth and a['_id'] in keys):
            b = copy.deepcopy(a)
            b['children'] = list_tree_lookup(a['children'], data, depth+1)
            del b['depth']
            result.append(b)
    return result

我使用import copycopy.deepcopy只是为了确保一切都保持不变。

那么,当你拥有

rootNode1 = {
    "_id" : "Root node 1",
    "children" : [
        { "_id" : "Subchild 1.1.1", "depth" : 1 },
        { "_id" : "Child 1.2", "depth" : 0 },
        { "_id" : "Child 1.1", "depth" : 0 }
    ]
}

呼叫list_tree_lookup([], rootNode1['children'])会产生

[
    {
        "_id": "Child 1.1"
        "children": [
            { "_id": "Subchild 1.1.1" }
        ]
    },
    {
        "_id": "Child 1.2"
        "children": []
    }
]

,并且可以将其分配回rootNode1['children']

答案 2 :(得分:0)

const allItems = await db.getCollection('tree').find({});
    if (allItems.length) {
        allItems = allItems.map((item) =>
            item.toObject()
        );
        allItems = allItems
            .map((item) => {
                let parentObj = null;
                if (item.parent_id) {
                    parentObj = allItems.find(
                        (findItem) =>
                            findItem._id.toString() ===
                            item.parent_id.toString()
                    );
                }
                if (parentObj) {
                    if (!parentObj.children) {
                        parentObj.children = [];
                    }
                    parentObj.children.push(item);
                }
                return item;
            })
            .filter((item) => !item.parent_id);
    } else {
        console.error("items not found");
    }