我正在尝试使用新的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"。 我正在寻找任何建议。谢谢:))
答案 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 copy
和copy.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");
}