mongo实战2——6-7章 聚合、更新、删除


第六章 聚合

聚合框架是MongoDB的高级查询语言,类似于关系数据库中的groupBy语句。

聚合框架概览

为调用聚合框架就要定义一个管道。聚合管道里的每一步输出做为下一步的输入。每一步都在输入文档执行单个操作并生成输出文档。
聚合管道流程
聚合管道包含下面一个部分

  • $project 指定输出文档里的字段; 类似SQL的select
  • $match 过滤要处理的文档; 类似SQL的where条件
  • $limit 限制传递给下一步文档的数量
  • $skip 跳过一定文档的数量
  • $unwind 扩展数据,为每个数据元素生成一个输出文档
  • $group 根据key分组 ;类似SQL的group by
  • $sort 排序文档
  • $geoNear 选择某个地理位置附近的文档
  • $out 把管道的结构输出到一个集合
  • $redact 控制特定数据的访问

样例:

db.products.aggregate( [ {$match:...},{$group:...},{$sort:...}] )

聚合样例

测试数据

db.student.insert({name:'zhangsan',age:14,class:'cla1',likes:['足球','羽毛球']})
db.student.insert({name:'lisi',age:16,class:'cla1',likes:['足球','篮球']})
db.student.insert({name:'wangwu',age:15,class:'cla2',likes:['羽毛球','篮球']})
db.student.insert({name:'zhaoliu',age:16,class:'cla2',likes:['乒乓球','跑步']})
db.student.insert({name:'tianqi',age:16,class:'cla1',likes:['乒乓球','足球']})

练习

统计每个班级的人数

db.student.aggregate( [ 
					{$group:
						{ _id:'$class' , count:{$sum:1} }
						}
					])
{ "_id" : "cla2", "count" : 2 }
{ "_id" : "cla1", "count" : 3 }
注意:使用$group时,必须指定分组字段名,且分组字段的名字只能是_id ,如果为如果为其它的名字会报错

$group-统计cla1的总人数

db.student.aggregate([
                        {$match:{class:'cla1'}},
                        {$group:{_id:'$class',count:{$sum:1}}}
                        ])
{ "_id" : "cla1", "count" : 3 }
根据$match获取到所有cla1的文档,之后统计cla1的文档个数

$avg-统计每个班级的人数,及年龄的平均值

db.student.aggregate([
                      {$group:{_id:'$class', count:{$sum:1}, avg:{$avg:'$age'}}}
                      ])
{ "_id" : "cla2", "count" : 2, "avg" : 15.5 }
{ "_id" : "cla1", "count" : 3, "avg" : 15.333333333333334 }

$out-将管道结果保存到集合中

db.student.aggregate([                     
				     {$group:{ _id:'$age', sum:{$sum:1} }} ,     
					 {$out:'result'}
				 ])
将聚合结果保存到result集合中

$project 过滤可以传给下一步的字段

db.student.aggregate([     
          		 {$project :{name:1, age:1, _id:0} }
            ])

$unwind 扩展数组-统计每个爱好的人数

db.student.aggregate([
                      {$project:{likes:1}},  只将likes传递到一个步骤,减少数据量优化性能
                     {$unwind:'$likes'}, 将likes数组的每个元素扩展为一个文档
                     {$group:{_id:'$likes',sum:{$sum:1}}} ,根据likes分组,并统计每个元素的个数。
                     {$sort:{sum:-1}}  根据数量排序
                 ])
{ "_id" : "跑步", "sum" : 1 }
{ "_id" : "足球", "sum" : 3 }
{ "_id" : "羽毛球", "sum" : 2 }
{ "_id" : "篮球", "sum" : 2 }
{ "_id" : "乒乓球", "sum" : 2 }

$group函数

  • $addToSet 求组内所有值的数组,去重
  • $ first 求组里的第一个值,只有前缀$sort才有意义
  • $ last 求组里的最后一个值,只有前缀$sort才有意义
  • $max 求组内字段的最大值
  • $min 求组内字段的最小值
  • $avg 求组内 字段的平均值
  • $push 求组内所有值的数组,不去重
  • $sum 求组内所有值的和
db.student.aggregate([
               {$group:
                      {_id:'$class',
                       avg_age:{$avg:'$age'},   均值
                       max_age:{$max:'$age'}, 最大值
                       min_age:{$min:'$age'}, 最小值
                       sum:{$sum:1},   和
                       first_age:{$first:'$age'}, 第一个值
                       last_age:{$last:'$age'}, 最后一个值
                       set_age:{$addToSet:'$age'}, 所有年龄 去重
                       push_age:{$push:'$age'} 所有年龄  不去重
                        }
                }
            ]).pretty()
结果
{            	
    "_id" : "cla2",
	"avg_age" : 15.5,
	"max_age" : 16,
	"min_age" : 15,
	"sum" : 2,
	"first_age" : 15,
	"last_age" : 16,
	"set_age" : [
		16,
		15
	],
	"push_age" : [
		15,
		16
	]
}
{
	"_id" : "cla1",
	"avg_age" : 15.333333333333334,
	"max_age" : 16,
	"min_age" : 14,
	"sum" : 3,
	"first_age" : 14,
	"last_age" : 16,
	"set_age" : [
		16,
		14
	],
	"push_age" : [
		14,
		16,
		16
	]
}

文档重塑

mongodb的聚合管道包含许多可以用来重塑文档的函数,因此可以在原有的文档中添加新的字段。通常会与$project一起使用这些函数。
除了重命名或者重新构建文档字段外,也可以使用不同的重塑函数来创建新的字段。重塑函数根据处理类型的不同进行分组:
字符串、算数运算、日期、逻辑、集合、其他类型。

字符串函数

mongo字符串函数

db.student.aggregate([
                   {$project:
                             {name:{$concat:['$name','_A','_B']},
                              subName:{$substr:['$name',0,3]},
                              upperName:{$toUpper:'$name'}
                              }
                   }
       ])
{ "_id" : ObjectId("5d1fea22b2b6226b3223e4ba"), "name" : "zhangsan_A_B", "subName" : "zha", "upperName" : "ZHANGSAN" }
{ "_id" : ObjectId("5d1fea35b2b6226b3223e4bb"), "name" : "lisi_A_B", "subName" : "lis", "upperName" : "LISI" }
{ "_id" : ObjectId("5d1fea4db2b6226b3223e4bc"), "name" : "wangwu_A_B", "subName" : "wan", "upperName" : "WANGWU" }
{ "_id" : ObjectId("5d1fea7bb2b6226b3223e4bd"), "name" : "zhaoliu_A_B", "subName" : "zha", "upperName" : "ZHAOLIU" }
{ "_id" : ObjectId("5d1fea8bb2b6226b3223e4be"), "name" : "tianqi_A_B", "subName" : "tia", "upperName" : "TIANQI" }

算数运算符

mongodb,算数运算符

日期函数

mongodb,日期函数

逻辑运算符

mongodb,逻辑运算符

集合运算符

mongodb,集合运算符

其他运算符

mongodb,其他运算符

聚合函数性能

聚合管道性能的关键点

  1. 尽早在管道里尝试减少文档的数量和大小 ;减少数量$match,减少字段 $project
  2. 索引只能用于 m a t c h 和 match和 matchsort操作,而且可以大大加速查询
  3. 使用 m a t c h 和 match和 matchsort 之外的操作符不能使用索引
  4. 如果是sharding部署模式,则 m a t c h 和 match和 matchproject会在单独的分片上执行。

聚合管道的选项参数

完整的聚合函数的参数

db.collection.aggregate(pipeline,additionalOptions);

pipe为之前的聚合管道操作数据;additionalOptions 为选项参数;如下

{explain:true, allowDiskUse:true, cursor:{batchSize:n}}
  • explain 显示管道的执行计划
  • allowDiskUse 使用磁盘存储数据
    当我们处理超大型的集合时,管道返回了超多Mongo RAM 内存限制的100MB数据,就会报错,可以通过指定allowDiskUse:true解决问题
  • cursor 指定初始批处理的大小

第7章 更新、原子操作、删除

单个文档更新

修改zhangsan的年龄和爱好
db.student.update(
                  {name:'zhangsan'},
                  { $set:{age:20}, $addToSet:{likes:'aa'}}
           )

多个文档更新

  {multi:true})
  每个同学都喜欢上网
db.student.update(
                  {},
                  {$addToSet:{likes:'上网'}},
                  {multi:true})
WriteResult({ "nMatched" : 5, "nUpserted" : 0, "nModified" : 5 })

upserts 文档存在时更新,不存在时插入

db.student.update(
                  {name:'jingba'},
                  {$set:{age:8}},
                  {upsert:true})

更新操作符

  • $inc 可以增加或减少一个数值
  • $unset 删除文档的字段
  • $rename 修改文档key的名字
 db.student.update(
                   {name:'jingba'},
                   { $inc:{age:1},
                     $rename:{'name':'rename'}
                   })

$setOnInsert 在upsert中,如果数据不存在,可以设置字段的初始值。

db.student.update(           
              {name:'hejiu'},            
              { $inc:{age:1},   
                $setOnInsert:{name:'hejiu',likes:['aa','bb']}   },  #name:'hejiu' 不存在时,设置初始值
               {upsert:true})
数组操作符

$push $each

在数组中插入一个元素
db.student.update(
                  {name:'zhangsan'},
                  {$push:{likes:'bb'}})
                  
    在数组中插入多个元素
db.student.update(
                    {name:'zhangsan'},
                    {$push:{likes:{$each:['c','d','e']}}})

$slice 限制数组元素的个数

db.temp.insert({_id:326,tmp:[92,93,94]});
db.temp.update(
                      {_id:326},
                      {$push:
                             {'tmp':{
                                     $each:[95,96],
                                     $slice:-4}
                              }
                      })

{ "_id" : 326, "tmp" : [ 93, 94, 95, 96 ] }
  $slice:-4 从数组的尾部开始保留4个元素
  $slice:4  从数组的头部开始保留4个元素

$sort对结合元素进行排序

db.temps.insert({_id:300,temps:[{day:6,temp:90},{day:5,temp:95}]});
db.temps.update(
                       {},
                       {$push:
                         {temps:{
                              $each:[{day:7,temp:92}],
                              $slice:-2,
                              $sort:{day:1}
                             }
                           }
                      }
 )

$pop 删除元素 通过元素的索引删除

db.temps.update(
...              {},
...            {$pop:{'temps':1}})
1从尾部删除 -1从头部删除

$pull 删除元素 ,通过元素的值删除

findAndModify命令

db.student.findAndModify({
                  query:{name:'zhangsan'},
                  update:{$set:{age:18}}
			});

参数
mongo,findAndModify参数

删除

db.student.remove({name:'zhangsan'})

并发、原子性和隔离

  1. 在mongodb3.0之后支持了文档级别的锁,降低了锁的粒度,提高了并发性能
  2. mongodb的插入、修改和删除都需要占用写锁。
  3. mongo的插入一般效率较高,修改和删除可能会影响整个结合,可能需要很长时间完成,为了提高并发性能,mongo允许这种长时间的操作为其他读写让路。当一个操作屈服时,它会暂停释放锁,后面再重新启动。
  4. 当更新和删除文档时,这种退让机制可能是不期望出现的。我们可以设置$isolated来保持操作独立,避免让路。

更新性能
1.尽量避免使用文档替换方式更新,如果替换后的文档较大,会发生文档移动影响更新性能
2.尽量使用字段替换的方式修改文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值