聚合是 MongoDB 中一个非常实用的功能,类似于 SQL 中的 where group by 等操作。

可以做分析型、统计型的场景,也可以用来做复杂数据的计算和处理。

聚合框架介绍

MongoDB 聚合框架(Aggregation Framework)是一个计算框架,它可以:

  • 作用在一个或几个集合上
  • 对集合中的数据进行一系列运算
  • 将这些数据转化为期望的格式

从效果而言,聚合框架相当于 SQL 查询中的:

  • GROUP BY
  • LEFT/OUTER/RIGHT/INNER JOIN
  • AS 等

管道(Pipeline)步骤(Stage)

整个聚合运算过程称为管道(Pipeline),它由多个步骤(Stage)组成。 每个管道:

  • 接收一系列文档(原始数据)
  • 每个步骤对这些文档进行一系列运算
  • 结果文档输出到下一步骤

原始数据 --步骤1--> 中间结果 1 --步骤2--> 中间结果 2 --步骤...--> 最终结果

聚合运算的基本格式

pipline = [$stage1, $stage2, ...$stageN]

db.<COLLECTION>.aggregate(
 pipeline,
 { options }
)

定义好每个 stage,做什么样的事情,格式为 json

常见步骤

步骤 作用 SQL 等价运算符
$match 过滤 WHERE
$project 投影 AS
$sort 排序 ORDER BY
$group 分组 GROUP BY
$skip 结果限制 SKIP
$limit 结果限制 LIMIT
$lookup 左外连接 LEFT OUTER JOIN
$unwind 展开数组(较少)(将 Mongo 中的数组,展开变成一行行的数据) N/A
$graphLookup 图搜索(较少) N/A
$facet 分面搜索(较少) N/A
$bucket 分面搜索(较少) N/A

常见步骤中的操作符

$match
  • $eq$gt$gte$lt$lte
  • $and$or$not$in
  • geoWithin¥intersect
$project
  • 选择需要的或排除不需要的字段
  • $map$reduce$filter
  • $range
  • $multiply$divide$substract$add
  • $year$month$dayOfMonth$hour$minute$second
  • ...
$group
  • $sum$avg
  • $push$addToSet
  • $first$last$max$min
  • ...

使用场景

聚合查询可以用于 OLAP 和 OLTP 场景。如:

OLTP OLAP
计算 分析一段时间内的销售额、均值
计算一段时间内的净利润
分析购买人的年龄分布
分析学生成绩分布
统计员工绩效

MQL 与 SQL 对比

对比 1:

SELECT
  FIRST_NAME AS '名',
  LAST_NAME AS '姓',
FROM Users
WHERE GENDER = '男'
SKIP 100
LIMIT 20
db.users.aggregate([
  { $match: { gender: "男" } },
  { $skip: 100 },
  { $limit: 20 },
  {
    $project: {
      名: "$first_name",
      姓: "$last_name",
    },
  },
]);

对比 2

SELECT DEPARTMENT
  COUNT(NULL) AS EMP_QTY
FROM USERS
WHERE GENDER = '女'
GROUP BY DEPARTMENT HAVING
COUNT(*) < 10
db.users.aggregate([
  { $match: { gender: "女" } },
  {
    $group: {
      _id: "$DEPARTMENT",
      emp_qty: { $sum: 1 },
    },
  },
  { $match: { emp_qty: { $lt: 10 } } },
]);

MQL 特有步骤 $unwind

db.students.findOne();

结果

{
  "name": "张三",
  "score": [
    { "subject": "语文", "score": 84 },
    { "subject": "数学", "score": 90 },
    { "subject": "外语", "score": 69 }
  ]
}

unwind

db.students.aggregate([{ $unwind: "$score" }]);

结果

{ "name": "张三", "score": { "subject": "语文", "score": 84 }}
{ "name": "张三", "score": { "subject": "数学", "score": 90 }}
{ "name": "张三", "score": { "subject": "外语", "score": 69 }}

MQL 特有步骤 $bucket

price 0-10 120条
10-20 20条
20-30 30条
30-40 5000条
40- 10条

使用 MQL

db.products.aggregate([
  {
    $bucket: {
      groupBy: "$price",
      boundaries: [0, 10, 20, 30, 40],
      default: "other",
      output: { count: { $sum: 1 } },
    },
  },
]);

MQL 特有步骤 $facet

数据
price 0-10 20条
10-20 20条
other 30条
year 1890-1901 80条
1901-1920 30条
other 40条

使用 MQL

db.prioducts.aggregate([{
  $facet:{
    price:{
      $bucket:{...}
    },
    year:{
      $bucket:{...}
    }
  }
}])