MongoDB--聚合框架,管道

MongoDB–聚合框架


使用聚合框架可以对集合中的文档进行变换和组合。可以用多个构件创建一个管道(pipeline,类似一个流),用于对一连串的文档进行处理。这些构件包括:

筛选(filtering)

投射(projecting)

分组(grouping)

排序(sorting)

限制(limiting)

跳过(skipping)

一:管道操作符

准备如下数据:

db.reac.insert([{"title":"a","author":"a1","date":"ag2","count":3},{"title":"tt","author":"a1","date":"afffd2","count":5},{"title":"aas","author":"b","date":"a2g","count":2},{"title":"ffa","author":"c","date":"adg2","count":22},{"title":"afa","author":"c","date":"agd2","count":13},{"title":"aafs","author":"a1","date":"adg2","count":45},{"title":"aee","author":"d","date":"agd2","count":33},{"title":"aef","author":"e","date":"adg2","count":1},{"title":"adg","author":"e","date":"a2sdg","count":63}])

db.reac.insert([{"title":"a","author":"a1","date":"agds2","count":3,"totalPay":0,"count1":4},{"title":"aa","author":"a1","date":"asdg2","count":32,"totalPay":0,"count1":14},{"title":"dsf","author":"a1","date":"acsg2","count":12,"totalPay":0,"count1":3},{"title":"ertf","author":"a1","date":"agccc2","count":11,"totalPay":0,"count1":6}])
1. $match

$match用于对文档集合进行筛选。match可以使用所有的常规查询操作符,但是不能使用地理空间操作符。一般来说match放在管道的前面位置:一是可以快速将不需要的文档过滤掉减小工作量;一方面是如果在投射和分组之前执行match,查询就可以使用索引。

筛选出count大于10的文档
cqsm>db.reac.aggregate({$match:{"count":{"$gt":10}}})
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef23"), "title" : "ffa", "author" : "c", "date" : "adg2", "count" : 22 }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef24"), "title" : "afa", "author" : "c", "date" : "agd2", "count" : 13 }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef25"), "title" : "aafs", "author" : "a1", "date" : "adg2", "count" : 45 }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef26"), "title" : "aee", "author" : "d", "date" : "agd2", "count" : 33 }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef28"), "title" : "adg", "author" : "e", "date" : "a2sdg", "count" : 63 }
2. $project

$project可以从文档中提取字段,加载到内存中,还可以在提取的字段上进行一些操作。

//默认情况下_id会被返回,值取0就不会返回了
cqsm>db.reac.aggregate({$project:{"author":1,"_id":0}})
{ "author" : "a1" }
{ "author" : "a1" }
{ "author" : "b" }
{ "author" : "c" }
{ "author" : "c" }
{ "author" : "a1" }
{ "author" : "d" }
{ "author" : "e" }
{ "author" : "e" }

//对投影的字段在返回结果中重命名,一定要排除_id,所以重命名之后不能使用_id为索引
db.reac.aggregate({"$project":{"userId":"$_id","_id":0}})  //成功
db.reac.aggregate({"$project":{"au":"$author","_id":0}})  

#####2.1 管道表达式

可以通过一些表达式进行组合进行任意深度的嵌套,以便创造复杂的表达式

2.2 数学表达式
操作符语法意义
$add$add:[ex1,ex2…]接受一个或者多个表达式作为参数,将参数相加,可以是常数,如果是文档中的数记住要加$
$substract$substract:[ex1,ex2.]只接受两个参数,第一个参数减去第二个参数
$multiply$multiply[ex1,ex2…]一到多个参数,相乘
$divide$divide[ex1,ex2.]只接受两个参数,用第一个参数除以第二个参数
$mod$mod[ex1,ex2.]只接受两个参数,取模
cqsm>db.reac.aggregate({"$project":{"totalPay":{"$add":["$count1","$count"]}}})
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef20"), "totalPay" : null }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef21"), "totalPay" : null }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef22"), "totalPay" : null }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef23"), "totalPay" : null }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef24"), "totalPay" : null }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef25"), "totalPay" : null }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef26"), "totalPay" : null }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef27"), "totalPay" : null }
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef28"), "totalPay" : null }
{ "_id" : ObjectId("5ddf894fd4733a02ad33ef29"), "totalPay" : 7 }
{ "_id" : ObjectId("5ddf894fd4733a02ad33ef2a"), "totalPay" : 46 }
{ "_id" : ObjectId("5ddf894fd4733a02ad33ef2b"), "totalPay" : 15 }
{ "_id" : ObjectId("5ddf894fd4733a02ad33ef2c"), "totalPay" : 17 }
2.3 日期表达式

日期字段的表达式,只能对日期类型的字段进行日期操作,不能对数值类型的字段进行日期操作。使用方法都是一样的,接受一个日期类型的参数,返回一个数值

  • $year

  • $month

  • $week

  • $dayOfMonth

  • $dayOfWeek

  • $dayOfYear

  • $hour

  • $minute

  • $second

//抽取出字段totalPay,获取当前日期的年份再赋值给totalPay
db.reac.aggregate({$project:{"totalPay":{$year:new Date()}}})
2.4 字符串表达式
操作符和语法含义
$substr:[exp,startOffset,numToReturn]exp是字符串,返回从startOffset开始的numToReturn个字节,是字节
$concat:[exp1,exp2…]多个字符串连接
$toLower:exp返回小写形式
$toUpper:exp返回大写形式
2.5 逻辑表达式

逻辑表达式比较多

  • $cmp:[exp1,exp2] //比较两个参数的大小,返回0/1
  • $strcasecmp:[string1,string2]//比较两个罗马字符组成的字符串的大小
  • gt,gte,lt,lte [exp1,exp2] //返回true或者false
  • $and:[exp1,exp2…] //如果所有的表达式都是true就返回true
  • $or:[exp1,exp2…] //如果任意的表达式都是true就返回true
  • $not:exp //对参数取反
  • $cond:[booleanExpr,trueExpr,falseExpr] //如果booleanExpr为true就返回trueExpr,否则返回falseExpr
  • $ifNull:[expr,expr2] //如果expr是null就返回expr2
2.6 注意项

算数操作符只能接受数值类型参数,日期操作符只能接受日期类型的参数,字符串操作符只能接受字符串类型的参数。必要的时候进行条件判断,避免报错。

2.6 运用

找出autuor是a1并且title是a的文档,将totalPay改为count+count1

cqsm>db.reac.aggregate({$match:{"author":"a1","title":"a"}},{$project:{"totalPay":{$add:["$count","$count1"]},"author":1,"title":1}})
{ "_id" : ObjectId("5ddf7d2dd4733a02ad33ef20"), "title" : "a", "author" : "a1", "totalPay" : null }
{ "_id" : ObjectId("5ddf894fd4733a02ad33ef29"), "title" : "a", "author" : "a1", "totalPay" : 7 }
3. $group

$group可以将文档依据特定字段的不同值进行分组。

//按照title分组
cqsm>db.reac.aggregate({"$group":{"_id":"$title"}})
{ "_id" : "ertf" }
{ "_id" : "dsf" }
{ "_id" : "ffa" }
{ "_id" : "tt" }
{ "_id" : "aas" }
{ "_id" : "aee" }
{ "_id" : "aa" }
{ "_id" : "afa" }
{ "_id" : "a" }
{ "_id" : "aafs" }
{ "_id" : "aef" }
{ "_id" : "adg" }
3.1 分组操作符

分组操作符可以允许对每个分组进行计算。例如sum,它就对计算结果+1.

3.2 算数操作符

**" s u m " : v a l u e ∗ ∗ , 对 分 组 的 每 一 个 文 档 , 将 v a l u e 与 计 算 结 果 相 加 , 注 意 引 用 文 档 中 的 字 段 一 定 要 加 sum":value**,对分组的每一个文档,将value与计算结果相加,注意引用文档中的字段一定要加 sum":valuevalue

cqsm>db.reac.aggregate({$group:{"_id":"$title","totalPay":{"$sum":"$count"}}})
{ "_id" : "ertf", "totalPay" : 11 }
{ "_id" : "dsf", "totalPay" : 12 }
{ "_id" : "ffa", "totalPay" : 22 }
{ "_id" : "tt", "totalPay" : 5 }
{ "_id" : "aas", "totalPay" : 2 }
{ "_id" : "aee", "totalPay" : 33 }
{ "_id" : "aa", "totalPay" : 32 }
{ "_id" : "afa", "totalPay" : 13 }
{ "_id" : "a", "totalPay" : 6 }
{ "_id" : "aafs", "totalPay" : 45 }
{ "_id" : "aef", "totalPay" : 1 }
{ "_id" : "adg", "totalPay" : 63 }

"$avg":value,返回每个分组的平均值

cqsm>db.reac.aggregate({"$group":{"_id":"$author","totalPay":{$avg:"$count"}}})
{ "_id" : "e", "totalPay" : 32 }
{ "_id" : "a1", "totalPay" : 15.857142857142858 }
{ "_id" : "b", "totalPay" : 2 }
{ "_id" : "c", "totalPay" : 17.5 }
{ "_id" : "d", "totalPay" : 33 }
3.3 极值操作符

"$max":expr,返回分组内的最大值

"$min":expr,返回分组内的最小值

db.reac.aggregate({"$group":{"_id":"$author","totalPay":{"$max":"$count"}}})

db.reac.aggregate({"$group":{"_id":"$author","totalPay":{"$min":"$count"}}})

"$first":expr,返回分组的第一个值,如果数据没排序这个操作没有意义

"$lase":expr,返回分组的最后一个值

db.reac.aggregate({"$group":{"_id":"$author","totalPay":{"$last":"$count"}}})
3.4 数组操作符

“$addToSet”:expr,如果数组不包含expr那就将它添加到数组中,正在返回结果集中,每个元素最多只出现一次,而且元素是不确定的。

“$push”:expr ,不管expr是什么值,都将它添加到数组中。返回包含所有值得数组。

4. $unwind

$unwind可以将数组中的每一个值拆分为单独的文档。

//想要得到的定用户的所有评论,只需要评论
db.blog.aggregate({"$project":{"comments":"$comments"}},{"$unwind":"$comments"},{"$mstch":{"commnets.author":"Mark"}})
5. $sort

$sort可以根据任何字段进行排序,与普通查询的语法相同。sort不能流式工作,只能在接受到所有的文档之后才能排序。

//排序使用的字段可以是文档中存在的字段,也可以是投影的时候重命名的文件
cqsm>db.reac.aggregate({$project:{"title":1,"sort":"$count","_id":0}},{"$sort":{"sort":1}})
{ "title" : "aef", "sort" : 1 }
{ "title" : "aas", "sort" : 2 }
{ "title" : "a", "sort" : 3 }
{ "title" : "a", "sort" : 3 }
{ "title" : "tt", "sort" : 5 }
{ "title" : "ertf", "sort" : 11 }
{ "title" : "dsf", "sort" : 12 }
{ "title" : "afa", "sort" : 13 }
{ "title" : "ffa", "sort" : 22 }
{ "title" : "aa", "sort" : 32 }
{ "title" : "aee", "sort" : 33 }
{ "title" : "aafs", "sort" : 45 }
{ "title" : "adg", "sort" : 63 }
6. $limit
db.reac.find().limit(3)  //只返回三条数据

二:注意

  • 尽量在管道的开始阶段执行projiect,group,unwind操作,这样就先将不需要的字段和文档过滤掉;
  • 管道如果不是在原本的集合中使用的数据就不能使用索引;
  • MongoDB不允许单一的聚合操作占用过多的系统内存,如果MongoDB发现某个聚合操作占用了20%以上的内存,就会直接报错;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值