首页 / MongoDB 入门教程 / 聚合管道概述

MongoDB 入门教程

聚合管道概述

本教程共 50 篇 · 第 28 篇 · 更新于 2026-07-30 · 约 7 分钟阅读

MongoDBMongoDB 入门教程聚合管道aggregationstageaggregate

28. 聚合管道概述

本节目标:理解聚合管道(Aggregation Pipeline)是怎么回事,会用 aggregate() 把多个阶段串成流水线,并知道 $match 要尽早写来减少数据量。

find 擅长「按条件取文档、挑字段」,但遇到「按用户分组算总额」「统计各类商品销量」这种要多步加工的分析,find 就力不从心了。聚合框架(Aggregation Framework)就是干分析的利器。

db.orders.insertMany([
  { _id: 1001, user_id: 1, status: "completed", total: 657, created_at: ISODate("2024-06-01") },
  { _id: 1002, user_id: 2, status: "completed", total: 1099, created_at: ISODate("2024-06-05") },
  { _id: 1003, user_id: 1, status: "shipped", total: 597, created_at: ISODate("2024-06-10") },
  { _id: 1004, user_id: 3, status: "pending", total: 528, created_at: ISODate("2024-06-12") },
  { _id: 1005, user_id: 2, status: "cancelled", total: 398, created_at: ISODate("2024-06-15") }
])

什么是管道(Pipeline)

聚合像工厂流水线:文档从一头进去,经过一道道工序(阶段 / Stage),每道工序加工后传给下一道,最后从另一头出来的是加工好的结果。

aggregate() 接收一个阶段数组,每个阶段是一个以运算符命名的文档:

// 先过滤已完成订单,再按用户分组算总额
db.orders.aggregate([
  { $match: { status: "completed" } },
  { $group: { _id: "$user_id", totalAmount: { $sum: "$total" } } }
])

阶段(Stage)是流水线的一环

每个阶段只做一件事:$match 负责筛、$group 负责聚、$sort 负责排。阶段按数组顺序依次执行,前一阶段的输出是后一阶段的输入。

Note

阶段名都以 $ 开头,比如 $match$group$project$sort$limit。后面几章会逐个拆开讲。

第一个阶段放 $match:尽早缩小数据

这是我反复强调的经验:能过滤的,越早过滤越好。把 $match 写在流水线最前面,能让后续阶段处理更少的文档,速度明显更快。

// 好写法:先 $match 缩小范围
db.orders.aggregate([
  { $match: { status: "completed" } },
  { $group: { _id: "$user_id", cnt: { $sum: 1 } } }
])

如果先把 $group 放前面、最后才 $match,相当于先对全部订单分组再筛,白白多算。

Warning

$match 放在最前还能利用索引;放中间或最后,往往已经用不上索引了。性能差别在大数据量时非常明显。

聚合与 find 的分工

能力find聚合管道
条件筛选$match 同样强
字段投影支持$project 更强
排序/分页支持$sort/$limit 支持
分组统计不支持$group 核心能力
多集合关联不支持$lookup 支持
字段重塑/计算表达式丰富

一句话:简单取数用 find;要分组、关联、复杂计算,上聚合。

聚合默认不改原数据

Tip

普通的 aggregate() 只是读取并加工,不会改动集合里的任何文档。只有管道里显式出现 $out$merge 阶段,才会把结果写回集合。

下一章我们细看最常用的一对阶段:$match 过滤,以及 $project 重塑字段。