官网上的例子
MongoDB的聚合框架是基于数据处理管道的概念建模的。文档进入一个多阶段管道,该管道将文档转换为聚合的结果。例如:
在这个例子中:
db.orders.aggregate([
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } }
])
- 第一阶段:$match阶段根据状态字段过滤文档,并将状态等于“A”的文档传递给下一阶段。
- 第二阶段:$group阶段根据cust_id字段对文档进行分组,以计算每个惟一cust_id的金额总和。
最基本的管道阶段提供了类似查询和文档转换的过滤器,用于修改输出文档的形式。
其他管道操作提供了按特定字段对文档进行分组和排序的工具,还提供了聚合数组(包括文档数组)内容的工具。此外,管道阶段可以为计算平均值或连接字符串等任务使用操作符。
该管道使用MongoDB中的本地操作提供了高效的数据聚合,是MongoDB中数据聚合的首选方法。
aggregate 类似于 shell 脚本的管道,上条命令的输出等于下条命令的输入。
下面通过实际操作来熟悉 aggregate 。
数据准备
随便填的数据,mongodb版本4.x
订单表
db.orders.insertOne( { order_id: 'T00001', name: '苹果', price: 10, status: '正在运输', list: [1,2]} )
db.orders.insertOne( { order_id: 'T00001', name: '香蕉', price: 8, status: '正在运输', list: [1,2]} )
db.orders.insertOne( { order_id: 'T00002', name: '脐橙', price: 12, status: '已关闭', list: [1,2]} )
db.orders.insertOne( { order_id: 'T00002', name: '葡萄', price: 20, status: '已关闭', list: [1,2]} )
db.orders.insertOne( { order_id: 'T00003', name: '火龙果', price: 10, status: '正在运输', list: [1,2]} )
db.orders.insertOne( { order_id: 'T00003', name: '草莓', price: 15, status: '正在运输', list: [1,2]} )
db.orders.insertOne( { order_id: 'T00003', name: '西瓜', price: 25, status: '正在运输', list: [1,2]} )
用户表
db.user.insertOne( { username: 'zlfan', phone: '12345678941', address: 'xx地址', order_id: 'T00001'} )
db.user.insertOne( { username: 'zlfan', phone: '12345678941', address: 'xx地址', order_id: 'T00002'} )
db.user.insertOne( { username: 'zlfan', phone: '12345678941', address: 'xx地址', order_id: 'T00003'} )
实战
$project
操作符
与 find() 的第二个参数作用相同,投影指定的列,还可以增加、删除、重命名字段
db.orders.aggregate([{ $project: { _id: 0, name: 1, price: 1 } }]);
$match
操作符
用于过滤数据,只有满足条件的文档才能进入下一阶段。$match使用MongoDB的标准查询操作。
如查询价格大于10的文档
db.orders.aggregate([
{ $project: { _id: 0, name: 1, price: 1 } },
{ $match: { price: { $gt: 10 } } },
])
$limit
操作符
用来限制返回的文档数量
db.orders.aggregate([
{ $project: { _id: 0, name: 1, price: 1 } },
{ $limit: 5 }
])
$skip
操作符
跳过指定的文档数返回文档
如查询第二条文档后面的所有文档
db.orders.aggregate([
{ $project: { _id: 0, name: 1, price: 1 } },
{ $skip: 2 },
]);
$sort
操作符
对于一个或多个要排序的字段,设置排序顺序为1或-1,分别指定升序或降序排序
db.orders.aggregate([
{ $project: { _id: 0, name: 1, price: 1 } },
{ $sort: { price: 1 } },
]);
重点:$group
操作符
将集合中的文档分组,可用于统计结果。通过指定的_id
表达式对输入文档进行分组,对于每个不同的分组,输出一个文档。每个输出文档的_id字段包含唯一的按值分组。输出文档还可以包含保存某些累加器表达式值的计算字段。
例:根据订单id分组,并统计出总金额,结果显示订单id和总金额
db.orders.aggregate([
{ $project: { _id: 0, order_id: 1, price: 1 } },
{ $group: { _id: '$order_id', totalPrice: {$sum: '$price'} } },
]);
下表是一些常用聚合操作符
表达式 | 说明 | 关键代码 |
---|---|---|
$sum | 计算总和 | { $group: { _id: '$order_id', sum: {$sum: '$price'} } }, |
$avg | 计算平均值 | { $group: { _id: '$order_id', avg: {$avg: '$price'} } } |
$min | 获取集合中所有文档对应值得最小值 | { $group: { _id: '$order_id', min: {$min: '$price'} } } |
$max | 获取集合中所有文档对应值得最大值 | { $group: { _id: '$order_id', max: {$max: '$price'} } } |
$push | 在结果文档中插入值到一个数组中 | { $group: { _id: '$order_id', goods: { $push: { name: '$name', price: '$price' } } } } |
$addToSet | 在结果文档中插入值到一个数组中,但不创建副本 | { $group: { _id: '$order_id', goods: { $addToSet : { name: '$name', price: '$price' } } } } |
$first | 根据资源文档的排序获取第一个文档数据 | { $group: { _id: "$order_id", firstName: { $first: "$name" } } } |
$last | 根据资源文档的排序获取最后一个文档数据 | { $group: { _id: "$order_id", lastName: { $last: "$name" } } } |
重点:$lookup
操作符
多表关联查询。
对同一数据库的非分片集合进行左外连接
,从以已连接的集合中筛选文档。对于每个输入文档,$lookup
阶段添加一个新的数组字段,它的元素来自连接集合的匹配文档。
例子:订单表左外连接用户表,显示商品名称,价格,用户名,用户电话
db.orders.aggregate([
{
$lookup: {
from: 'user', ## 来自哪个集合(表)
localField: 'order_id', ## 主集合(表)用以连接的字段
foreignField: 'order_id', ## 要连接表的字段
as: 'user' ## user将作为新的数组字段,加入主集合(表)
}
},
{ $project: { _id:0, name: 1, price: 1, 'user.username':1, 'user.phone': 1 } }
]);
更多聚合管道操作符:https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/