【问题标题】:Calculate depth distribution for tree structures in MongoDB计算 MongoDB 中树结构的深度分布
【发布时间】:2022-01-14 16:15:40
【问题描述】:

考虑一下,我有以下代表树结构的文档:

[
  {
    "_id": 1,
    "parentId": null,
    
  },
  {
    "_id": 2,
    "parentId": null,
    
  },
  {
    "_id": 3,
    "parentId": 1,
    
  },
  {
    "_id": 4,
    "parentId": 1,
    
  },
  {
    "_id": 5,
    "parentId": 2,
    
  },
  {
    "_id": 6,
    "parentId": 5,
    
  },
  
]

使用 MongoDB 聚合计算这些树的深度分布的最高效方法是什么?

我希望收到以下或类似的结果:

[
  {
    "depth": 0,
    "count": 2,
  },
  {
    "depth": 1,
    "count": 3,
  },
  {
    "depth": 2,
    "count": 1,
  }
]

所有count 的总和应该等于集合中的文档数。


我尝试使用各种聚合函数的组合,但只设法在不考虑根节点的情况下计算数据:

db.collection.aggregate([
  {
    // Skipping the root nodes,
    // otherwise it will calculate results
    // for all the nodes and count them multiple times
    $match: {
      parentId: null
    }
  },
  {
    $graphLookup: {
      from: "collection",
      startWith: "$_id",
      connectFromField: "_id",
      connectToField: "parentId",
      as: "descendants",
      depthField: "depth",
    },
  },
  {
    $unwind: "$descendants",
  },
  {
    $group: {
      _id: "$descendants.depth",
      count: {
        $sum: 1,
      },
    },
  },
  {
    $project: {
      _id: 0,
      depth: "$_id",
      count: "$count",
    },
  },
  {
    $sort: {
      depth: 1,
    },
  },
]);

这是带有示例数据的Mongo Playground

【问题讨论】:

    标签: javascript mongodb mongodb-query aggregate query-optimization


    【解决方案1】:

    在该结构中,理论上存在一个未列出的根节点,其 id 为null。从该节点开始的单个图形查找将找到其父级返回为空的所有节点。即它不会在树中包含非根循环。

    要实现这一点,您首先需要检索单个文档,然后从 null 开始查找图形,也许:

    [
      { $limit: 1 },
      {
        $graphLookup: {
          from: "collection",
          startWith: null,
          connectFromField: "_id",
          connectToField: "parentId",
          as: "descendants",
          depthField: "depth",
        }
      }
    },
    

    接下来的放松、分组、项目、排序阶段会将结果转换为您正在寻找的格式。

    至于性能,graphLookup 阶段隐式读取集合中的每个文档。这意味着没有多少索引会提高性能。如果整个集合都适合缓存,您可能会获得合理的性能。随着集合的增长,执行此查询所需的磁盘读取量也会增长。

    如果这是一个不经常运行的操作,可以隔离到一个不为应用程序负载提供服务的专用分析节点,那么长期性能可能是可以接受的。

    【讨论】:

    • 谢谢!看起来我错过了$limit 部分。你能解释一下在这种情况下它是如何工作的吗?
    • 顺便问一下,您确定要编入索引吗? connectToField 字段上的索引不应该实际上提高性能吗?
    • 阈值是,如果您正在读取大约 50-75% 的数据集,则索引不会大大提高性能。在这种情况下,您正在阅读 100% 的数据,因此您不需要索引来帮助您找到要阅读的文档 - 您将阅读所有内容。请通过测试确认/反驳这一点。
    猜你喜欢
    • 2016-09-13
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2013-02-19
    • 2015-11-07
    相关资源
    最近更新 更多