MongoDB $graphLookup查询问题:计算jerry与superman的分隔度
解决MongoDB $graphLookup计算节点分隔度的问题
问题分析
你当前的查询存在几个关键问题,导致返回结果不符合预期:
- 未限定遍历起点,对集合中所有4个连接文档都执行了
$graphLookup,因此返回4个结果; - 用
$size统计connections数组长度来计算分隔度完全错误——数组包含了双向连接的重复节点,和实际路径深度无关; - 未利用
depthField记录路径深度,无法准确计算节点间的分隔步数。
修正后的查询语句
针对双向节点结构,我们需要从指定起点(jerry)开始遍历,记录路径深度,最终提取到目标节点(superman)的最短路径深度并转换为分隔度:
db.getCollection("connections").aggregate([ // 生成起始文档,指定遍历起点为jerry { $documents: [{ startNode: "jerry" }] }, { $graphLookup: { from: "connections", startWith: "$startNode", // 从jerry开始遍历 connectFromField: "from", connectToField: "to", maxDepth: 5, depthField: "depth", // 记录每个节点相对于起点的深度 as: "pathNodes", restrictSearchWithMatch: { to: "superman" } // 仅保留到superman的路径,减少计算量 } }, // 计算最小分隔度:深度值+1(depth=0代表直接相连,对应分隔度1;depth=1代表隔1个节点,对应分隔度2) { $project: { degreeOfSeparation: { $add: [{ $min: "$pathNodes.depth" }, 1] } } }, // 确保只返回有效结果 { $match: { degreeOfSeparation: { $exists: true } } } ])
关键逻辑说明
- 限定起点:用
$documents生成一个仅包含起点的文档,避免遍历集合中所有无关的连接记录; - 记录路径深度:通过
depthField参数,$graphLookup会自动记录每个遍历节点相对于起点的深度——jerry直接连接的lois深度为0,lois连接的superman深度为1; - 转换为分隔度:深度值加1即为实际分隔度(深度0对应分隔度1,深度1对应分隔度2),取最小值保证得到最短路径的分隔度;
- 过滤冗余路径:
restrictSearchWithMatch只保留到superman的路径,减少不必要的数据处理。
替代方案(基于现有文档起点)
如果不想用$documents,也可以从jerry的连接文档开始遍历:
db.getCollection("connections").aggregate([ { $match: { from: "jerry" } }, { $graphLookup: { from: "connections", startWith: "$to", connectFromField: "from", connectToField: "to", maxDepth: 5, depthField: "depth", as: "traversedPaths", restrictSearchWithMatch: { to: "superman" } } }, { $project: { degreeOfSeparation: { $add: [{ $min: "$traversedPaths.depth" }, 2] } } }, { $group: { _id: null, minDegree: { $min: "$degreeOfSeparation" } } }, { $project: { _id: 0, degreeOfSeparation: "$minDegree" } } ])
这个方案中,起点是jerry→lois的连接,所以遍历到superman的深度为0,需要加2得到分隔度2。
内容的提问来源于stack exchange,提问作者user3524762
相关产品推荐
相关产品推荐

