MongoDB | 聚合 aggregate

聚合 aggregate

mongoDB官网

mongoDB中文网

  1. 聚合(aggregate)主要用于计算数据,类似sql中的sum()、avg()
  2. 语法
    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

  • 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
  • 例:查询学生的姓名、年龄,_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 }
    

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值