MongoDB树模型:获取所有祖先,获取所有后代

MongoDB树模型:获取所有祖先,获取所有后代,mongodb,tree,aggregation-framework,ancestor,descendant,Mongodb,Tree,Aggregation Framework,Ancestor,Descendant,我有一个任意的树结构 数据结构示例: 每个节点和叶都有两个属性:id和name 重要问题: 1.给出了一个叶id。查询应该返回从根到叶的整个路径,以及所有节点的id和name属性 如果返回值是节点的排序数组,或者是嵌套节点的对象,则不重要 示例:如果给出了leaf2的id,则查询应返回:root(id,name)、node1(id,name)、leaf2(id,name) 2.给定任何节点id:获取整个(子)树。在这里,最好检索一个对象,其中每个节点都有一个子数组 思想、尝试和错误: 1.

我有一个任意的树结构

数据结构示例: 每个节点和叶都有两个属性:
id
name


重要问题:
1.
给出了一个叶id。查询应该返回从根到叶的整个路径,以及所有节点的
id
name
属性

如果返回值是节点的排序数组,或者是嵌套节点的对象,则不重要

示例:如果给出了
leaf2
id
,则查询应返回:
root(id,name)、node1(id,name)、leaf2(id,name)


2.
给定任何节点
id
:获取整个(子)树。在这里,最好检索一个对象,其中每个节点都有一个子数组


思想、尝试和错误:
1.:
首先,我试图简单地将树建模为单个JSON文档,但随后查询将变得不可能:无法确定叶的嵌套级别。如果我知道
id
s从根到叶的整个路径,我就必须使用带有多个位置运算符的投影,而MongoDB目前不支持这一点。此外,由于嵌套可能是无限的,因此无法为叶
ids
编制索引

2.:
下一个想法是使用平面数据设计,其中每个节点都有一个数组,其中包含节点的祖先ID:

{
  id: ...,
  name: ...,
  ancestors: [ rootId, node1Id, ... ]
}
这样,我必须进行两次查询,以获得从根到某个节点或叶的整个路径,这非常好

问题: 如果我选择数据模型
2.
:如何获得整个树或子树

获取所有子代很容易:
find({祖先:myStartingNodeId})
。但这些当然不会被排序或嵌套

有没有办法使用聚合框架或完全不同的数据模型来解决这个问题


谢谢大家!

MongoDB不是图形数据库,不提供图形遍历操作,因此没有直接的解决方案

您可以使用第2点中描述的数据模型。(具有祖先列表的节点),查询
查找({祖先:“myStartingNodeId”})
并将结果排序/嵌套到应用程序代码中

另一种可能是使用数据模型,其中
\u id
(或某些其他字段)表示完整路径,例如
'root.node1.node2'
。然后,可以将图形查询转换为子字符串查询,只需按此
\u id
排序即可实现正确的排序(我希望如此)



更新:顺便说一句,MongoDB文档中描述了一些树结构模式:

以下是我最终提出的数据结构。它针对读取查询进行了优化。有些写查询(如移动子树)可能会很痛苦

{
  id: "...",
  ancestors: ["parent_node_id", ..., "root_node_id"], // order is important!
  children: ["child1_id", "child2_id", ...]
}
好处:
  • 易于获取子树的所有文档

  • 从某个节点到根节点轻松获取所有文档

  • 易于检查某些文档是否是某个节点的父/子/祖先/后代

  • 孩子们被分类了。可以通过更改
    子项
    数组顺序轻松移动

如何使用它:
  • 按ID获取:
    findOne({ID:“…”})

  • 获取父项:
    findOne({children:“…”})

  • 获取所有祖先:首先按ID获取,然后获取祖先数组并查找与给定ID列表匹配的所有文档

  • 获取所有子项:
    find({'祖先.0':“…”})

  • 获取所有子体:
    find({祖先:“…”})

  • 获取x代以下的所有子代:
    find({$and:[{祖先:“…”},{祖先:{$size:x}}]})

缺点:
  • 应用程序代码必须遵循正确的顺序

  • 应用程序代码必须构建嵌套对象(使用MongoDB聚合框架可能实现这一点)

  • 每次插入
    都必须使用2个查询来完成

  • 在节点之间移动整个子树必须更新大量文档


您可以使用
graphLookup

文件:

我知道MongoDB不是图形数据库,但我不想为那个用例使用额外的数据库。也许另一种选择是存储两个数据模型,然后查询最适合的模型?关键是:树将创建一次,然后每隔几个月修改一次。虽然不会有任何相关的开销。你怎么看?我不喜欢模型1——嵌套文档——因为它太动态了。正如您所写的,编写查询或索引很难或不可能。如果图形增长过大,则可能会达到文档大小限制(16MB)。我在处理大量大型文档方面的经验不好,因为在网络、反序列化等方面存在开销。但是你可以使用它。我会将“主要数据”存储在单独的文档(模型2)中,但您可以将它们聚合到“模型1”文档中作为缓存。我知道我的模型有点修改:我在每个节点中添加了一个
子节点
数组,因此我可以在树中轻松地向前和向后移动,对于每个节点,我可以立即看到是否有子元素,或者它是否是一片叶子。那么关于模型1:我将完全忽略它。在查询之后,必须有人从数据中创建(在我的例子中)Java对象,不管是由框架还是我自己创建。通过这种方式,我甚至可以在飞行中使用附加信息来丰富数据。
{
  id: "...",
  ancestors: ["parent_node_id", ..., "root_node_id"], // order is important!
  children: ["child1_id", "child2_id", ...]
}