MongoDB 聚合管道 aggregate()

聚合管道 aggregate

MongoDB 的 aggregate()方法是其聚合框架的核心,用于对数据进行复杂的计算、转换和分析。
它通过一个由多个​​阶段(Stage)​​ 组成的​​管道(Pipeline)​​ 来处理数据,每个阶段对输入文档执行特定操作,并将结果传递给下一阶段。

常用聚合阶段 Stage

$match 过滤

在管道初期使用 $match 可以大幅减少后续阶段需要处理的文档数量,提升聚合效率。

$group 分组

按指定的标识符 _id 表达式对文档进行分组,并对每个分组应用​​聚合表达式​​(如 $sum, $avg)进行计算。这是进行统计汇总的核心阶段。
_id: null 可将所有文档分到同一组进行整体计算。

{
  $group: {
    _id: '$customer_id',
    first_purchase_date: { $first: '$orderdate' },
    total_value: { $sum: '$value' },
    total_orders: { $sum: 1 },
    orders: {
      $push: {
        orderdate: '$orderdate',
        value: '$value',
      },
    },
  },
}

$project 投影

重塑文档结构,类似于 SQL 中的 SELECT。作用如下:

  • 保留、排除特定字段(设置 1或 0)。
  • 重命名字段。
  • 插入新计算字段(如使用 $multiply, $divide等运算符)。
    默认情况下 _id 字段会被包含,可以显式设置 _id: 0 来排除它

删除文档中的指定字段

$sort 排序

根据指定字段对文档进行排序,可选升序(1)或降序(-1)。

$limit 限制

限制返回的文档数量,用于分页或获取前 N 个结果。

$skip 跳过

跳过指定数量的文档,用于分页。

$unwind 展开

将数组字段拆分成多个文档,每个文档包含数组中的一个元素。
可以使用 preserveNullAndEmptyArrays: true 选项来防止在数组为空、不存在或为 null 时丢弃该文档

$lookup 关联

从另一个集合中查询与输入文档相关的文档,并将匹配的结果作为一个新数组字段添加到输入文档中

$densify 填充缺失

为指定字段的缺失值自动创建新的文档

{
  $densify: {
    field: <fieldName>,
    partitionByFields: [ <field1>, <field2>, ... <fieldn> ],
    range: {
      step: <number>,
      unit: <timeUnit>, // 仅当字段为日期类型时需要
      bounds: < "full" | "partition" | [ <lowerBound>, <upperBound> ] >
    }
  }
}

$setWindowFields 窗口函数

为每个文档计算窗口函数值,如滚动平均值、滚动总和等。

{
  $setWindowFields: {
    partitionBy: <expression>, // 可选:指定分组字段
    sortBy: <sortSpec>,       // 必需:指定窗口内数据的排序方式
    output: {                 // 必需:指定要添加的新字段及其计算方式
      <outputField>: {
        <windowOperator>: <expression>,
        window: {
          documents | range: [ <lowerBound>, <upperBound> ]
        }
      },
      ... // 可以添加多个输出字段
    }
  }
}

注意事项:

  • ​字段类型限制​​:如果集合中有文档的 field 字段是日期类型,则必须指定 unit;如果是数值类型,则不能指定 unit,否则会报错。
  • ​​输出顺序​​:​​$densify 不保证输出文档的顺序​​。如果需要特定的顺序(例如按时间或数值排序),应在 $densify 阶段之后使用 $sort 阶段。
  • ​​新文档的字段​​:由 $densify 创建的新文档​​通常只包含 field 字段和 partitionByFields 中指定的字段​​(如果使用了分区)。原文档中的其他字段在这些新创建的文档中不会出现。

聚合表达式

统计类

  • $sum:求和。$sum: 1表示计数。
  • $avg:均值
  • $min / $max获取最小/最大值
  • $first/ $last:获取第一个/最后一个文档的指定字段

数组操作类

  • $push:将值添加到结果数组中​​(允许重复)​​
  • $addToSet:将值添加到结果数组中​​(确保唯一,不重复)

算数操作符

常用于 $project 阶段进行字段间的计算

  • $multiply:乘法
  • $divide:除法

优化建议

  • 管道顺序至关重要​​:阶段的顺序会直接影响聚合结果和性能。通常应优先使用 $match$project 阶段来尽可能早地过滤和减少文档的大小与数量。
  • 索引的使用​​:在 $match$sort 阶段涉及的字段上创建索引可以显著提高聚合查询的速度。
  • 内存限制与磁盘使用:
    • 每个聚合管道阶段最多只能使用 100MB 内存。如果处理大量数据时可能超出此限制,可以通过设置 allowDiskUse: true 选项允许 MongoDB 将临时数据写入磁盘,但这会降低性能。
    • 聚合结果返回的单个文档不能超过 16MB 的 BSON 文档大小限制。对于大型结果集,应使用返回游标(cursor)的方式。