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 对属性进行分组,并为每个属性包含 nameaccommodatesprice 字段。$out 阶段将在当前数据库中创建一个名为 properties_by_type 的新集合,并将结果文档写入该集合。