快速上手MongoDB聚合:$match和$group操作符详解

MongoDB的聚合管道就像一条数据处理的流水线,我们可以把数据依次通过不同的“工序”(聚合操作符)进行筛选、统计和转换,最终得到想要的结果。今天我们来学习聚合管道中最常用的两个操作符:$match$group,帮你快速上手MongoDB的聚合分析。

一、MongoDB聚合管道基础

聚合管道由多个阶段组成,每个阶段对应一个操作符(如$match$group),数据从左到右依次经过每个阶段处理。我们用一个示例集合来理解,假设我们有一个学生成绩集合students,文档结构如下:

{ "_id": 1, "name": "张三", "subject": "数学", "score": 90, "class": "一班" }
{ "_id": 2, "name": "李四", "subject": "语文", "score": 85, "class": "一班" }
{ "_id": 3, "name": "王五", "subject": "数学", "score": 78, "class": "二班" }
{ "_id": 4, "name": "赵六", "subject": "语文", "score": 92, "class": "二班" }
{ "_id": 5, "name": "钱七", "subject": "数学", "score": 88, "class": "一班" }

二、$match:筛选数据的“过滤器”

$match就像SQL中的WHERE子句,用来筛选出符合条件的文档,只把满足条件的文档传递给下一个阶段,避免后续处理无效数据。

语法

{ $match: { <查询条件> } }

<查询条件>find()方法的条件一致,支持等于、大于、小于、包含等操作符,例如:
- 等于:{ class: "一班" }(筛选班级为“一班”的文档)
- 大于:{ score: { $gt: 80 } }(筛选分数大于80的文档)
- 包含:{ subject: { $in: ["数学", "语文"] } }(筛选科目为数学或语文的文档)

示例:筛选“一班”的学生成绩

db.students.aggregate([
  { $match: { class: "一班" } } // 只保留class为“一班”的文档
])

执行后会返回所有class为“一班”的3条文档:

{ "_id": 1, "name": "张三", "subject": "数学", "score": 90, "class": "一班" }
{ "_id": 2, "name": "李四", "subject": "语文", "score": 85, "class": "一班" }
{ "_id": 5, "name": "钱七", "subject": "数学", "score": 88, "class": "一班" }

三、$group:分组统计的“计算器”

筛选出数据后,$group按指定字段分组,并对每个组内的数据进行统计(如计数、求和、求平均等)。

语法

{ $group: { _id: <分组键>, <自定义字段>: { <累加器操作符>: <字段名> }, ... } }
  • _id:分组键,用来指定分组的依据(如按classsubject分组)。若设为null,则整个集合作为一个组。
  • 自定义字段:如student_countavg_score,用来存储统计结果。
  • 累加器操作符:对每个组内的字段进行计算,常用的有:
  • $sum:求和(如$sum: "$score"计算总分)
  • $avg:求平均值(如$avg: "$score"计算平均分)
  • $count:计数(等价于$sum: 1,统计文档数量)
  • $max/$min:取最大值/最小值

示例1:按班级分组统计学生数量

db.students.aggregate([
  { $group: { _id: "$class", student_count: { $sum: 1 } } } 
  // 按class分组,每个组计数1(统计学生数量)
])

结果:

{ "_id": "一班", "student_count": 3 },
{ "_id": "二班", "student_count": 2 }

示例2:按科目分组计算总分数

db.students.aggregate([
  { $group: { _id: "$subject", total_score: { $sum: "$score" } } } 
  // 按subject分组,计算每个科目的总分
])

结果:

{ "_id": "数学", "total_score": 90 + 78 + 88 = 256 },
{ "_id": "语文", "total_score": 85 + 92 = 177 }

示例3:按班级分组计算平均分数

db.students.aggregate([
  { $group: { _id: "$class", avg_score: { $avg: "$score" } } } 
  // 按class分组,计算每个班级的平均分
])

结果:

{ "_id": "一班", "avg_score": (90 + 85 + 88)/3  87.67 },
{ "_id": "二班", "avg_score": (78 + 92)/2 = 85 }

四、$match$group组合使用

实际分析中,我们常先筛选再分组。例如:统计“数学”科目中每个班级的平均分数

步骤:

  1. 第一步:用$match筛选出subject为“数学”的文档;
  2. 第二步:用$groupclass分组,计算平均分数。
db.students.aggregate([
  { $match: { subject: "数学" } }, // 筛选数学科目的学生
  { $group: { _id: "$class", avg_math_score: { $avg: "$score" } } } // 按班级分组算平均分
])

结果:

{ "_id": "一班", "avg_math_score": (90 + 88)/2 = 89 },
{ "_id": "二班", "avg_math_score": 78 }

总结

  • $match:像“过滤器”,先筛选数据,减少后续处理量。
  • $group:像“计算器”,按分组键分组后,用累加器操作符统计每个组的数据。
  • 两者组合是聚合分析的核心模式:先过滤再分组统计。

通过这两个操作符,你已经能完成基础的统计分析。后续可学习$project(投影,只保留需要字段)、$sort(排序)等操作符,进一步扩展数据分析能力!

小夜