AGGREGATION 关联

什么是AGGREGATION?

NoSQL是K/V型数据库,本身不支持关系的,所以为了支持关系,就需要提供关联查询,这就是AGGREGATION产生的必然原因。

linux里的pipe:ps -ef|grep node|awk ‘{print $2}’|xargs kill -9

mongoose AGGREGATION

调用Model.aggregate()将会返回aggregate实例,他具有如下这些方法:

append

往aggregate管道里添加一个新的操作,比如新增加一个match匹配方式:

  1. aggregate.append({ $project: { field: 1 }}, { $limit: 2 });
  2. // or pass an array
  3. var pipeline = [{ $match: { daw: 'Logic Audio X' }} ];
  4. aggregate.append(pipeline);

只是一个语法糖的效果,方便统一的api

exec

执行当前的aggregate管道内容,和之前的其他find操作一样,传入一个回调函数,执行这次计算操作,这里不多说。

allowDiskUse

在mongodb 2.6之后加入,告诉mongodb是否需要将本次汇总计算时暂时用硬盘存储。

cursor

批量设置游标大小

group

分组操作,表示对某些字段进行分组,比如我们对department字段进行分组:

  1. aggregate.group({ _id: "$department" });

limit

表示限制返回集合的大小,例如:

  1. aggregate.limit(10);

match

表示汇总计算的筛选条件,比如:

  1. aggregate.match({ department: { $in: [ "sales", "engineering" } } });

就是从以上条件中进行group分组汇总操作

near

查找附近的记录,如下代码:

  1. aggregate.near({
  2. near: [40.724, -73.997],
  3. distanceField: "dist.calculated", // required
  4. maxDistance: 0.008,
  5. query: { type: "public" },
  6. includeLocs: "dist.location",
  7. uniqueDocs: true,
  8. num: 5
  9. });

project

根据project表达式指定输入的字段或者计算的字段,语法如下:

  1. { $project: { <specifications> } }

比如有如下文档:

  1. {
  2. "_id" : 1,
  3. title: "abc123",
  4. isbn: "0001122223334",
  5. author: { last: "zzz", first: "aaa" },
  6. copies: 5
  7. }

指定_id、title,author出现在输出的文档中:

  1. {
  2. $project : {
  3. title : 1 ,
  4. author : 1
  5. }
  6. }

经过操作之后,就将输出

  1. { "_id" : 1, "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }

当然,在之前也可以指定内嵌文档的字段,比如上面可以指定只输出author.last。 也可以通过加入计算操作,生成新的文档,数据库有如下文档:

  1. {
  2. "_id" : 1,
  3. title: "abc123",
  4. isbn: "0001122223334",
  5. author: { last: "zzz", first: "aaa" },
  6. copies: 5
  7. }

进行如下操作

  1. db.books.aggregate(
  2. [
  3. {
  4. $project: {
  5. title: 1,
  6. isbn: {
  7. prefix: { $substr: [ "$isbn", 0, 3 ] },
  8. group: { $substr: [ "$isbn", 3, 2 ] },
  9. publisher: { $substr: [ "$isbn", 5, 4 ] },
  10. title: { $substr: [ "$isbn", 9, 3 ] },
  11. checkDigit: { $substr: [ "$isbn", 12, 1] }
  12. },
  13. lastName: "$author.last",
  14. copiesSold: "$copies"
  15. }
  16. }
  17. ]
  18. )

操作之后的结果如下:

  1. {
  2. "_id" : 1,
  3. "title" : "abc123",
  4. "isbn" : {
  5. "prefix" : "000",
  6. "group" : "11",
  7. "publisher" : "2222",
  8. "title" : "333",
  9. "checkDigit" : "4"
  10. },
  11. "lastName" : "zzz",
  12. "copiesSold" : 5
  13. }

read

当查询汇总操作时的读取偏好,可以从以下这些中设置一个:

  • ReadPreference.PRIMARY,从分布式部署的主库PRIMARY读取,如果主库PRIMARY不能访问则会报异常,默认设置
  • ReadPreference.PRIMARY_PREFERRED,从主库读取,如果主库不能访问,则尝试从SECONDARY读取
  • ReadPreference.SECONDARY,从SECONDARY读取数据,否则报异常
  • ReadPreference.SECONDARY_PREFERRED,从SECONDARY读取,否则从PRIMARY读取
  • ReadPreference.NEAREST,从延迟最小的一个节点读取数据,无论主从

skip

表示跳过多少条记录

  1. aggregate.skip(10);

sort

对计算结果排序

  1. // 下面这些是等价的
  2. aggregate.sort({ field: 'asc', test: -1 });
  3. aggregate.sort('field -test');

unwind

将输入的文档数组解构,看个例子就明白了,有如下文档数据:

  1. { "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] }

进行unwind操作:

  1. db.inventory.aggregate( [ { $unwind : "$sizes" } ] )

输出结果:

  1. { "_id" : 1, "item" : "ABC1", "sizes" : "S" }
  2. { "_id" : 1, "item" : "ABC1", "sizes" : "M" }
  3. { "_id" : 1, "item" : "ABC1", "sizes" : "L" }

Aggregate中的一些操作关键字

  • $and,与操作
  • $or,或操作
  • $not,非操作
  • $setEquals,设置列是否相同,相同返回true

如下数据:

  1. { "_id" : 1, "A" : [ "red", "blue" ], "B" : [ "red", "blue" ] }
  2. { "_id" : 2, "A" : [ "red", "blue" ], "B" : [ "blue", "red", "blue" ] }
  3. { "_id" : 3, "A" : [ "red", "blue" ], "B" : [ "red", "blue", "green" ] }
  4. { "_id" : 4, "A" : [ "red", "blue" ], "B" : [ "green", "red" ] }
  5. { "_id" : 5, "A" : [ "red", "blue" ], "B" : [ ] }
  6. { "_id" : 6, "A" : [ "red", "blue" ], "B" : [ [ "red" ], [ "blue" ] ] }
  7. { "_id" : 7, "A" : [ "red", "blue" ], "B" : [ [ "red", "blue" ] ] }
  8. { "_id" : 8, "A" : [ ], "B" : [ ] }
  9. { "_id" : 9, "A" : [ ], "B" : [ "red" ] }

进行操作:

  1. db.experiments.aggregate(
  2. [
  3. { $project: { A: 1, B: 1, sameElements: { $setEquals: [ "$A", "$B" ] }, _id: 0 } }
  4. ]
  5. )

执行的结果如下:

  1. { "A" : [ "red", "blue" ], "B" : [ "red", "blue" ], "sameElements" : true }
  2. { "A" : [ "red", "blue" ], "B" : [ "blue", "red", "blue" ], "sameElements" : true }
  3. { "A" : [ "red", "blue" ], "B" : [ "red", "blue", "green" ], "sameElements" : false }
  4. { "A" : [ "red", "blue" ], "B" : [ "green", "red" ], "sameElements" : false }
  5. { "A" : [ "red", "blue" ], "B" : [ ], "sameElements" : false }
  6. { "A" : [ "red", "blue" ], "B" : [ [ "red" ], [ "blue" ] ], "sameElements" : false }
  7. { "A" : [ "red", "blue" ], "B" : [ [ "red", "blue" ] ], "sameElements" : false }
  8. { "A" : [ ], "B" : [ ], "sameElements" : true }
  9. { "A" : [ ], "B" : [ "red" ], "sameElements" : false }
  • $setIntersection 找出数组中多个内嵌文档数组中的相同的内容
  • $setUnion 找出数组中多个内嵌文档数组非重复内容
  • $setDifference 找出数组中多个内嵌文档数组中的不同的内容
  • $setIsSubset 判断数组A是不是包含数组B
  • $anyElementTrue 判断文档数组中是否含有一个值是true的
  • $allElementsTrue 判断数组都是是否都为true值
  • $cmp 如果两个值相同,返回0,1或-1表示不同
  • $eq 判断两个值是否相同
  • $gt 第一个值大于第二个值,返回true
  • $gte 第一个值大于等于第二个值,返回true
  • $lt 第一个值小于第二个值,返回true
  • $lte 第一个值小于等于第二个值,返回true
  • $ne 第一个值不等于第二个值,返回true

字符串操作:

  • $concat 将字符串拼接.
  • $substr 截断字符串
  • $toLower 转小写
  • $toUpper 转大写
  • $strcasecmp 当两个字符串相同返回0,当第一个大于第二个返回1,第二个大于第一个返回-1 文字搜索操作:
    $- meta 好像是计算汇总命中率 数组操作:
  • $size 返回数组的长度

变量操作

$map 将每一条记录都经过map操作, { $map: { input: , as: , in: }

例如有数据:

  1. { _id: 1, quizzes: [ 5, 6, 7 ] }
  2. { _id: 2, quizzes: [ ] }
  3. { _id: 3, quizzes: [ 3, 8, 9 ] }

执行map操作:

  1. db.grades.aggregate(
  2. [
  3. { $project:
  4. { adjustedGrades:
  5. {
  6. $map:
  7. {
  8. input: "$quizzes",
  9. as: "grade",
  10. in: { $add: [ "$$grade", 2 ] }
  11. }
  12. }
  13. }
  14. }
  15. ]
  16. )

输出结果,每个都加了2

  1. { "_id" : 1, "adjustedGrades" : [ 7, 8, 9 ] }
  2. { "_id" : 2, "adjustedGrades" : [ ] }
  3. { "_id" : 3, "adjustedGrades" : [ 5, 10, 11 ] }

$let 可以绑定变量,通过计算输出结果

文字操作:

  • $literal 返回一个解析的值

日期操作:

  • $dayOfYear 返回日期类型那个中的年的日期,1-366
  • $dayOfMonth 返回日期类型中的月中的日期,1-31
  • $dayOfWeek 返回周几1-7
  • $year 返回年
  • $month 返回月1-12
  • $week 返回周1-53
  • $hour 返回小时0-23
  • $minute 返回分钟0-59
  • $second 返回秒0-59
  • $millisecond 返回毫秒0-999

条件表达式:

  • $cond 条件表达式,类似if then else:
  • $cond: { if: { $gte: [ “$qty”, 250 ] }, then: 30, else: 20 }
  • $ifNull 如果条件是null,则返回第三个参数值,如果不是null则返回真实值: description: { $ifNull: [ “$description”, “Unspecified” ] }

累加器:

  • $sum 累加每个列的数值,如果不是数字将忽略
  • $avg 平均所有列的数值
  • $first 获取第一个文档指定列的数值
  • $last 获取最后一个文档指定列的数值
  • $max 获取分组中最大的数值
  • $min 获取分组中最小的数值
  • $push 将指定列数值放入数组
  • $addToSet 将指定列的数值,唯一的值放入数组中

日期统计

  1. Partaker.aggregate({
  2. $group:{
  3. _id:{year:{$year:"$time.join"},month:{$month:"$time.join"},day:{$dayOfMonth:"$time.join"}},
  4. count:{$sum:1}
  5. }
  6. },{
  7. $group:{
  8. _id:{year:"$_id.year",month:"$_id.month"},
  9. dailyusage:{$push:{day:"$_id.day",count:"$count"}}
  10. }
  11. },{
  12. $group:{
  13. _id:{year:"$_id.year"},
  14. monthlyusage:{$push:{month:"$_id.month",dailyusage:"$dailyusage"}}
  15. }
  16. },function(err,partakers){
  17. if(err) console.error(err);
  18. log.info({partakers:partakers},'结果');
  19. });

已有SQL经验,如何快速学习

《SQL to Aggregation Mapping Chart》
http://docs.mongodb.org/manual/reference/sql-aggregation-comparison/

总结