聚合管道

聚合管道用于统计、分组、转换数据。

可以理解成一条数据处理流水线:

输入文档 -> 过滤 -> 分组 -> 排序 -> 输出结果

MongoDB 使用 aggregate 执行聚合。

一、最简单的聚合

统计已发布文章数量:

db.posts.aggregate([
  {
    $match: {
      status: "published"
    }
  },
  {
    $count: "total"
  }
])

结果:

[
  { total: 10 }
]

二、常见阶段

阶段作用
$match过滤文档
$project选择或改造字段
$group分组统计
$sort排序
$skip跳过
$limit限制数量
$lookup关联其他集合
$count统计数量

三、按状态分组统计

db.posts.aggregate([
  {
    $group: {
      _id: "$status",
      total: { $sum: 1 }
    }
  }
])

结果类似:

[
  { _id: "published", total: 20 },
  { _id: "draft", total: 5 }
]

_id 是分组字段。

$sum: 1 表示每条文档加 1。

四、统计每个作者的文章数

db.posts.aggregate([
  {
    $group: {
      _id: "$author.id",
      total: { $sum: 1 }
    }
  },
  {
    $sort: {
      total: -1
    }
  }
])

这里按嵌套字段 author.id 分组。

五、只输出部分字段

db.posts.aggregate([
  {
    $project: {
      _id: 0,
      title: 1,
      authorName: "$author.username",
      createdAt: 1
    }
  }
])

$project 可以:

  • 保留字段。
  • 排除字段。
  • 重命名字段。
  • 计算新字段。

六、$lookup 关联查询

如果 posts 里只保存 authorId

{
  title: "MongoDB 入门",
  authorId: ObjectId("665f0e0b6f8c2b5f8e123456")
}

可以用 $lookup 关联 users

db.posts.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "authorId",
      foreignField: "_id",
      as: "author"
    }
  }
])

author 会是一个数组。

如果只想取第一个作者,可以继续使用 $unwind

db.posts.aggregate([
  {
    $lookup: {
      from: "users",
      localField: "authorId",
      foreignField: "_id",
      as: "author"
    }
  },
  {
    $unwind: "$author"
  }
])

七、聚合使用建议

  • 能先 $match 就先 $match,减少后续处理的数据量。
  • 聚合里用到的过滤字段也要考虑索引。
  • 不要把所有业务逻辑都塞进超复杂聚合。
  • 报表统计适合聚合,普通详情接口不一定需要聚合。

聚合很强大,但也要保持可读性。