MongoDB 聚合
聚合管道(Aggregation Pipelines)
聚合操作允许你进行分组、排序、执行计算、分析数据等更多操作。
聚合管道可以有一个或多个“阶段”。这些阶段的顺序很重要。每个阶段都会对前一个阶段的结果进行操作。
实例
db.posts.aggregate([
// 第一阶段:只查找具有超过 1 个点赞的文档
{
$match: { likes: { $gt: 1 } }
},
// 第二阶段:按类别对文档进行分组,并对每个类别的点赞数进行求和
{
$group: { _id: "$category", totalLikes: { $sum: "$likes" } }
}
])
样本数据
为了演示聚合管道中阶段的使用,我们将把示例数据加载到我们的数据库中。
在 MongoDB Atlas 仪表板中,转到“数据库”。点击省略号并选择“加载示例数据集”。这将把几个示例数据集加载到你的数据库中。
在接下来的部分中,我们将使用此示例数据更详细地探讨几个聚合管道阶段。
聚合 $group
此聚合阶段按提供的唯一 _id 表达式对文档进行分组。
不要将此 _id 表达式与提供给每个文档的 _id ObjectId 混淆。
实例
在此例中,我们使用了从“聚合介绍”部分中的示例数据加载的“sample_airbnb”数据库。
db.listingsAndReviews.aggregate(
[ { $group : { _id : "$property_type" } } ]
)
这将返回 property_type 字段中的不同值。
聚合 $limit
此聚合阶段限制了传递到下一阶段的文档数量。
实例
在此例中,我们使用了从“聚合介绍”部分中的示例数据加载的“sample_mflix”数据库。
db.movies.aggregate([ { $limit: 1 } ])
这将从集合中返回 1 部电影。
聚合 $project
此聚合阶段仅将指定字段传递到下一个聚合阶段。
这与 find() 方法使用的投影相同。
实例
在此例中,我们使用了从“聚合介绍”部分中的示例数据加载的“sample_restaurants”数据库。
db.restaurants.aggregate([
{
$project: {
"name": 1,
"cuisine": 1,
"address": 1
}
},
{
$limit: 5
}
])
这将返回文档,但仅包含指定的字段。
请注意,_id 字段也被包含在内。除非明确排除,否则此字段始终包含在内。
我们使用 1 来包含字段,使用 0 来排除字段。
注意:您不能在同一对象中同时使用 0 和 1。唯一的例外是 _id 字段。您应该指定要包含的字段或要排除的字段。
聚合 $sort
此聚合阶段将所有文档按照指定的排序顺序进行分组排序。
请记住,阶段的顺序很重要。每个阶段只对前一阶段提供的文档进行操作。
实例
在这个例子中,我们使用了从聚合介绍”部分的示例数据中加载的“sample_airbnb”数据库。
db.listingsAndReviews.aggregate([
{
$sort: { "accommodates": -1 }
},
{
$project: {
"name": 1,
"accommodates": 1
}
},
{
$limit: 5
}
])
这将返回按 accommodates 字段降序排序的文档。
可以使用 1 或 -1 来选择排序顺序。1 为升序,-1 为降序。
聚合 $match
此聚合阶段的行为类似于查找。它将过滤与所提供的查询匹配的文档。
在管道中尽早使用 $match 可以提高性能,因为它限制了下一阶段必须处理的文档数量。
实例
在此例中,我们使用了从“聚合介绍”部分中的示例数据加载的“sample_airbnb”数据库。
db.listingsAndReviews.aggregate([
{ $match : { property_type : "House" } },
{ $limit: 2 },
{ $project: {
"name": 1,
"bedrooms": 1,
"price": 1
}}
])
这将只返回 property_type 为 "House" 的文档。
聚合 $addFields
此聚合阶段向文档添加新字段。
实例
在这个例子中,我们使用了从“聚合介绍”部分的示例数据中加载的“sample_restaurants”数据库。
db.restaurants.aggregate([
{
$addFields: {
avgGrade: { $avg: "$grades.score" }
}
},
{
$project: {
"name": 1,
"avgGrade": 1
}
},
{
$limit: 5
}
])
这将返回文档以及一个新字段 avgGrade,该字段将包含每家餐馆 grades.score 的平均值。
聚合 $count
此聚合阶段计算从上一阶段传递的文档总数。
实例
在这个例子中,我们使用了从“聚合介绍”部分的示例数据中加载的“sample_restaurants”数据库。
db.restaurants.aggregate([
{
$match: { "cuisine": "Chinese" }
},
{
$count: "totalChinese"
}
])
这将返回 $count 阶段中文档的数量,并将其作为名为 "totalChinese" 的字段。
聚合 $lookup
此聚合阶段对同一数据库中的集合执行左外连接。
有四个必填字段:
from |
用于在同一数据库中查找的集合。 |
localField |
主要集合中的字段,可用作 from 集合中的唯一标识符。 |
foreignField |
from 集合中的字段,可用作主要集合中的唯一标识符。 |
as |
将包含 from 集合中匹配文档的新字段的名称。 |
实例
在此例中,我们使用了从“聚合介绍”部分中的示例数据加载的“sample_mflix”数据库。
db.comments.aggregate([
{
$lookup: {
from: "movies",
localField: "movie_id",
foreignField: "_id",
as: "movie_details",
},
},
{
$limit: 1
}
])
这将返回与每条评论相关的电影数据。
聚合 $out
此聚合阶段将从聚合管道返回的文档写入集合。
$out 阶段必须是聚合管道的最后阶段。
实例
在此例中,我们使用了从“聚合介绍”部分的示例数据中加载的“sample_airbnb”数据库。
db.listingsAndReviews.aggregate([
{
$group: {
_id: "$property_type",
properties: {
$push: {
name: "$name",
accommodates: "$accommodates",
price: "$price",
},
},
},
},
{ $out: "properties_by_type" },
])
第一阶段将根据 property_type 对属性进行分组,并为每个属性包含 name、accommodates 和 price 字段。$out 阶段将在当前数据库中创建一个名为 properties_by_type 的新集合,并将结果文档写入该集合。