在MongoDB中,聚合查询(Aggregation)是一种强大的数据处理工具,它允许我们对集合中的文档进行多阶段的转换和分析,就像流水线一样处理数据。比如统计不同地区的用户数量、计算用户平均年龄、分析订单总金额等,这些都可以通过聚合管道轻松实现。
准备示例数据¶
为了方便理解,我们假设一个用户数据集合users,每个用户文档结构如下(简化版):
{
"_id": ObjectId("..."),
"name": "张三",
"age": 25,
"gender": "男",
"region": "北京",
"orders": [
{ "amount": 100, "date": "2023-01-01" },
{ "amount": 150, "date": "2023-02-15" }
]
}
包含的字段:姓名(name)、年龄(age)、性别(gender)、地区(region)、订单数组(orders,每个订单有金额amount和日期date)。
基础聚合阶段介绍¶
MongoDB聚合管道由多个阶段(Stage) 组成,每个阶段处理数据的一个操作,前一个阶段的输出作为下一个阶段的输入。常用阶段和操作符:
- $match:过滤文档(类似SQL的WHERE)
- $group:按指定字段分组,配合累加器(如$sum、$avg)统计结果
- $project:只保留需要的字段(类似SQL的SELECT)
- $sort:按指定字段排序(类似SQL的ORDER BY)
- $unwind:展开数组字段(如将orders数组拆分为多个文档)
- 累加器操作符:$sum(求和)、$avg(平均值)、$max(最大值)、$min(最小值)
实例1:统计每个性别的用户数量¶
需求:统计“男”“女”用户各有多少人。
聚合管道:
db.users.aggregate([
{
$group: {
_id: "$gender", // 按gender字段分组
count: { $sum: 1 } // 每个分组的文档数+1(即统计数量)
}
},
{
$sort: { count: -1 } // 按数量降序排序
}
])
解释:
- $group:按gender分组,_id指定分组字段,count是统计结果字段,$sum:1表示每个文档加1(即每个用户算1次)。
- $sort:按count降序排列,让结果更直观。
输出示例:
[
{ "_id": "男", "count": 120 },
{ "_id": "女", "count": 80 }
]
实例2:统计各地区用户的平均年龄¶
需求:计算每个地区用户的平均年龄(忽略年龄为null的用户)。
聚合管道:
db.users.aggregate([
{
$match: { age: { $exists: true } } // 过滤掉age不存在的用户
},
{
$group: {
_id: "$region", // 按地区分组
avg_age: { $avg: "$age" } // 计算该地区用户的平均年龄
}
},
{
$project: { // 只保留需要的字段,隐藏_id
region: "$_id",
avg_age: 1,
_id: 0
}
},
{
$sort: { avg_age: -1 } // 按平均年龄降序排列
}
])
解释:
- $match:用$exists: true过滤掉没有年龄数据的用户,避免$avg计算错误。
- $group:$avg: "$age"计算分组内的平均年龄(自动忽略null/不存在的字段)。
- $project:将_id重命名为region,并隐藏原始_id,结果更清晰。
实例3:统计每个用户的总消费金额¶
需求:用户可能有多个订单,需要统计每个用户的订单总金额。
聚合管道:
db.users.aggregate([
{
$unwind: "$orders" // 展开orders数组,每个订单拆分为独立文档
},
{
$group: {
_id: "$_id", // 按用户ID分组(即每个用户)
total_amount: { $sum: "$orders.amount" } // 累加每个订单的金额
}
},
{
$sort: { total_amount: -1 }, // 按总金额降序排列
$limit: 10 // 只取消费最高的前10个用户
}
])
解释:
- $unwind: "$orders":将orders数组中的每个元素拆分为独立文档(例如一个用户有2个订单,拆分为2个文档)。
- $group:按用户_id分组,累加每个订单的amount金额。
- $limit: 10:只取前10条结果(可配合$skip实现分页,如$skip: 20跳过前20条)。
实例4:统计各地区用户数量、平均年龄、最大年龄¶
需求:同时统计每个地区的用户数、平均年龄和最大年龄。
聚合管道:
db.users.aggregate([
{
$group: {
_id: "$region",
user_count: { $sum: 1 },
avg_age: { $avg: "$age" },
max_age: { $max: "$age" }
}
},
{
$sort: { user_count: -1 }
}
])
解释:
- $group中可以同时使用多个累加器:$sum:1统计用户数,$avg: "$age"计算平均年龄,$max: "$age"取最大年龄。
- 结果会包含每个地区的user_count、avg_age、max_age三个字段。
总结¶
MongoDB聚合查询通过管道式阶段实现灵活的数据处理,适合初学者的核心操作包括:
1. 过滤:用$match缩小数据范围(类似SQL的WHERE)。
2. 分组统计:用$group+累加器($sum/$avg等)实现统计。
3. 字段处理:用$project隐藏不需要的字段,结果更简洁。
4. 排序/分页:用$sort和$limit/$skip控制结果顺序和数量。
建议从简单分组统计开始练习,逐步尝试复杂场景(如多层嵌套聚合、数组处理等)。遇到问题可参考MongoDB官方文档的聚合管道语法(https://docs.mongodb.com/manual/reference/operator/aggregation/),多动手写代码验证结果!