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
的新集合,并将结果文档写入该集合。