MongoDB聚合管道是一套基于文档流的数据处理框架,它允许把多个操作阶段按顺序串联起来,前一个阶段的输出作为后一个阶段的输入。社区和文档里偶尔提到的$features特性列表,并不是指某个单独的命令,而是对聚合框架中以美元符号开头的阶段命令和表达式操作符的统称。理解这一层含义之后,再去看官方文档或调试信息就不会被词汇本身绊住。

一、基础阶段特性:过滤、投影与分组
聚合管道最常用的阶段是$match、$project和$group。$match负责按条件过滤文档,它的行为和find中的查询条件基本一致,但放在管道开头可以尽早利用索引,减少后续阶段需要处理的数据量。$project用来选择或排除字段,也能在投影过程中添加计算字段,不过更推荐使用$addFields或$set来追加字段,因为这两个阶段不会影响已有字段的传递。
$group是聚合统计的核心,它按照指定的_id表达式对文档分组,然后配合累加器表达式计算每组的汇总值。累加器支持的常用操作包括$sum、$avg、$min、$max、$first、$last、$push和$addToSet。与关系数据库的GROUP BY不同,MongoDB允许在_id中使用文档或数组结构,因此可以按多个字段组合、日期截断结果甚至对象键进行分组。
下面这段代码先按状态过滤订单,再按客户ID分组统计订单数量和总金额:
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $group: { _id: "$customerId", totalAmount: { $sum: "$amount" }, orderCount: { $sum: 1 } } },
{ $sort: { totalAmount: -1 } }
])
管道还提供了$sort、$limit和$skip用于排序和分页。其中$sort如果紧跟在$match之后且排序字段与索引顺序一致,可以借助索引避免内存排序,这对大数据集非常重要。
二、表达式特性:条件判断与字段计算
聚合表达式的强大之处在于可以在阶段内完成复杂的字段计算,而不需要先把数据取回应用层再处理。条件表达式$cond、$ifNull和$switch能够根据文档字段动态生成新值。$cond接受if、then、else三个参数,作用相当于三元运算符;$switch则适合多分支判断,结构上更接近编程语言中的switch语句。
除了逻辑判断,MongoDB还提供算术操作符如$add、$subtract、$multiply、$divide、$mod,字符串操作符如$concat、$toUpper、$substrCP,日期操作符如$year、$month、$dayOfMonth、$dateToString,以及数组操作符$size、$slice、$map、$filter。这些操作符可以嵌套组合,实现字段拼接、数据脱敏、时区转换和数组过滤等功能。
以下示例在$project阶段同时使用$concat拼接触点信息,并用$cond根据消费金额标记客户等级:
db.customers.aggregate([
{
$project: {
fullContact: { $concat: ["$phone", " / ", "$email"] },
level: { $cond: { if: { $gte: ["$totalSpent", 10000] }, then: "VIP", else: "普通" } }
}
}
])
需要注意的是,表达式操作符虽然功能丰富,但过度嵌套会降低可读性,也容易触发单个文档的内存限制。对于特别复杂的业务逻辑,可以考虑拆分成多个阶段,或者使用$function在服务端执行自定义JavaScript,但后者会牺牲一定的可移植性和性能。
三、高级特性:跨集合关联与分面统计
当数据分散在多个集合时,$lookup阶段可以实现类似SQL左外连接的功能。它允许从另一个集合中查询匹配的文档,并把结果写入当前文档的数组字段。MongoDB从5.0开始支持带管道的$lookup,连接条件不再局限于简单的等值匹配,还可以在子管道中进行过滤、投影和限制,这让跨集合数据整合变得更加灵活。
$facet是另一个高级特性,它能够在单个聚合阶段内并行执行多个子管道,每个子管道独立输出一组统计结果。这种设计特别适合构建仪表盘或分类汇总接口,一次往返就能拿到多个维度的聚合数据。$bucket和$bucketAuto则用于把连续数值或日期范围切分成离散区间,用于分布分析;$sortByCount是$group加$sort的快捷方式,适合统计高频标签。
以下代码使用$facet同时统计各状态订单数量、消费最高的三个客户和按月分布:
db.orders.aggregate([
{
$facet: {
byStatus: [
{ $group: { _id: "$status", count: { $sum: 1 } } }
],
topCustomers: [
{ $group: { _id: "$customerId", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 3 }
],
monthly: [
{ $group: { _id: { $dateToString: { format: "%Y-%m", date: "$createdAt" } }, count: { $sum: 1 } } }
]
}
}
])
此外,$merge和$out可以把聚合结果写入现有集合或新集合,适合定期生成报表或物化视图的场景。在Atlas Search环境中,$search作为管道的入口阶段可以提供全文检索、模糊匹配和相关度评分,进一步扩展了聚合管道的适用边界。
四、执行限制与性能优化思路
聚合管道默认使用最多100MB的内存来保存阶段之间的文档,超过限制会报错。可以通过在aggregate命令中设置allowDiskUse: true让MongoDB把临时数据写入磁盘,但磁盘IO会明显拖慢执行速度,因此更推荐从索引和阶段顺序上做优化。
性能优化的第一个原则是把$match放在管道最前面,并确保过滤条件能够命中索引。第二个原则是尽早使用$project剔除不需要的字段,减少文档体积。第三个原则是谨慎使用$unwind,它会将数组展开成多个文档,当数组很大时会产生数据膨胀。如果只是想计算数组长度或提取特定元素,优先考虑$size、$slice和$arrayElemAt等表达式,而不是直接展开数组。
最后,熟悉执行计划工具explain也能帮助发现性能瓶颈。对聚合查询执行db.collection.explain("executionStats").aggregate([...]),可以查看每个阶段扫描的文档数、是否使用索引以及内存占用情况。根据这些指标调整阶段顺序和索引设计,往往能把运行时间从秒级优化到毫秒级。
MongoDB聚合管道$操作符特性列表修改时间:2026-09-22 00:24:05