我有一个任意的树结构。
root
|--node1
| |--node2
| | |--leaf1
| |
| |--leaf2
|
|--node3
|--leaf3
每个节点和叶子都有2个属性:id
和name
。
1.:
给出了一个叶子id。查询应返回从root到该leaf的整个路径,包含所有节点的id
和name
属性。
如果返回值是排序的节点数组,或者它是嵌套节点的对象,那么它并不重要。
示例:如果提供id
的{{1}},则查询应返回:leaf2
。
root(id, name), node1(id, name), leaf2(id, name)
给定任何节点2.:
:获取整个(子)树。在这里检索单个对象会很好,每个节点都有一个id
数组。
children
首先,我尝试将树简单地建模为单个JSON文档,但随后查询将变得不可能:无法找到叶子的嵌套级别。如果我知道1.:
从根到叶子的整个路径,我就必须使用具有多个位置运算符的投影,而MongoDB目前不支持这种投影。此外,它无法索引叶id
,因为嵌套可能是无限的。
ids
下一个想法是使用平面数据设计,其中每个节点都有一个包含节点的祖先2.:
的数组:
ids
这样我就必须进行2次查询,才能获得从root到某个节点或leaf的整个路径,这非常好。
如果我选择数据模型{
id: ...,
name: ...,
ancestors: [ rootId, node1Id, ... ]
}
:我如何获得整个树或子树?
获取所有后代很简单:2.
。但那些当然不会被排序或嵌套。
有没有办法使用聚合框架或完全不同的数据模型来解决这个问题?
谢谢!
答案 0 :(得分:3)
MongoDB不是图形数据库,也不提供图遍历操作,因此没有直接的解决方案。
您可以使用第2点中描述的数据模型(具有祖先列表的节点),查询find({ancestors:"myStartingNodeId"})
并将结果排序/嵌套到应用程序代码中。
另一种可能性是使用数据模型,其中_id
(或其他某些字段)表示完整路径,例如'root.node1.node2'
。然后可以将图形查询转换为子字符串查询,并且可以通过按_id
排序来实现正确的排序(我希望)。
更新:顺便说一句。 MongoDB文档中描述了一些树结构模式:Model Tree Structures in MongoDB
答案 1 :(得分:2)
这是我最终提出的数据结构。它针对读取查询进行了优化。一些写查询(比如移动子树)可能很痛苦。
{
id: "...",
ancestors: ["parent_node_id", ..., "root_node_id"], // order is important!
children: ["child1_id", "child2_id", ...]
}
轻松获取子树的所有文档
轻松将所有文档从某个节点获取到根
轻松检查某个文档是否是某个节点的父/子/祖先/后代
对儿童进行排序。可以通过更改children
数组顺序
获取ID:findOne({ id: "..." })
获取家长:findOne({ children: "..." })
获取所有祖先:首先执行 Get by ID ,然后获取祖先数组并查找与给定ID列表匹配的所有文档
获取所有儿童:find({ 'ancestors.0': "..." })
获取所有后代:find({ ancestors: "..." })
让所有后代达到 x 代:find({ $and: [ {ancestors: "..."}, {ancestors: {$size: x}} ] })
应用程序代码必须处理正确的订单。
应用程序代码必须构建嵌套对象(也许这可以使用MongoDB聚合框架)。
每个insert
必须使用2个查询完成。
在节点之间移动整个子树必须更新大量文档。
答案 2 :(得分:0)
您可以使用graphLookup
文档: https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/