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