在上一篇文章【MongoDB聚合管道实战一:project操作符的应用与技巧】中,展示了如何使用 MongoDB 的聚合管道计算每位学生的平均成绩,并找出表现最好的学生。利用聚合管道中的project、sort和limit等操作符,可以方便地进行数据分析与处理,满足具体的业务需求。本文中,我们将探讨如何利用MongoDB的聚合管道$group操作符来实现这样的高级功能。
知识回顾
MongoDB 的聚合管道提供了一系列强大的操作符,用于处理和分析数据。以下是一些常用的聚合阶段及其介绍:
1. $match
-
功能:过滤文档以指定条件。
-
使用场景:在数据流经过管道时,选择符合条件的文档进行后续处理。
-
示例:
{ $match: { status: "active" } }
2. $group
-
功能:将多个文档分组,并对每个组进行聚合操作(例如求和、计数等)。
-
使用场景:进行汇总计算,如计算总销售额、平均值等。
-
示例:
{ $group: { _id: "$customer_id", totalSales: { $sum: "$amount" } } }
3. $sort
-
功能:对文档进行排序。
-
使用场景:根据特定字段的值进行升序或降序排序。
-
示例:
{ $sort: { totalSales: -1 } } // 降序排序
4. $project
-
功能:选择、重命名或计算字段。
-
使用场景:输出所需的字段,进行字段的格式化或计算。
-
示例:
{ $project: { stu_name: 1, averageScore: { $avg: "$scores" } } }
5. $limit
-
功能:限制返回的文档数量。
-
使用场景:控制输出结果集的大小。
-
示例:
{ $limit: 5 }
6. $skip
-
功能:跳过指定数量的文档。
-
使用场景:常用于分页,跳过前面的文档。
-
示例:
{ $skip: 10 }
7. $unwind
-
功能:将数组字段拆分为多条文档,每个文档只包含数组中的一个元素。
-
使用场景:处理文档中包含数组字段的情况,便于进一步分析。
-
示例:
{ $unwind: "$items" }
8. $addFields
-
功能:向文档添加新字段或更新现有字段。
-
使用场景:在管道中动态计算和添加字段。
-
示例:
{ $addFields: { totalPrice: { $multiply: ["$price", "$quantity"] } } }
9. $replaceRoot
-
功能:替换输入文档为指定文档。
-
使用场景:当需要将嵌套文档提升为根文档时使用。
-
示例:
{ $replaceRoot: { newRoot: "$details" } }
10. $facet
-
功能:进行多管道并行处理,可以在同一集合上进行不同的聚合查询。
-
使用场景:在一个聚合操作中获得多个不同的结果。
-
示例:
{ $facet: { totalSales: [{ $group: { _id: null, total: { $sum: "$amount" } }}], averageSales: [{ $group: { _id: null, average: { $avg: "$amount" } }}] } }
11. $lookup
-
功能:进行集合间的连接操作(类似 SQL 的 JOIN)。
-
使用场景:将来自其他集合的数据合并到当前文档。
-
示例:
{ $lookup: { from: "customers", localField: "customer_id", foreignField: "_id", as: "customerInfo" } }
任务描述
使用 MongoDB 的聚合管道来分析销售数据,计算每天的销售总额,并找出销售额最高的日期。利用 $group、$sort 和 $limit 等聚合操作符,您可以灵活地进行数据分析,获取商业洞察。
假设我们有一个 sales 集合,用于记录销售订单。每个销售订单包含以下字段:_id、date(销售日期)、amount(销售金额)。
数据结构
{
"_id": ObjectId("..."),
"date": ISODate("2024-01-01 00:00:00"),
"amount": 200
}
任务准备
首先,我们需要插入一些示例数据:
db.sales.insertMany([
{ date: new Date("2024-01-01"), amount: 200 },
{ date: new Date("2024-01-01"), amount: 150 },
{ date: new Date("2024-01-02"), amount: 300 },
{ date: new Date("2024-01-02"), amount: 250 },
{ date: new Date("2024-01-03"), amount: 400 },
{ date: new Date("2024-01-03"), amount: 300 }
]);
任务实施
聚合管道实现
我们将使用以下聚合管道来实现需求:计算每天的销售总额,并找出销售额最高的日期。
db.sales.aggregate([
{
$group: {
_id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, // 以日期为依据进行分组
totalSales: { $sum: "$amount" } // 计算每天的销售总额
}
},
{
$sort: { totalSales: -1 } // 按销售总额降序排序
},
{
$limit: 1 // 只返回销售额最高的日期
}
]);
聚合管道分解
-
$group:
"_id": { $dateToString: { format: "%Y-%m-%d", date: "$date" } }:以日期为依据进行分组,使用$dateToString操作符将日期格式化为字符串。totalSales: { $sum: "$amount" }:计算每天的销售总额。
-
$sort:
{ totalSales: -1 }:按照计算出的销售总额进行降序排序。
-
$limit:
- 限制结果只返回销售额最高的日期。
执行结果
执行上述聚合管道后,您将获得类似的结果:
[ { "_id": "2024-01-03", "totalSales": 700 }]
这表明在 2024 年 1 月 3 日的销售总额最高,达到了 700。
实验实训
小任务1:计算每门科目的平均成绩
编写一个聚合查询,计算学生在每门科目的平均成绩。假设每个学生有多门科目成绩存储在一个文档中。
预期输出:
科目 (subject) 平均成绩 (averageScore) MongoDB聚合查询:
db.students.aggregate([
{
$unwind: "$scores"
},
{
$group: {
_id: "$scores.subject",
averageScore: { $avg: "$scores.score" }
}
},
{
$project: {
subject: "$_id",
averageScore: 1,
_id: 0
}
}
]);
小任务2:统计每门科目的成绩分布
编写一个聚合查询,统计每门科目中不同成绩范围的学生数量。假设成绩范围分为 60 以下、60-70、70-80、80-90 和 90 以上。
预期输出:
科目 (subject) 不同成绩范围的学生数量 (scoreDistribution) MongoDB聚合查询:
db.students.aggregate([
{
$unwind: "$scores"
},
{
$group: {
_id: "$scores.subject",
distribution: {
$push: {
scoreRange: {
$cond: {
if: { $lt: ["$scores.score", 60] },
then: "<60",
else: {
$cond: {
if: { $lt: ["$scores.score", 70] },
then: "60-70",
else: {
$cond: {
if: { $lt: ["$scores.score", 80] },
then: "70-80",
else: {
$cond: {
if: { $lt: ["$scores.score", 90] },
then: "80-90",
else: ">90"
}
}
}
}
}
}
}
},
count: 1
}
}
}
},
{
$project: {
subject: "$_id",
scoreDistribution: {
$reduce: {
input: "$distribution",
initialValue: {},
in: {
$mergeObjects: [
"$$value",
{
[$$.this.scoreRange]: { $sum: ["$$value.$$.this.count", "$$.this.count"] }
}
]
}
}
}
}
},
{
$project: {
subject: 1,
scoreDistribution: 1,
_id: 0
}
}
]);
小任务3:统计每门科目的最高成绩
编写一个聚合查询,找出每门科目的最高成绩。
预期输出:
科目 (subject) 最高成绩 (maxScore) MongoDB聚合查询:
db.students.aggregate([
{
$unwind: "$scores"
},
{
$group: {
_id: "$scores.subject",
maxScore: { $max: "$scores.score" }
}
},
{
$project: {
subject: "$_id",
maxScore: 1,
_id: 0
}
}
]);
小任务4:统计每个学生每门科目的成绩变化趋势
编写一个聚合查询,统计每个学生每门科目的成绩变化趋势,输出每个学生每门科目的最早和最晚的成绩。
预期输出:
学生编号 (stu_no) 学生姓名 (stu_name) 科目 (subject) 最早成绩 (earliestScore) 最晚成绩 (latestScore) MongoDB聚合查询:
db.students.aggregate([
{
$unwind: "$scores"
},
{
$group: {
_id: { stu_no: "$stu_no", stu_name: "$stu_name", subject: "$scores.subject" },
earliestScore: { $min: "$scores.score" },
latestScore: { $max: "$scores.score" }
}
},
{
$project: {
stu_no: "$_id.stu_no",
stu_name: "$_id.stu_name",
subject: "$_id.subject",
earliestScore: 1,
latestScore: 1,
_id: 0
}
}
]);
通过这些任务,您可以更加熟练地使用 $group 操作符来处理数据,并能够应对各种不同的聚合需求。
总结
通过以上案例,我们展示了如何使用 MongoDB 的聚合管道计算每位学生的平均成绩,并找出表现最好的学生。利用聚合管道中的 $project、$sort 和 $limit 等操作符,可以方便地进行数据分析与处理,满足具体的业务需求。这种灵活性使得 MongoDB 成为处理复杂数据分析任务的理想选择。下一篇我们继续讲解聚合管理group 和 $project`综合应用。