You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用MongoDB $graphLookup实现文档层级关系查询?

基于MongoDB $graphLookup实现双向层级关系查询

核心问题分析

你的需求是通过partOf(子节点关联父节点ID)和contains(父节点关联子节点ID)两个数组字段,查询文档的完整层级关系。普通单方向的$graphLookup无法覆盖双向关联场景,需要调整关联字段的匹配逻辑,或针对树形结构的构建做递归处理。

样本数据

// relationships_collection 样本数据
[
  {
    "_id": ObjectId("60d21b4667d0d8992e610c85"),
    "name": "总分类",
    "contains": [ObjectId("60d21b4667d0d8992e610c86"), ObjectId("60d21b4667d0d8992e610c87")],
    "partOf": []
  },
  {
    "_id": ObjectId("60d21b4667d0d8992e610c86"),
    "name": "子分类A",
    "contains": [ObjectId("60d21b4667d0d8992e610c88")],
    "partOf": [ObjectId("60d21b4667d0d8992e610c85")]
  },
  {
    "_id": ObjectId("60d21b4667d0d8992e610c87"),
    "name": "子分类B",
    "contains": [],
    "partOf": [ObjectId("60d21b4667d0d8992e610c85")]
  },
  {
    "_id": ObjectId("60d21b4667d0d8992e610c88"),
    "name": "孙分类A1",
    "contains": [],
    "partOf": [ObjectId("60d21b4667d0d8992e610c86")]
  }
]

目标输出示例(树形层级)

{
  "_id": ObjectId("60d21b4667d0d8992e610c85"),
  "name": "总分类",
  "contains": [ObjectId("60d21b4667d0d8992e610c86"), ObjectId("60d21b4667d0d8992e610c87")],
  "partOf": [],
  "children": [
    {
      "_id": ObjectId("60d21b4667d0d8992e610c86"),
      "name": "子分类A",
      "contains": [ObjectId("60d21b4667d0d8992e610c88")],
      "partOf": [ObjectId("60d21b4667d0d8992e610c85")],
      "children": [
        {
          "_id": ObjectId("60d21b4667d0d8992e610c88"),
          "name": "孙分类A1",
          "contains": [],
          "partOf": [ObjectId("60d21b4667d0d8992e610c86")],
          "children": []
        }
      ]
    },
    {
      "_id": ObjectId("60d21b4667d0d8992e610c87"),
      "name": "子分类B",
      "contains": [],
      "partOf": [ObjectId("60d21b4667d0d8992e610c85")],
      "children": []
    }
  ]
}

调整后的聚合查询方案

方案1:向下构建树形层级(从根节点遍历子节点)

适用于从父节点出发,获取所有子节点的嵌套层级:

db.relationships_collection.aggregate([
  // 匹配根节点(这里选择partOf为空的总分类,可根据需求修改匹配条件)
  { $match: { partOf: { $size: 0 } } },
  // 用$graphLookup获取所有子节点及层级
  {
    $graphLookup: {
      from: "relationships_collection",
      startWith: "$contains",          // 从当前节点的contains数组开始遍历子节点
      connectFromField: "contains",    // 子节点继续用自己的contains数组遍历下一级
      connectToField: "_id",           // 匹配目标文档的_id
      as: "children",                  // 存储结果的字段名
      maxDepth: 10,                    // 最大遍历层级,按需设置
      depthField: "level"              // 可选:添加层级深度字段,方便调试
    }
  },
  // 将扁平的子节点数组转换为嵌套树形结构
  {
    $addFields: {
      children: {
        $function: {
          body: function(children) {
            // 用Map存储所有节点,方便快速查找
            const nodeMap = new Map();
            children.forEach(node => {
              nodeMap.set(node._id.toString(), { ...node, children: [] });
            });
            // 遍历节点,根据partOf关联父节点,构建嵌套关系
            children.forEach(node => {
              node.partOf.forEach(parentId => {
                const parentNode = nodeMap.get(parentId.toString());
                if (parentNode) {
                  parentNode.children.push(nodeMap.get(node._id.toString()));
                }
              });
            });
            // 返回根节点的直接子节点
            return Array.from(nodeMap.values()).filter(node => 
              node.partOf.some(id => id.toString() === "60d21b4667d0d8992e610c85")
            );
          },
          args: ["$children"],
          lang: "js"
        }
      }
    }
  }
])

方案2:双向遍历(同时获取父级和子级节点)

如果需要获取某个节点的所有关联层级(包括上级父节点和下级子节点),可使用以下查询:

db.relationships_collection.aggregate([
  // 匹配目标节点
  { $match: { _id: ObjectId("60d21b4667d0d8992e610c86") } },
  {
    $graphLookup: {
      from: "relationships_collection",
      startWith: { $concatArrays: ["$contains", "$partOf"] }, // 同时从contains和partOf开始遍历
      connectFromField: { $concatArrays: ["$contains", "$partOf"] }, // 每个节点继续双向遍历
      connectToField: "_id",
      as: "allRelatedNodes",
      depthField: "depth" // depth为正表示子节点,负表示父节点(可自定义逻辑)
    }
  }
])

常见问题修正

你之前的查询可能存在以下问题:

  • 仅设置了单方向关联(比如只匹配partOf而未处理contains),导致层级不完整
  • 未对$graphLookup返回的扁平数组做嵌套处理,无法得到树形结构
  • 未限制遍历深度,可能导致性能问题或无限递归

内容的提问来源于stack exchange,提问作者Prabhjot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 09:09:51