您的位置:首页 > 数据库 > Mongodb

MongoDB | 聚合 aggregate

2017-09-16 17:38 330 查看

聚合 aggregate

mongoDB官网

mongoDB中文网

聚合(aggregate)主要用于计算数据,类似sql中的sum()、avg()
语法
db.集合名称.aggregate([{管道:{表达式}}])


管道

管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的输入
ps -aux | grep mongo


在mongodb中,管道具有同样的作用,文档处理完毕后,通过管道进行下一次处理,管道操作是可以重复的。
常用管道
$group:将集合中的文档分组,可用于统计结果
$match:过滤数据,只输出符合条件的文档
$project:修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
$sort:将输入文档排序后输出
$limit:限制聚合管道返回的文档数
$skip:跳过指定数量的文档,并返回余下的文档
$unwind:将数组类型的字段进行拆分

表达式

处理输入文档并输出
表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

语法
#列名要加$
表达式:'$列名'


常用表达式
$sum:计算总和,$sum:1同count表示计数
$avg:计算平均值
$min:获取最小值
$max:获取最大值
$push:在结果文档中插入值到一个数组中
$first:根据资源文档的排序获取第一个文档数据
$last:根据资源文档的排序获取最后一个文档数据

示例数据

> db.stu.find()
{ "_id" : ObjectId("59b8ed20fdefef4e475a942f"), "name" : "立夏", "gender" : 0, "age" : 25 }
{ "_id" : ObjectId("59b8ed4efdefef4e475a9430"), "name" : "王子", "gender" : 1, "age" : 20 }
{ "_id" : ObjectId("59b8eda8fdefef4e475a9431"), "name" : "上官玉", "gender" : 0, "age" : 20 }
{ "_id" : ObjectId("59b8ededfdefef4e475a9432"), "name" : "于萌", "gender" : 0, "age" : 15 }
{ "_id" : ObjectId("59b8fc98fdefef4e475a9433"), "name" : "夏至", "gender" : 1, "age" : 27 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9434"), "name" : 0, "gender" : 0, "age" : 21 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9435"), "name" : 2, "gender" : 1, "age" : 22 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9436"), "name" : 3, "gender" : 0, "age" : 23 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9437"), "name" : 4, "gender" : 1, "age" : 25 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9438"), "name" : 5, "gender" : 0, "age" : 30 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9439"), "name" : 6 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a943a"), "name" : 7 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a943b"), "name" : 8 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a943c"), "name" : 9 }
{ "_id" : ObjectId("59b93a99fdefef4e475a943d"), "name" : 1 }
{ "_id" : ObjectId("59ba38110686052694339d66"), "name" : "123" }
{ "_id" : ObjectId("59bb76d898a8f12a368878ef"), "gender" : 1, "age" : 20, "name" : "heey" }

$group

将集合中的文档分组,可用于统计结果
_id表示分组的依据,使用某个字段的格式为  '$字段'
$sum:1 表示count计数

例:男生、女生的总人数
> db.stu.aggregate([
{$group:{
_id:'$gender',
count:{$sum:1}}
}
])
#结果
{ "_id" : null, "count" : 6 }
{ "_id" : 1, "count" : 5 }
{ "_id" : 0, "count" : 6 }


例:男生、女生的总年龄
> db.stu.aggregate([
{$group:{
_id:'$gender',
sumage:{$sum:'$age'}}
}
])
#结果
{ "_id" : null, "sumage" : 0 }
{ "_id" : 1, "sumage" : 114 }
{ "_id" : 0, "sumage" : 134 }


例:男生、女生的平均年龄
> db.stu.aggregate([
{$group:{
_id:'$gender',
avg_age:{$avg:'$age'}}
}
])
#结果
{ "_id" : null, "avg_age" : null }
{ "_id" : 1, "avg_age" : 22.8 }
{ "_id" : 0, "avg_age" : 22.333333333333332 }


例:男生、女生的最小年龄
> db.stu.aggregate([
{$group:{
_id:'$gender',
min_age:{$min:'$age'}}
}
])
#结果
{ "_id" : null, "min_age" : null }
{ "_id" : 1, "min_age" : 20 }
{ "_id" : 0, "min_age" : 15 }


Group by null

将集合中所有文档分为一组
例:求学生总人数、平均年龄
> db.stu.aggregate([
{$group:{
_id:null,
count:{$sum:1},
avg_age:{$avg:'$age'}}
}
])
#结果
{ "_id" : null, "count" : 17, "avg_age" : 22.545454545454547 }


透视数据 $push

例:统计学生性别及学生姓名
> db.stu.aggregate([
{$group:{
_id:'$gender',
name:{$push:'$name'}}
}
])
#结果
{ "_id" : null, "name" : [ 6, 7, 8, 9, 1, "123" ] }
{ "_id" : 1, "name" : [ "王子", "夏至", 2, 4, "heey" ] }
{ "_id" : 0, "name" : [ "立夏", "上官玉", "于萌", 0, 3, 5 ] }


使用$$ROOT可以将文档所有内容加入到结果集的数组中,代码如下
> db.stu.aggregate([
{
$group:{
_id:'$gender',
name:{$push:'$$ROOT'}}
}
]).pretty()
#结果
{
"_id" : null,
"name" : [
{
"_id" : ObjectId("59b8fdd2fdefef4e475a9439"),
"name" : 6
},
{
"_id" : ObjectId("59b8fdd2fdefef4e475a943a"),
"name" : 7
},
......
]
}
{
"_id" : 1,
"name" : [
{
"_id" : ObjectId("59b8ed4efdefef4e475a9430"),
"name" : "王子",
"gender" : 1,
"age" : 20
},
{
"_id" : ObjectId("59b8fc98fdefef4e475a9433"),
"name" : "夏至",
"gender" : 1,
"age" : 27
},
......
]
}
{
"_id" : 0,
"name" : [
{
"_id" : ObjectId("59b8ed20fdefef4e475a942f"),
"name" : "立夏",
"gender" : 0,
"age" : 25
},
{
"_id" : ObjectId("59b8ededfdefef4e475a9432"),
"name" : "于萌",
"gender" : 0,
"age" : 15
},
......
]
}


$match

用于过滤数据,只输出符合条件的文档
使用MongoDB的标准查询操作
例:查询年龄大于20的学生
> db.stu.aggregate([
{$match:{
age:{$gt:20}}
}
])
#结果
{ "_id" : ObjectId("59b8ed20fdefef4e475a942f"), "name" : "立夏", "gender" : 0, "age" : 25 }
{ "_id" : ObjectId("59b8fc98fdefef4e475a9433"), "name" : "夏至", "gender" : 1, "age" : 27 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9434"), "name" : 0, "gender" : 0, "age" : 21 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9435"), "name" : 2, "gender" : 1, "age" : 22 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9436"), "name" : 3, "gender" : 0, "age" : 23 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9437"), "name" : 4, "gender" : 1, "age" : 25 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9438"), "name" : 5, "gender" : 0, "age" : 30 }


例:查询年龄大于20的男生、女生人数
> db.stu.aggregate([
{$match:{age:{$gt:20}}},
{$group:{_id:'$gender',count:{$sum:1}}}
])
#结果
{ "_id" : 1, "count" : 3 }
{ "_id" : 0, "count" : 4 }


$project

修改输入
4000
文档的结构,如重命名、增加、删除字段、创建计算结果
例:查询学生的姓名、年龄,_id是默认显示
> db.stu.aggregate([
{$project:{
_id:0,
name:1,
age:1}
}
])
#结果
{ "name" : "立夏", "age" : 25 }
{ "name" : "王子", "age" : 20 }
{ "name" : "上官玉", "age" : 20 }
{ "name" : "于萌", "age" : 15 }
{ "name" : "夏至", "age" : 27 }
{ "name" : 0, "age" : 21 }
{ "name" : 2, "age" : 22 }
{ "name" : 3, "age" : 23 }
{ "name" : 4, "age" : 25 }
{ "name" : 5, "age" : 30 }
......


例:查询男生、女生人数,输出人数
> db.stu.aggregate([
{$group:{_id:'$gender',count:{$sum:1}}},
{$project:{_id:0}}
])
#结果
{ "count" : 6 }
{ "count" : 5 }
{ "count" : 6 }
> db.stu.aggregate([
{$group:{_id:'$gender',count:{$sum:1}}},
{$project:{_id:0,count:1}}
])
#结果
{ "count" : 6 }
{ "count" : 5 }
{ "count" : 6 }


$sort

将输入文档排序后输出
1:升序     -1:降序
例:查询学生信息,按年龄升序
> db.stu.aggregate([
{$sort:{age:1}}
])
#结果
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9439"), "name" : 6 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a943a"), "name" : 7 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a943b"), "name" : 8 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a943c"), "name" : 9 }
{ "_id" : ObjectId("59b93a99fdefef4e475a943d"), "name" : 1 }
{ "_id" : ObjectId("59ba38110686052694339d66"), "name" : "123" }
{ "_id" : ObjectId("59b8ededfdefef4e475a9432"), "name" : "于萌", "gender" : 0, "age" : 15 }
{ "_id" : ObjectId("59b8ed4efdefef4e475a9430"), "name" : "王子", "gender" : 1, "age" : 20 }
{ "_id" : ObjectId("59b8eda8fdefef4e475a9431"), "name" : "上官玉", "gender" : 0, "age" : 20 }
{ "_id" : ObjectId("59bb76d898a8f12a368878ef"), "gender" : 1, "age" : 20, "name" : "heey" }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9434"), "name" : 0, "gender" : 0, "age" : 21 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9435"), "name" : 2, "gender" : 1, "age" : 22 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9436"), "name" : 3, "gender" : 0, "age" : 23 }
{ "_id" : ObjectId("59b8ed20fdefef4e475a942f"), "name" : "立夏", "gender" : 0, "age" : 25 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9437"), "name" : 4, "gender" : 1, "age" : 25 }
{ "_id" : ObjectId("59b8fc98fdefef4e475a9433"), "name" : "夏至", "gender" : 1, "age" : 27 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9438"), "name" : 5, "gender" : 0, "age" : 30 }


例:查询男生、女生人数,按人数降序
> db.stu.aggregate([
{$group:{_id:'$gender',count:{$sum:1}}},
{$sort:{count:-1}}
])
#结果
{ "_id" : null, "count" : 6 }
{ "_id" : 0, "count" : 6 }
{ "_id" : 1, "count" : 5 }


$limit

限制聚合管道返回的文档数
例:查询2条学生信息
> db.stu.aggregate([
{$limit:2}
])
#结果
{ "_id" : ObjectId("59b8ed20fdefef4e475a942f"), "name" : "立夏", "gender" : 0, "age" : 25 }
{ "_id" : ObjectId("59b8ed4efdefef4e475a9430"), "name" : "王子", "gender" : 1, "age" : 20 }


$skip

跳过指定数量的文档,并返回余下的文档
例:查询从第3条开始的学生信息
> db.stu.aggregate([
{$skip:3}
])
#结果
{ "_id" : ObjectId("59b8ededfdefef4e475a9432"), "name" : "于萌", "gender" : 0, "age" : 15 }
{ "_id" : ObjectId("59b8fc98fdefef4e475a9433"), "name" : "夏至", "gender" : 1, "age" : 27 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9434"), "name" : 0, "gender" : 0, "age" : 21 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9435"), "name" : 2, "gender" : 1, "age" : 22 }
......


例:查询从第2条开始的5条学生信息
> db.stu.aggregate([
{$skip:1},
{$limit:5}
])
#结果
{ "_id" : ObjectId("59b8ed4efdefef4e475a9430"), "name" : "王子", "gender" : 1, "age" : 20 }
{ "_id" : ObjectId("59b8eda8fdefef4e475a9431"), "name" : "上官玉", "gender" : 0, "age" : 20 }
{ "_id" : ObjectId("59b8ededfdefef4e475a9432"), "name" : "于萌", "gender" : 0, "age" : 15 }
{ "_id" : ObjectId("59b8fc98fdefef4e475a9433"), "name" : "夏至", "gender" : 1, "age" : 27 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9434"), "name" : 0, "gender" : 0, "age" : 21 }


例:统计男生、女生人数,按人数升序,取第二条数据
> db.stu.aggregate([
{$group:{_id:'$gender',count:{$sum:1}}},
{$sort:{count:1}}
])
#结果
{ "_id" : 1, "count" : 5 }
{ "_id" : null, "count" : 6 }
{ "_id" : 0, "count" : 6 }
> db.stu.aggregate([
{$group:{_id:'$gender',count:{$sum:1}}},
{$sort:{count:1}},
{$skip:1},
{$limit:2}
])
#结果
{ "_id" : null, "count" : 6 }
{ "_id" : 0, "count" : 6 }


注意顺序:先写skip,再写limit
使用sort与skip和limit合用时,结果有时会错乱

> db.stu.aggregate([{$sort:{age:1}},{$skip:2},{$limit:3}])
#正确结果应该是 name:8 9 1
{ "_id" : ObjectId("59b93a99fdefef4e475a943d"), "name" : 1 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a943c"), "name" : 9 }
{ "_id" : ObjectId("59b8fdd2fdefef4e475a9439"), "name" : 6 }


$unwind

将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值

语法1

对某字段值进行拆分
db.集合名称.aggregate([{$unwind:'$字段名称'}])


构造数据

db.t2.insert({_id:1,item:'t-shirt',size:['S','M','L','XL']})
> db.t1.find()
{ "_id" : 1, "item" : "t-shirt", "size" : [ "S", "M", "L" ,"XL"] }


查询

> db.t2.aggregate({$unwind:'$size'})
{ "_id" : 1, "item" : "t-shirt", "size" : "S" }
{ "_id" : 1, "item" : "t-shirt", "size" : "M" }
{ "_id" : 1, "item" : "t-shirt", "size" : "L" }
{ "_id" : 1, "item" : "t-shirt", "size" : "XL" }


语法2

对某字段值进行拆分
处理空数组、非数组、无字段、null情况
db.inventory.aggregate([{
$unwind:{
path:'$字段名称',
preserveNullAndEmptyArrays:#防止数据丢失
}
}])


构造数据
db.t3.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 }
])


使用语法1查询
> db.t3.aggregate([{$unwind:'$size'}])
{ "_id" : 1, "item" : "a", "size" : "S" }
{ "_id" : 1, "item" : "a", "size" : "M" }
{ "_id" : 1, "item" : "a", "size" : "L" }
{ "_id" : 3, "item" : "c", "size" : "M" }


查看查询结果,发现对于空数组、无字段、null的文档,都被丢弃了
问:如何能不丢弃呢?
答:使用语法2查询
> db.t3.aggregate([{$unwind:{path:'$sizes',preserveNullAndEmptyArrays:true}}])
{ "_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 }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: