聚合管道的思维方式
聚合管道(Aggregation Pipeline)像一条数据流水线:文档从一端流入,依次经过一个个阶段(stage),每个阶段做一种变换(过滤、投影、分组、关联),最后流出结果集。和传统 SQL 的 SELECT/GROUP BY 是同一类思维,但阶段可以任意串联组合,表达力更强。
本文核心知识点
- $match过滤文档,尽量放最前。
- $group按字段分组聚合。
- $lookup左连接另一集合。
- $facet一次产出多组统计。
$match 与 $project:先筛后投影
$match按条件过滤文档,$project决定输出哪些字段、要不要计算新字段。关键经验:把 $match 放管道最前面,尽早缩小数据量,它还能命中索引,性能天差地别;先投影再分组也能减少中间数据。
$group:分组聚合
$group 按 _id 指定的字段分组,用累加器统计每组:$sum 求和、$avg 平均、$max/$min 最值、$push 把字段收集成数组。这是做报表统计最核心的阶段。
db.orders.aggregate([
{ $match: { status: "paid" } },
{ $group: {
_id: "$city",
total: { $sum: "$amount" },
cnt: { $sum: 1 }
} }
])
$lookup:集合关联
$lookup相当于 SQL 的左连接:把当前集合的字段和另一个集合关联、把子文档嵌进来。比如订单集合想带上商品详情,不用应用层再查一次,管道里一次 join 完成。
$bucket 与 $facet
$bucket把数值按区间分桶(如按年龄分 18-25、26-35),做分布统计很方便。$facet能在一次管道里并行跑出多组不同统计,避免重复扫描集合。
管道顺序与性能
顺序很重要:$match、$limit 尽量靠前以利用索引;$project 放在中间减少传输;$group、$lookup 放后面。对大集合跑聚合,先用 explain 看是否命中索引、是否有内存超限(默认 100MB 上限,超了要 allowDiskUse)。
典型报表示例
做"城市销量排行":先 $match 出已支付订单,$group 按城市求和,再 $sort 按总额倒序,最后 $limit 取前十。一条管道就把应用层要写好几步的统计一次完成。
落地清单
- $match 放最前,尽量命中索引。
- 能在管道里 join 就别应用层多次查询。
- 大集合聚合用 explain 验证、注意内存上限。
- 分布统计用 $bucket,多组统计用 $facet。
- 先投影减少中间数据量。