聚合是 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:{...}
}
}
}])