聚合框架详情 请参考官方文档
https://docs.mongodb.com/manual/aggregation/
MongoDB 聚合框架(Aggregation Framework)是一个计算框架,它可以:
• 作用在一个或几个集合上;
• 对集合中的数据进行的一系列运算;
• 将这些数据转化为期望的形式;
从效果而言,聚合框架相当于 SQL 查询中的:
• GROUP BY
• LEFT OUTER JOIN
• AS等
整个聚合运算过程称为管道(Pipeline),它是由多个步骤(Stage)组成的
每个管道:
• 接受一系列文档(原始数据);
• 每个步骤对这些文档进行一系列运算;
• 结果文档输出给下一个步骤;
pipeline = [$stage1, $stage2, ...$stageN]; db.<COLLECTION>.aggregate( pipeline, { options } );
常见的stage步骤
步骤 | 作用 | SQL等价运算符 |
$match | 过滤 | WHERE |
$project | 投影 | AS |
$sort | 排序 | ORDER BY |
$group | 分组 | GROUP BY |
$skip/$limit | 结果限制 | SKIP/LIMIT |
$lookup | 左外连接 | LEFT OUTER JOIN |
常见步骤中的运算符
$match | $project | $group |
•$eq/$gt/$gte/$lt/$lte •$and/$or/$not/$in •$geoWithin/$intersect •…… |
•选择需要的或排除不需要的字段 •$map/$reduce/$filter •$range •$multiply/$divide/$substract/$add •$year/$month/$dayOfMonth/$hour/$minute/$second •…… |
•$sum/$avg •$push/$addToSet •$first/$last/$max/$min •…… |
其他功能
步骤 | 作用 | SQL等价运算符 |
$unwind | 展开数组 | N/A |
$graphLookup | 图搜索 | N/A |
$facet/$bucket | 分面搜索 | N/A |
比如展开数据就有妙用,可以将内嵌文档展开为多行数据。
使用示例:
条件过滤步骤 $match
投影步骤 $project
展开数据步骤 $unwind
分组聚合步骤 $group
mongoSQl特有步骤$bucket
$bucket 和select 中的case when 比较像。
$Facet组合$bucket
数据样例
计算到目前为止的所有订单的总销售额 db.orders.aggregate([ { $group: { _id: null, total: { $sum: "$total" } } } ]) // 结果: // { "_id" : null, "total" : NumberDecimal("44019609") }
查询2019年第一季度(1月1日~3月31日)已完成订单(completed)的订单总金额和订单总数 db.orders.aggregate([ // 步骤1:匹配条件 { $match: { status: "completed", orderDate: { $gte: ISODate("2019-01-01"), $lt: ISODate("2019-04-01") }
}
}, // 步骤二:聚合订单总金额、总运费、总数量 { $group: { _id: null, total: { $sum: "$total" }, shippingFee: { $sum: "$shippingFee" }, count: { $sum: 1 } }
}, { $project: { // 计算总金额 sum(a+b) as grandTotal grandTotal: { $add: ["$total", "$shippingFee"] }, count: 1, _id: 0 }
} ]) // 结果: // { "count" : 5875, "grandTotal" : NumberDecimal("2636376.00") }
join操作
适用场景:
一般情况下,设计合理的mongoDB的文档嵌套模型可以避免频繁适用join操作,但是还是有些场景,需要设计主表和关联表做join
1. 内嵌文档数据多,可能上万条或者更多。
2.内嵌文档数据量大,可能数 MB 或者超过 16MB,而且并非常用字段。
3.内嵌文档或数组元素会频繁修改,一直增长。
mongoDB join的适用限制
MongoDB 对使用引用的集合之间并无主外键检查
MongoDB 使用聚合框架的 $lookup 来模仿关联查询
$lookup 只支持 left outer join
$lookup 的关联目标(from)不能是分片表
example1:
db.contacts.aggregate([ { $lookup: { from: "groups", localField: "group_ids", foreignField: "group_id", as: "groups" } } ])
//select a.xx from contacts a left join groups b on a.group_ids=b.group_id
//as 是起的别名
example2:
//表1 db.orders.insert([ { "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 }, { "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }, { "_id" : 3 } ])
//表2 db.inventory.insert([ { "_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120 }, { "_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80 }, { "_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60 }, { "_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70 }, { "_id" : 5, "sku": null, description: "Incomplete" }, { "_id" : 6 } ])
//join示例 db.orders.aggregate([ { $lookup: { from: "inventory", localField: "item", foreignField: "sku", as: "inventory_docs" } } ])
返回结果
"_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2, "inventory_docs" : [ { "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 } ] } { "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1, "inventory_docs" : [ { "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 } ] } { "_id" : 3, "inventory_docs" : [ { "_id" : 5, "sku" : null, "description" : "Incomplete" }, { "_id" : 6 } ] }
上面是一对一的关联,还有多对多的关联,详情参考 官方文档:
传送门 https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/