介绍
在本实验中,你将学习 MongoDB 中数据聚合的基础知识。你将重点学习如何使用聚合管道来分组文档、对分组数据执行计算,然后过滤、排序和重塑结果。这些操作对于 MongoDB 中的数据分析和报告至关重要。在本实验结束时,你将能够熟练使用 $group 操作符以及其他关键的聚合阶段,从你的数据中提取有意义的见解。
按字段分组文档
数据聚合的第一步通常是根据共同字段对文档进行分组。在此步骤中,你将连接到 MongoDB shell,创建一个新的数据库和集合,然后使用 $group 操作符按类别对文档进行分组。
首先,在终端中运行以下命令打开 MongoDB shell:
mongosh
进入 shell 后,你将看到 test> 提示符。让我们切换到一个名为 salesdb 的新数据库并插入一些示例产品数据。当你首次向数据库和集合插入数据时,MongoDB 会自动创建它们。
将以下命令复制并粘贴到 mongosh shell 中:
use salesdb
db.products.insertMany([
{ category: "Electronics", brand: "Apple", price: 1200 },
{ category: "Electronics", brand: "Samsung", price: 800 },
{ category: "Electronics", brand: "Sony", price: 950 },
{ category: "Apparel", brand: "Nike", price: 150 },
{ category: "Apparel", brand: "Adidas", price: 120 },
{ category: "Books", brand: "Penguin", price: 25 },
{ category: "Books", brand: "Penguin", price: 35 }
]);
现在你有了数据,可以执行聚合操作了。以下命令按 category 字段对文档进行分组,并使用 $sum 累加器计算每个类别的总价。
db.products.aggregate([
{
$group: {
_id: "$category",
totalPrice: { $sum: "$price" }
}
}
]);
示例输出:
[
{ "_id": "Books", "totalPrice": 60 },
{ "_id": "Apparel", "totalPrice": 270 },
{ "_id": "Electronics", "totalPrice": 2950 }
]
让我们分解一下这个聚合阶段:
db.products.aggregate([...]): 这是执行聚合的方法。它接受一个由阶段组成的数组,形成一个管道。$group: 这是对输入文档进行分组的阶段操作符。_id: "$category": 这个表达式指定了分组的键。在这里,我们按category字段的值进行分组。$前缀表示字段路径。totalPrice: { $sum: "$price" }: 这是一个累加器。它在输出文档中定义了一个名为totalPrice的新字段。$sum操作符计算组内所有文档的price字段的总和。
使用多个累加器
$group 阶段可以同时计算多个聚合。你可以计算平均值、查找最小值或最大值,以及计算每个组中的项目数。此步骤演示了如何在单个 $group 阶段中使用多个累加器。
你仍然应该在 mongosh shell 中,使用 salesdb 数据库。
让我们编写一个更复杂的聚合,它计算每个类别的总价、平均价和产品数量。
db.products.aggregate([
{
$group: {
_id: "$category",
totalPrice: { $sum: "$price" },
averagePrice: { $avg: "$price" },
productCount: { $sum: 1 }
}
}
]);
示例输出:
[
{
"_id": "Books",
"totalPrice": 60,
"averagePrice": 30,
"productCount": 2
},
{
"_id": "Apparel",
"totalPrice": 270,
"averagePrice": 135,
"productCount": 2
},
{
"_id": "Electronics",
"totalPrice": 2950,
"averagePrice": 983.3333333333334,
"productCount": 3
}
]
这里是我们使用的新累加器:
averagePrice: { $avg: "$price" }:$avg操作符计算组内所有文档的price字段的平均值。productCount: { $sum: 1 }: 这是在组中计算文档数量的常用方法。对于每个文档,它将1加到总和中,从而有效地计算了文档数量。
过滤分组数据
在分组数据后,你通常需要根据计算出的值来过滤这些组。例如,你可能只想查看总销售额超过特定金额的类别。$match 阶段用于此目的。它可以放在 $group 阶段之后,以过滤分组后的文档。
让我们找出产品总价大于 500 的类别。
db.products.aggregate([
{
$group: {
_id: "$category",
totalPrice: { $sum: "$price" }
}
},
{
$match: {
totalPrice: { $gt: 500 }
}
}
]);
示例输出:
[{ "_id": "Electronics", "totalPrice": 2950 }]
在此管道中:
$group阶段首先计算每个类别的totalPrice。- 来自
$group阶段的输出文档随后传递给$match阶段。 $match阶段过滤这些文档,只保留totalPrice字段大于 ($gt) 500 的文档。
这展示了聚合管道的强大之处,一个阶段的输出成为下一个阶段的输入。
对分组数据进行排序
在你获得分组和过滤后的数据后,最后一步通常是对其进行排序。$sort 阶段允许你根据一个或多个字段,以升序或降序排列文档。
让我们按类别对产品进行分组,计算总价,然后按 totalPrice 降序(从高到低)对结果进行排序。
db.products.aggregate([
{
$group: {
_id: "$category",
totalPrice: { $sum: "$price" }
}
},
{
$sort: {
totalPrice: -1
}
}
]);
示例输出:
[
{ "_id": "Electronics", "totalPrice": 2950 },
{ "_id": "Apparel", "totalPrice": 270 },
{ "_id": "Books", "totalPrice": 60 }
]
$sort 阶段接受一个文档,该文档指定要排序的字段和排序顺序:
totalPrice: -1: 这将按totalPrice字段对文档进行排序。值-1指定降序。要按升序排序,应使用1。
你也可以按多个字段进行排序。例如,$sort: { category: 1, totalPrice: -1 } 将首先按类别名称字母顺序排序,然后在类别名称相同的情况下按总价降序排序。
使用 $project 重塑输出
有时 $group 阶段的输出格式并非你所需要的。例如,分组键默认命名为 _id。$project 阶段允许你通过添加、删除或重命名字段来重塑输出文档。
让我们构建一个管道,按类别分组,按总价排序,然后重塑输出,为类别提供一个更具描述性的字段名。
db.products.aggregate([
{
$group: {
_id: "$category",
totalPrice: { $sum: "$price" }
}
},
{
$sort: {
totalPrice: -1
}
},
{
$project: {
_id: 0,
category: "$_id",
total: "$totalPrice"
}
}
]);
示例输出:
[
{ "category": "Electronics", "total": 2950 },
{ "category": "Apparel", "total": 270 },
{ "category": "Books", "total": 60 }
]
$project 阶段的工作方式如下:
_id: 0: 这将从输出中排除_id字段。默认情况下,_id始终包含,除非被显式排除。category: "$_id": 这将创建一个名为category的新字段,并将其值赋为现有_id字段的值。total: "$totalPrice": 这将创建一个名为total的新字段,并将其值赋为totalPrice字段的值。
使用 $project 是格式化聚合管道最终输出以供应用程序或报告使用的强大方法。
总结
在本实验中,你学习了如何使用 MongoDB 聚合管道来分组和分析数据。你首先使用 $group 操作符对文档进行分组并计算总和。然后,你通过使用 $avg 和 $sum: 1 等多个累加器来执行更复杂的计算,从而扩展了这一能力。你还学会了如何将聚合阶段链接在一起,使用 $match 过滤分组结果,使用 $sort 对其进行排序,以及使用 $project 将最终输出重塑为清晰、易读的格式。这些是任何使用 MongoDB 的开发者或数据分析师的基础技能。

