让我们通过一个具体的例子来详细解释 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限制查询深度 
这种查询方式相比原来的嵌套树结构,在并发环境下性能更好,因为不再需要锁定整个树结构,而是可以单独查询和修改各个文件夹节点。