让我们通过一个具体的例子来详细解释 get_subtree
函数如何使用 MongoDB 的 $graphLookup
操作来查询文件夹子树。
示例数据
假设我们有以下文件夹结构:
1 | Root (id: 1) |
对应的 MongoDB 文档:
1 | [ |
查询示例 1:获取整个 Documents 子树
1 | get_subtree(folder_id=2) # 查询 Documents 文件夹的完整子树 |
聚合管道执行过程:
$match
阶段:首先找到 ID 为 2 的文档1
{ "_id": 2, "name": "Documents", "parent": 1, "path": "/Documents/" }
$graphLookup
阶段:递归查找所有子文件夹- 从
_id: 2
开始 - 查找
parent
字段等于当前文档_id
的所有文档 - 递归过程:
- 第一层:找到
_id: 3
(parent=2) 和_id: 4
(parent=2) - 第二层:尝试找 parent=3 和 parent=4 的文档,但没有找到
- 第一层:找到
- 结果收集到
children
数组中
- 从
最终返回结果:
1 | { |
查询示例 2:限制查询深度
1 | get_subtree(folder_id=1, depth=2) # 查询 Root 文件夹,深度为2 |
聚合管道执行过程:
$match
阶段:找到 ID 为 1 的文档 (Root)$graphLookup
阶段:maxDepth
: 1 (因为 depth=2,maxDepth=depth-1)- 递归过程:
- 第一层:找到
_id: 2
(parent=1) 和_id: 5
(parent=1) - 第二层:不继续查找,因为达到了 maxDepth
- 第一层:找到
最终返回结果:
1 | { |
查询示例 3:查询单层结构
1 | get_subtree(folder_id=5) # 查询 Photos 文件夹 |
返回结果:
1 | { |
关键参数解释
from
: 要查询的集合名称(在此例中是 “folder”)startWith
: 开始递归查询的起点值(通常是当前文档的_id
)connectFromField
: 当前文档中用于连接的字段(通常是_id
)connectToField
: 目标文档中用于连接的字段(在此例中是parent
)as
: 存储查询结果的数组字段名maxDepth
: 递归的最大深度(0表示只查直接子项)
性能考虑
- 对于大型树结构,
$graphLookup
可能会消耗较多资源 - 可以通过添加索引优化:
1
2# 确保 parent 字段有索引
Folder.create_index([("parent", 1)]) - 对于非常深的树结构,考虑使用
maxDepth
限制查询深度
这种查询方式相比原来的嵌套树结构,在并发环境下性能更好,因为不再需要锁定整个树结构,而是可以单独查询和修改各个文件夹节点。