MongoDB Aggregation 聚合框架
文章目录
前言
本章主要讨论以下几点
- 介绍 MongoDB Aggregation 聚合框架相关的概念和基础知识
- MQL(MongoDB 脚本语言)与 SQL 进行类比
- MongoDB Compass(推荐工具)介绍
- 结合实际项目功能演示 Spring Data MongoDB
1.MongoDB 聚合框架是什么
MongoDB 聚合框架(Aggregation Framework)是一个计算框架,它可以:
- 作用在一个或几个集合上
- 对集合中的数据进行的一系列运算
- 将这些数据转化为期望的形式
从效果而言,聚合框架相当于 SQL 查询中的
- GROUP BY
- LEFT OUTER JOIN
- AS
2.管道(Pipeline)和步骤(Stage)
整个聚合运算过程称为管道(Pipeline),它是由多个步骤(Stage)组成的,每个管道:
- 接收一系列文档(原始数据)
- 每个步骤对这些文档进行一系列运算
- 结果文档输出给下一个步骤
- 直到输出最终结果
3.聚合运算的基本格式
pipeline = [$stage1,$stage2,$stage3...];
db.<collection_name>.aggregate(
pipeline,
{options}
);
4.步骤
步骤 | 作用 | SQL 等价运算符 |
---|---|---|
$match | 过滤 | where |
$project | 投影 | as |
$sort | 排序 | order by |
$group | 分组 | group by |
$skip/$limit | 结构限制 | skip/limit |
$lookup | 左外连接 | left outer join |
$unwind | 展开数组 | 无 |
$graphLookup | 图搜索 | 无 |
$facet/$bucket | 分面搜索 | 无 |
5.步骤中的运算符
5.1 $match
类似于 where,匹配条件
- $eq/$gt/$gte/$lt/$lte
- $and/$or/$not/$in
- $geoWithin/$intersect
5.2 $project
类似于 as,设置别名
- 选择需求的或者排除不需要的字段
- $map/$reduce/$filter
- $range
- $multiply/$divide/$substract/$add
- $year/$month/$dayOfMonth/$hour/$minute/$second
5.3 $group
类似于 group by 分组
- $sum/$avg
- $push/$addToSet
- $first/$last/$max/$min
6.MQL & SQL 对比
6.1 例1
SQL:
SELECT
NAME AS '名称',
age AS '年龄'
FROM
users
WHERE
gender = '男'
skip 100
LIMIT 20
MQL :
db.users.aggregate([
// 步骤1:匹配性别
{
$match: {
gender: "男"
}
},
// 步骤2:跳过前100
{
$skip: 100
},
// 步骤3:取20个
{
$limit: 20
},
// 步骤4:设置别名
{
$project: {
"名称": "$name",
"年龄": "$age"
}
}
])
6.2 例2
SQL:
SELECT
department,
count(*) AS count
FROM
users
WHERE
gender = '男'
GROUP BY
department
HAVING
count(*)< 10
MQL:
db.users.aggregate([
// 步骤1:条件匹配
{
$match: {
gender: "男"
}
},
// 步骤2:分组求和
{
$group: {
_id: "$department",
count: {
$sum: 1
}
}
},
// 步骤3:根据上一步的 count 条件匹配
{
$match: {
count: {
$lt: 10
}
}
}
])
7.特殊步骤
7.1 $unwind
普通查询:
使用 $unwind 之后,将数组拆分成三条数据显示
7.2 $bucket
按年龄进行分组,原始数据如下:
MQL:
db.getCollection("users").aggregate([{
$bucket: {
groupBy: "$age",
boundaries: [0, 10, 20, 30, 40, 50],
default: "Other",
output: {
output: {
$sum: 1
}
}
}
}])
在 MongoDB Compass(下面会提到这个工具) 中看到的结果如下:
7.3 $facet
在上面的基础上增加一个薪水(salary)字段
MQL:
db.getCollection("users").aggregate([{
$facet: {
age: [{
$bucket: {
groupBy: "$age",
boundaries: [0, 10, 20, 30, 40, 50],
default: "Other",
output: {
output: {$sum: 1}
}
}
}],
salary: [{
$bucket: {
groupBy: "$salary",
boundaries: [0, 5000, 7000, 8000, 10000],
default: "Other",
output: {
output: {$sum: 1}
}
}
}]
}
}])
在 MongoDB Compass (下面会提到这个工具)中看到的结果如下:
8.MongoDB Compass(推荐工具)
官网下载地址:https://www.mongodb.com/products/compass
8.1 基本使用
以 6.2 例2 为例
选择对应的集合 users 和 Aggregations
选择步骤,这里我们选择 $match
每写一个条件,会实时显示当前步骤产生的中间结果
8.2 导出
全部写完成之后,点击导出按钮
左边是 MQL,右边可以选择导出的语言类型
9.Spring Data MongoDB
官网地址:https://docs.spring.io/spring-data/mongodb/docs/3.1.5/reference/html/#reference
搜索 Aggregation 关键字即可找到对应的文档。
下面两个示例是实际项目的代码,进行了一定的处理
9.1 按日期求和
MQL:
db.xxx_log.aggregate(
[{
//步骤1:对 operate_time 时间字段进行截取,设置别名 operate_day
$project: {
"operate_day": {
$substrCP: ['$operate_time', 0, 10]
}
}
}, {
//步骤2:分组
$group: {
_id: "$operate_day",
count: {
$sum: 1
},
operate_day: {
$first: "$operate_day"
}
}
}, {
//步骤3:排序
$sort: {
"operate_day": 1
}
}]
);
Spring 中对应的代码
Aggregation agg = Aggregation
.newAggregation(
// 步骤1:显示字段
project().and("operate_time").substring(0, 10).as("operate_day"),
// 步骤2:分组
group("operate_day").count().as("count").
first("operate_day").as("operateDay"),
// 步骤3:排序
sort(Direction.ASC, "operateDay"));
AggregationResults<XXXLogGroupDay> result = operations
.aggregate(agg, "xxx_log", XXXLogGroupDay.class);
return result.getMappedResults();
XXXLogGroupDay 实体类
@Data
public class XXXLogGroupDay {
private String operateDay;
private Integer count;
}
9.2 多字段分组,查询每天的情况
MQL
db.image_analyze_log.aggregate(
// 步骤1:截取字段,设置部分字段别名
[{$project: {
"operate_day": { $substrCP: ['$operate_time', 0, 10] },
"pass":"$pass",
"failureReason":"$failureReason"
}},
// 步骤2:分组
{$group: {
// 按 operate_day 和 pass 两个属性进行分组
_id: {operate_day:"$operate_day",pass:"$pass"},
operate_day:{$first:"$operate_day"},
// 取第一个值显示
pass:{$first:"$pass"},
// 将字段推送到一个数组中
failureReason:{$push:"$failureReason"},
count: {$sum: 1},
}},
// 步骤3:排序
{$sort: {
"operate_day": 1
}}]
)
Spring 中对应的代码
Aggregation agg = Aggregation
.newAggregation(
// 步骤1:显示字段
project().and("operate_time").substring(0, 10).as("operate_day")
.and("analyze_result.qualityCheckResult.pass").as("pass")
.and("analyze_result.qualityCheckResult.failureReason").as("failureReason"),
// 步骤2:分组
group("operate_day", "pass").count().as("count").
push("failureReason").as("failureReason").
first("pass").as("pass").
first("operate_day").as("operateDay"),
// 步骤3:排序
sort(Direction.ASC, "operateDay"));
AggregationResults<XXXLogGroupDay> result = operations
.aggregate(agg, "xxx_log", XXXLogGroupDay.class);
return result.getMappedResults();
返回结果:
{
"code": 0,
"msg": "成功",
"data": [
{
"operateDay": "2021-03-04",
"pass": "true",
"count": 666,
"failureReason": []
},
{
"operateDay": "2021-03-04",
"pass": "false",
"count": 10,
"failureReason": [
"原因1",
"原因2",
...
]
},
{
"operateDay": "2021-03-05",
"pass": "true",
"count": 909,
"failureReason": []
}
]
}
10.参考
- 《MongoDB高手课》- 唐建法
- Spring 官网