MongoDB中聚合操作是将查询到的数据通过一个流水线,经一次或多次处理后返回结果的操作。比如经过若干条件筛选后,分组统计平均值、求和等等。
Pipeline、Stage
1
| db.collection.aggregate( [ { <stage> }, ... ] )
|
Stage常见操作符
Stage |
作用 |
对应SQL |
$match |
条件过滤 |
WHERE |
$project |
投影 |
AS |
$sort |
排序 |
ORDER BY |
$group |
分组 |
GROUP BY |
$skip |
跳过 |
SKIP |
$limit |
结果限制 |
LIMIT |
$lookup |
左外连接 |
LEFT OUTER JOIN |
$unwind |
展开数组 |
|
$graphLookup |
图搜索 |
|
$bucket |
分桶 |
|
$facet |
多个聚合操作 |
|
所有操作符:MongoDB Manual - Aggregation Pipeline Quick Reference
各Stage对应的常见运算符
$match |
$group |
$project |
$eq/$gt/$gte/$lt/$lte $and/$or/$not/$in $geoWithin/$intersect …… |
$sum/$avg $push/$addToSet $first/$last/$max/$min …… |
$map/$reduce/$filter $range $multiply/$divide/$substract/$add $year/$month/$dayOfMonth/$hour/$minute/$second …… |
实操用例
测试数据结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| > db.orders.findOne() { _id: ObjectId("5dbe7a545368f69de2b4d36e"), user_id: '1000001', lv: 11, sex: 'male', current_money: 100, avatar_list: [ { name: '小强', lv: 100, type: '近战' }, { name: '小刚', lv: 150, type: '法系' } ] }
|
1. 基本功能
一些简单的查询操作使用普通的查询命令与聚合命令的对比
1 2 3 4 5 6 7 8 9 10 11
| > db.collection.find({lv:{$exists:true}}, {_id:0, user_id:1, lv:1}).sort({lv:-1})skip(1).limit(1) {user_id:"110203678995", lv: 70}
> db.collection.aggregate([ { $match: {lv:{$exists:true}} }, { $sort: {lv:-1} }, { $skip: 1 }, { $limit: 1 }, { $project: {_id:0, user_id:1, '等级':'$lv'} } ]) {user_id:"110203678995", 等级: 70}
|
$sort、$skip、$limit顺序
2. 求和
求50级以上玩家总共有多少钱?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| > db.collection.aggregate([ { $match: { lv: { $gt: 50 } } }, { $group: { _id: null, total: { $sum: '$current_money' } } }, { $project: { '总计': '$total', _id: 0 } } ]) {总计: 1000000}
|
3. 求平均
求10级以上的男性玩家的平均等级是多少?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| > db.collection.aggregate([ { $match: { sex: { $in: ['male'] }, lv: { $gt: 10 } } }, { $group: { _id: '$sex', avg_lv: { $avg: '$lv' } } }, { $project: { _id: 0, '性别': '$_id', '平均等级': '$avg_lv' } } ]) {性别: 'male', 平均等级: 9.8395061}, {性别: 'male', 平均等级:8.4914285}
|
4. 数组展开
$unwind,操作符功能示例:
1 2 3 4 5 6
| > db.students.find() {name:'张三', score:[{subject:'语文',score:84}, {subject:'数学',score:90}]}
> db.students.aggregate([ {$unwind:'$score'} ]) {name:'张三', score:{subject:'语文',score:84}} {name:'张三', score:{subject:'数学',score:90}}
|
求所有玩家的游戏人物中,每种等级的角色总数?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| > db.collection.aggregate([ { $unwind: '$avatar_list' }, { $group: { _id: '$avatar_list.lv', count: { $sum: 1 } } } ]) {_id:80, count:3} {_id:56, count:2} ...
|
5. 分组统计
分段统计各个等级的玩家数目?
$bucket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| db.collection.aggregate([ { $bucket: { groupBy: '$lv', boundaries: [0, 20, 40, 60], // [0, 20), [20, 40), [40, 60) default: 'Other', // 不属于上述区间内的 output: { count: { $sum: 1 }, } } } ]) {_id:0, count:802} {_id:20, count:4} {_id:40, count:1} {_id:"Other", count:796}
|
6. 多表关联
$lookup,操作符功能示例:
1 2 3 4 5 6 7 8 9
| > db.product.find() { "_id" : 1, "productname" : "商品1", "price" : 15 } { "_id" : 2, "productname" : "商品2", "price" : 36 }
> db.orders.find() { "_id" : 1, "pid" : 1, "ordername" : "订单1" } { "_id" : 2, "pid" : 2, "ordername" : "订单2" } { "_id" : 3, "pid" : 2, "ordername" : "订单3" } { "_id" : 4, "pid" : 1, "ordername" : "订单4" }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| > db.product.aggregate([ { $lookup: { from: "orders", localField: "_id", foreignField: "pid", as: "inventory_docs" } } ]) { "_id":1, "productname":"商品1", "price":15, "inventory_docs":[ { "_id":1, "pid":1, "ordername":"订单1" }, { "_id":4, "pid":1, "ordername":"订单4" } ] } { "_id":2, "productname":"商品2", "price":36, "inventory_docs":[ { "_id":2, "pid":2, "ordername":"订单2" }, { "_id":3, "pid":2, "ordername":"订单3" } ] }
|
MongoDB Compass
这是官方提供的一个GUI工具,用来做聚合操作非常直观方便。每一个Stage可以单独显示处理后的结果示例,可以方便的调试。
参考资料
极客时间《MongoDB高手课》
MongoDB官方文档 - Aggregation
MongoDB Compass