06-MongoDB聚合aggregate
什么是聚合
- 聚合(aggregate)主要用于计算数据,类似sql中的sum()、avg()
- 语法
db.集合名称.aggregate([{管道:{表达式}}])
管道
当文档处理完毕后,通过管道可以进一步处理
序号 | 管道命令 | 类型 |
1 | $group | 将集合中的文档分组,可用于统计结果 |
2 | $match | 过滤数据,只输出符合条件的文档 |
3 | $project | 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果 |
4 | $sort | 将输入文档排序后输出 |
5 | $limit | 限制聚合管道返回的文档数 |
6 | $skip | 跳过指定数量的文档,并返回余下的文档 |
7 | $unwind | 将数组类型的字段进行拆分 |
表达式
表达式:'$列名'
序号 | 表达式 | 类型 |
1 | sum|计算总和, s u m | 计 算 总 和 , sum:1同count表示计数 | |
2 | $avg | 计算平均值 |
3 | $min | 获取最小值 |
4 | $max | 获取最大值 |
5 | $push | 在结果文档中插入值到一个数组中 |
6 | $first | 根据资源文档的排序获取第一个文档数据 |
7 | $last | 根据资源文档的排序获取最后一个文档数据 |
$Group
将集合中的文档进行分组,用于统计结果,使用某个字段进行分组的格式为‘$Group’
先查出所有学生
/* 1 */
{
"_id" : ObjectId("5a98f8ddf4a43a13865dc78b"),
"name" : "Rps",
"gender" : 1,
"age" : 22
}
/* 2 */
{
"_id" : "20160101",
"name" : "JackMa",
"gender" : 0.0,
"age" : 20
}
/* 3 */
{
"_id" : ObjectId("5a98fd85f4a43a13865dc78c"),
"name" : "hr",
"gender" : 1,
"age" : 42
}
/* 4 */
{
"_id" : ObjectId("5a9e25c4e22f7535dd66aab3"),
"name" : "EricChen",
"gender" : 0.0,
"age" : 20
}
/* 5 */
{
"_id" : ObjectId("5a9e25cbe22f7535dd66aab4"),
"name" : "Make",
"gender" : 1,
"age" : 23
}
根据性别对学生进行分组
db.stu.aggregate([{
$group:{
_id:'$gender',
counter:{$sum:1}
}
}])
/* 1 */
{
"_id" : 1.0,
"counter" : 2.0
}
/* 2 */
{
"_id" : 0.0,
"counter" : 3.0
}
求出学生总人数和平均年龄
db.stu.aggregate([{
$group:{
_id: null,
counter:{$sum:1},
aveAge:{$avg:'$age'}
}
}])
/* 1 */
{
"_id" : null,
"counter" : 5.0,
"aveAge" : 20.0
}
透视数据:
统计学生性别及学生姓名
db.stu.aggregate([
{$group:
{
_id:'$gender',
name:{$push:'$name'}
}
}
])
/* 1 */
{
"_id" : 0.0,
"name" : [
"JackMa",
"EricChen"
]
}
/* 2 */
{
"_id" : 1,
"name" : [
"Rps",
"hr",
"Make"
]
}
使用$$ROOT可以将文档内容加入到结果集的数组中,代码如下
db.stu.aggregate([
{$group:
{
_id:'$gender',
name:{$push:'$$ROOT'}
}
}
])
/* 1 */
{
"_id" : 0.0,
"name" : [
{
"_id" : "20160101",
"name" : "JackMa",
"gender" : 0.0,
"age" : 20
},
{
"_id" : ObjectId("5a9e25c4e22f7535dd66aab3"),
"name" : "EricChen",
"gender" : 0.0,
"age" : 20
}
]
}
/* 2 */
{
"_id" : 1,
"name" : [
{
"_id" : ObjectId("5a98f8ddf4a43a13865dc78b"),
"name" : "Rps",
"gender" : 1,
"age" : 22
},
{
"_id" : ObjectId("5a98fd85f4a43a13865dc78c"),
"name" : "hr",
"gender" : 1,
"age" : 42
},
{
"_id" : ObjectId("5a9e25cbe22f7535dd66aab4"),
"name" : "Make",
"gender" : 1,
"age" : 23
}
]
}
$match
用于过滤数据,只输出符合条件的文档
查询年龄大于20的学生:
db.stu.aggregate([{
$match:{
age:{
$gt:20
}
}
}])
/* 1 */
{
"_id" : ObjectId("5a98f8ddf4a43a13865dc78b"),
"name" : "Rps",
"gender" : 1,
"age" : 22
}
/* 2 */
{
"_id" : ObjectId("5a98fd85f4a43a13865dc78c"),
"name" : "hr",
"gender" : 1,
"age" : 42
}
/* 3 */
{
"_id" : ObjectId("5a9e25cbe22f7535dd66aab4"),
"name" : "Make",
"gender" : 1,
"age" : 23
}
$project
修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
查询学生的姓名、年龄
db.stu.aggregate([{
$project:{
_id:0,
name:1,
age:1
}
}])
/* 1 */
{
"name" : "Rps",
"age" : 22
}
/* 2 */
{
"name" : "JackMa",
"age" : 20
}
/* 3 */
{
"name" : "hr",
"age" : 42
}
/* 4 */
{
"name" : "EricChen",
"age" : 20
}
/* 5 */
{
"name" : "Make",
"age" : 23
}
查询男生、女生人数,输出人数
db.stu.aggregate([
{$group:{_id:'$gender',counter:{$sum:1}}},
{$project:{_id:0,counter:1}}
])
/* 1 */
{
"counter" : 2.0
}
/* 2 */
{
"counter" : 3.0
}
$sort
将输入文档排序后输出
查询学生信息,按年龄升序:
db.stu.aggregate([
{$sort:{age:1}}
])
/* 1 */
{
"_id" : "20160101",
"name" : "JackMa",
"gender" : 0.0,
"age" : 20
}
/* 2 */
{
"_id" : ObjectId("5a9e25c4e22f7535dd66aab3"),
"name" : "EricChen",
"gender" : 0.0,
"age" : 20
}
/* 3 */
{
"_id" : ObjectId("5a98f8ddf4a43a13865dc78b"),
"name" : "Rps",
"gender" : 1,
"age" : 22
}
/* 4 */
{
"_id" : ObjectId("5a9e25cbe22f7535dd66aab4"),
"name" : "Make",
"gender" : 1,
"age" : 23
}
/* 5 */
{
"_id" : ObjectId("5a98fd85f4a43a13865dc78c"),
"name" : "hr",
"gender" : 1,
"age" : 42
}
- 例2:查询男生、女生人数,按人数降序
db.stu.aggregate([
{$group:{_id:'$gender',counter:{$sum:1}}},
{$sort:{counter:-1}}
])
/* 1 */
{
"_id" : 1,
"counter" : 3.0
}
/* 2 */
{
"_id" : 0.0,
"counter" : 2.0
}
$limit
限制聚合管道返回的文档数
db.stu.aggregate([{$limit:2}])
$skip
跳过指定数量的文档,并返回余下的文档
例2:查询从第3条开始的学生信息
db.stu.aggregate([{$skip:2}])
- 例3:统计男生、女生人数,按人数升序,取第二条数据
db.stu.aggregate([
{$group:{_id:'$gender',counter:{$sum:1}}},
{$sort:{counter:1}},
{$skip:1},
{$limit:1}
])
注意顺序:先写skip,再写limit$unwind
$unwind
将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值
语法1:对某字段值进行拆分
db.集合名称.aggregate([{$unwind:'$字段名称'}])
db.stu.aggregate([{$unwind:'$size'}])
/* 1 */
{
"_id" : 1.0,
"item" : "num",
"size" : "S"
}
/* 2 */
{
"_id" : 1.0,
"item" : "num",
"size" : "M"
}
/* 3 */
{
"_id" : 1.0,
"item" : "num",
"size" : "L"
}
语法2:对某字段进行拆分同事处理空数组,非数组,无字段,null情况
db.inventory.aggregate([{
$unwind:{
path:'$字段名称',
preserveNullAndEmptyArrays:<boolean>#防止数据丢失
}
}])
当使用语法1时:发现对于空数组、无字段、null的文档,都被丢弃了
db.my.insert([
{ "_id" : 1, "item" : "a", "size": [ "S", "M", "L"] },
{ "_id" : 2, "item" : "b", "size" : [ ] },
{ "_id" : 3, "item" : "c", "size": "M" },
{ "_id" : 4, "item" : "d" },
{ "_id" : 5, "item" : "e", "size" : null }
])
db.my.aggregate([{$unwind:'$size'}])
/* 1 */
{
"_id" : 1.0,
"item" : "a",
"size" : "S"
}
/* 2 */
{
"_id" : 1.0,
"item" : "a",
"size" : "M"
}
/* 3 */
{
"_id" : 1.0,
"item" : "a",
"size" : "L"
}
/* 4 */
{
"_id" : 3.0,
"item" : "c",
"size" : "M"
}
使用语法2:
db.my.aggregate([
{$unwind:{path:'$sizes',preserveNullAndEmptyArrays:true}}
])
/* 1 */
{
"_id" : 1.0,
"item" : "a",
"size" : [
"S",
"M",
"L"
]
}
/* 2 */
{
"_id" : 2.0,
"item" : "b",
"size" : []
}
/* 3 */
{
"_id" : 3.0,
"item" : "c",
"size" : "M"
}
/* 4 */
{
"_id" : 4.0,
"item" : "d"
}
/* 5 */
{
"_id" : 5.0,
"item" : "e",
"size" : null
}