一、什么是MongoDB聚合管道?¶
想象你有一堆散落的数据,比如学生的考试成绩、购物订单信息,现在要分析这些数据:比如统计各科平均分、找出最高分学生、按类别分组统计等。MongoDB的聚合管道就像一条“数据流水线”,你可以把数据依次经过多个“加工站”(称为“阶段”),每个加工站对数据做不同处理,最后得到你想要的分析结果。
举个例子:
你有一个“学生成绩表”集合(类似Excel表格),里面记录了每个学生的姓名、科目、分数。要分析“数学平均分”,你不需要先把所有数据导出到Excel,再手动计算,而是用聚合管道“一步到位”:先筛选出“科目=数学”的学生(筛选阶段),再计算这些学生的平均分(分组统计阶段)。
二、聚合管道的核心概念¶
聚合管道由多个“阶段” 组成,每个阶段是一个“加工站”,数据从第一个阶段流到下一个,最终输出结果。阶段的顺序很重要,因为每个阶段处理前一个阶段的输出。
核心阶段和操作符¶
| 阶段名(操作符) | 作用通俗解释 | 类似SQL |
|---|---|---|
$match |
筛选数据(类似 find() 查询) |
WHERE |
$project |
只保留需要的字段(或重命名/计算新字段) | SELECT |
$group |
按条件分组,统计数据(如平均分、总数) | GROUP BY |
$sort |
对结果排序 | ORDER BY |
$limit |
限制返回的数据量 | LIMIT |
三、实战案例:从简单到复杂¶
假设我们有一个名为 students 的集合,数据结构如下(简化版):
{
"_id": 1,
"name": "张三",
"class": 1,
"subject": "数学",
"score": 85
},
{
"_id": 2,
"name": "李四",
"class": 1,
"subject": "数学",
"score": 92
},
{
"_id": 3,
"name": "王五",
"class": 2,
"subject": "数学",
"score": 78
}
案例1:筛选+投影(只看数学成绩)¶
需求:找出1班学生的数学成绩,只显示姓名和分数。
代码实现:
db.students.aggregate([
// 阶段1:筛选班级=1且科目=数学的学生
{ $match: { class: 1, subject: "数学" } },
// 阶段2:只保留姓名和分数字段
{ $project: { _id: 0, name: 1, score: 1 } }
])
输出结果:
{ "name": "张三", "score": 85 },
{ "name": "李四", "score": 92 }
解释:
- $match 相当于 SQL 的 WHERE class=1 AND subject="数学",只保留符合条件的文档。
- $project 中的 _id: 0 表示不显示默认的 _id 字段,name: 1 和 score: 1 表示显示这两个字段。
案例2:分组统计(按科目计算平均分)¶
需求:按“科目”分组,计算每个科目的平均分数。
代码实现:
db.students.aggregate([
// 阶段1:按科目分组,计算平均分
{ $group: {
_id: "$subject", // 分组依据:科目字段
avgScore: { $avg: "$score" } // 计算平均分,$avg 是累加器操作符
}
},
// 阶段2:按平均分降序排序
{ $sort: { avgScore: -1 } }
])
输出结果:
{ "_id": "数学", "avgScore": 85.0 },
{ "_id": "语文", "avgScore": 76.5 } // 假设还有语文数据
解释:
- $group 的 _id: "$subject" 表示按“科目”字段分组。
- avgScore 是自定义的统计结果名,$avg: "$score" 表示对每个分组的 score 字段求平均值。
- $sort 按 avgScore 降序排列(-1 表示降序,1 表示升序)。
案例3:多阶段组合(统计班级平均分+总人数)¶
需求:统计每个班级每个科目的平均分和总人数。
代码实现:
db.students.aggregate([
// 阶段1:按班级+科目分组
{ $group: {
_id: { class: "$class", subject: "$subject" }, // 复合分组:班级+科目
totalStudents: { $sum: 1 }, // 总人数:每个文档+1
avgScore: { $avg: "$score" } // 平均分
}
},
// 阶段2:按班级排序
{ $sort: { "_id.class": 1, "_id.subject": 1 } }
])
输出结果:
{ "_id": { "class": 1, "subject": "数学" }, "totalStudents": 2, "avgScore": 88.5 },
{ "_id": { "class": 2, "subject": "数学" }, "totalStudents": 1, "avgScore": 78.0 }
解释:
- $group 的 _id 可以是一个对象,包含多个字段(班级+科目),实现复合分组。
- $sum: 1 是统计总人数的常用写法(每个文档贡献1个计数)。
四、常用操作符速查表¶
| 操作符 | 作用 | 示例 |
|---|---|---|
$match |
筛选文档 | { $match: { score: { $gt: 60 } } }(分数>60) |
$project |
控制输出字段 | { $project: { name: 1, newScore: { $add: ["$score", 5] } } } |
$group |
分组并统计 | { $group: { _id: "$subject", total: { $sum: "$score" } } } |
$sort |
排序 | { $sort: { score: -1 } }(降序) |
$limit |
限制数量 | { $limit: 10 }(取前10条) |
$skip |
跳过数据 | { $skip: 5 }(跳过前5条) |
五、总结¶
MongoDB聚合管道就像“数据加工厂”,通过多个阶段的组合,你可以轻松完成复杂的数据分析:
1. 筛选:用 $match 过滤不需要的数据;
2. 投影:用 $project 只保留关键信息;
3. 分组:用 $group 按条件统计(平均分、总数等);
4. 排序/限制:用 $sort 和 $limit 优化结果。
从简单例子开始,逐步尝试多阶段组合,你会发现聚合管道比手动在代码中处理数据更高效、更灵活!
小建议:多练习不同场景的组合(比如“先筛选再分组”“先排序再限制”),熟悉每个阶段的作用,很快就能掌握~