5月29日 01:09

Mongoose 聚合管道有哪些常用阶段?$match 为什么放最前面?

Mongoose 聚合通过 Model.aggregate([...stages]) 执行管道,常用阶段:$match 过滤文档、$group 分组统计($sum 计数/求和、$avg 均值、$push 收集数组)、$project 投影和计算新字段、$sort 排序、$limit/$skip 分页、$lookup 关联其他集合(类似 SQL JOIN)、$unwind 展开数组为多条记录、$facet 并行执行多个子管道。$match 必须放最前面,因为它能利用索引减少进入后续阶段的数据量,管道是顺序执行的,越早过滤性能越好。$lookup 是左外连接,localField/foreignField 匹配或 pipeline 子查询方式,结果以数组形式放入 as 指定字段。

追问

$lookup 的 pipeline 子查询方式和 localField/foreignField 有什么区别? pipeline 方式支持在关联时加 $match$project 等阶段过滤和塑形关联数据,更灵活且减少返回量;localField/foreignField 只做等值连接,无法过滤。pipeline 还支持 let 变量引用主文档字段。

$unwind 会导致数据膨胀怎么办? 对大数组 unwind 再 group 会产生大量中间文档。替代方案:用 $reduce$map 在数组上直接聚合,或在 unwind 前用 $project 只保留需要的字段减少每条文档体积。

$facet 有什么限制? 各子管道不能引用其他子管道的结果,也不能包含 $out$merge 阶段。facet 内每个子管道独立处理同一份输入数据,内存消耗是各子管道之和。

聚合结果超过 100MB 内存限制怎么办?allowDiskUse: true 选项让中间结果写入临时文件,或优化管道尽早 $match + $project 缩减数据量。

写段代码

javascript
const stats = await Order.aggregate([ { $match: { createdAt: { $gte: new Date('2025-01-01') } } }, { $group: { _id: '$productId', total: { $sum: '$amount' }, count: { $sum: 1 } } }, { $sort: { total: -1 } }, { $limit: 10 } ]);
标签:Mongoose