【知识小课堂】 之 聚合函数


我们先来看几个简单的聚合命令:

1.count

> db.foo.count()
4
> db.foo.find({_id:{$gte:2}})
{ "_id" : 2, "x" : 2 }
{ "_id" : 3, "x" : 3 }
> db.foo.find({_id:{$gte:2}}).count()
2
> db.foo.count({_id:{$gte:2}})
2
> 	

这个命令很简单,但我们往往会忽略 最后一种用法,其实count() 函数里面也可以加条件的。


类似于sql 的:  select count(*) from table_name;


2.distinct

> db.order_detail.find()
{ "_id" : 1, "cust_id" : 1, "order_id" : 1, "prod_id" : 1 }
{ "_id" : 2, "cust_id" : 1, "order_id" : 1, "prod_id" : 2 }
{ "_id" : 3, "cust_id" : 2, "order_id" : 2, "prod_id" : 2 }
{ "_id" : 4, "cust_id" : 3, "order_id" : 2, "prod_id" : 1 }
{ "_id" : 5, "cust_id" : 3, "order_id" : 3, "prod_id" : 3 }

示例数据如上。如果我们要查询 有多少种prod_id

db.runCommand({distinct:"order_detail",key:"prod_id"})
{"values" : [1,2,3],
	"stats" : {
		"n" : 5,
		"nscanned" : 5,
		"nscannedObjects" : 5,
		"timems" : 0,
		"cursor" : "BasicCursor"
	},
	"ok" : 1
}


功能类似于sql : select distinct prod_id from table_name


3.GROUP

我们再来先看示例:

> db.runCommand({group:{ "ns":"order_detail", 
... key:{cust_id:true}, 
... initial:{"qty":0},
... "$reduce":function(doc,prev){
... prev.qty +=1; } 
... } } )
{"retval" : [	{"cust_id" : 1,"qty" : 2},
	{"cust_id" : 2,"qty" : 1},
	{"cust_id" : 3,"qty" : 2}
	],
	"count" : 5,
	"keys" : 3,
	"ok" : 1
}


关键词说明:

ns: 要分组的集合

Key :分组依据的键

Initial:初始化

Reduce: reduce 函数

sort: 排序依据;




使用GROUP 可以求:

Count   计数;

SUM 

AVG  平均值

MAX  最大值

MIN  最小值

FIRST  第一个值

LAST  最后一个值


如果指定了排序键值,那么使用 $first / $last 性能会比 MAX/ MIN 好。但在使用$FIRST/$ LAST  时,$sort 放在 $FIRST/$ LAST  前面


以下是一个书上的例子:

按日期分组,列出每天最后交易时间及价格

> db.runCommand({"group" : {
... "ns" : "stocks",
... "key" : "day",
... "initial" : {"time" : 0},
... "$reduce" : function(doc, prev) {
...     if (doc.time > prev.time) {
...         prev.price = doc.price;
...         prev.time = doc.time;
...     }
... }}})
db.scores.aggregate(
... {
...     "$sort" : {"score" : 1}
... },
... {
...     "$group" : {
...         "_id" : "$grade", 
...         "lowestScore" : {"$first" : "$score"}, 
...         "highestScore" : {"$last" : "$score"}
...     }
... })

当然,在group 中,你也可以使用  $LIMIT,$SKIP ,同时也可以添加条件进行汇总查询。

$condition  添加查询条件


> db.order_detail.find()
{ "_id" : 1, "cust_id" : 1, "order_dt" : ISODate("2014-04-30T16:00:00Z"), "order_id" : 1, "prod_id" : 1 }
{ "_id" : 2, "cust_id" : 1, "order_dt" : ISODate("2014-08-11T05:45:50.584Z"), "order_id" : 1, "prod_id" : 2 }
{ "_id" : 3, "cust_id" : 2, "order_dt" : ISODate("2014-08-11T05:45:50.584Z"), "order_id" : 2, "prod_id" : 2 }
{ "_id" : 4, "cust_id" : 3, "order_dt" : ISODate("2014-08-11T05:45:50.584Z"), "order_id" : 2, "prod_id" : 1 }
{ "_id" : 5, "cust_id" : 3, "order_dt" : ISODate("2014-08-11T05:45:50.584Z"), "order_id" : 3, "prod_id" : 3 }
 > db.runCommand({group:{ "ns":"order_detail", 
... key:{cust_id:true}, 
... initial:{"qty":0},
... "$reduce":function(doc,prev){
... prev.qty +=1; } ,
... condition:{order_dt:{$gt:new Date('2014,08,01')}}
... } } )
{"retval" : [{"cust_id" : 1,"qty" : 1},
			{"cust_id" : 2,"qty" : 1},
			{"cust_id" : 3,"qty" : 2}
			],
	"count" : 4,
	"keys" : 3,
	"ok" : 1
}
> 

Condition  也可以简写成 cond, 或:query q



4.Aggregate()

还是先看示例:

> db.order_detail.aggregate( {$group:{_id:"$cust_id",count:{$sum:1}}} )
{"result" : [{"_id" : 3,"count" : 2},
	{"_id" : 2,	"count" : 1},
	{"_id" : 1,	"count" : 2}
	],
	"ok" : 1
}
> 

aggregate() 聚合函数功能就强大了:

关键词如下:


{$project:{“author”:1}} 指定要显示的字段名称

{$match:{“state”:”OR”}} 指定查询的条件

{$group:{_id:”$day”}} 指定分组的字段

{$sort:{count:-1}} 指定排序

{$limit:5} 指定返回的数量 ,当然也可以使用 $skip

{unwind:”$comments”} 指定拆分的字段  (独立使用)


4.1 这里主要来讲讲 $project :

在sql 中,我们有 substr(col1,1,10), to_str(create_dt,'yyyy') ,此类的功能,只取字段的某一部分。在MONGODB 中,我们也可以实现。

这就是我们这里要讲的。

a. 指定某些字段要显示: {$project:{"userId":"$_id","_id":0}}

b.字符串处理:

    "$substr":{$substr:[col1,1,10]}  ,类似于 substring(col1,1,10)

    “$concat”:{"$concat":[col1,col2]}, 类似于 col1+col2

     "$toLower":{"$toLower:col1"}     ,类似于 lower(col1)

     "$toUpper":{"$toLower:col1"}


b.日期处理:

      "$year","$month”,"$week","$dayOfMonth","$dayOfweek","$dayOfYear","$hour","$minuse","$second" ,使用这些函数,可以返回你所需要的一个日期字段的部分值。

c.数字计算:

    “$add” + ,"subtract" - ,'$multiply' *,'$divide' /,"$mod" 取余。这些计算操作符,可以把多个字段的计算后返回。

d.逻辑表达式:

    还可以进行逻辑处理,返回一个新的字段值:

   “$cmp”: [expr1,expr2]

        expr1=expr2: 0;

        expr1<expr2: 负数;

        expr1>expr2: 正数;


“$strcasecmp”:[str1,str2]

   区分str1,str2 大小

"$and":[expr1,expr2]

  所有条件为true,返回 true,否则false;

"$or":[expr1,expr2]

  只要有一条件为true,返回 true,否则false;

"$not":expr1

取反


"$cond":[booleanExpr: trueExpr,falseExpr]


 如果 booleanExpr=true,返回trueExpr ,否则返回 falseExpr


"$ifNull":[expr,replacementExpr] 类似于: oracle: nvl()


4.2 多重分组

先看一个官方的示例:

db.zipcodes.aggregate( [
   { $group : { _id : { state : "$state", city : "$city" }, pop : { $sum : "$pop" } } },
   { $group : { _id : "$_id.state", avgCityPop : { $avg : "$pop" } } }
] )


在aggregate 中,可以使用多重组合。也就是使用前一个分组后的数据,再进行一次分组。

比如下面的sql:

 select ip.city,count(distince user_cookie) from website_open_log

group by ip.city

我要统计网站的登录用户的分布。但汇总的数据为有多人人,而不是有多少次。同一个用户可能有多次访问。

下面的代码中,我使用了两种方法,统计结果稍有些不同,但在这里很能说明问题

db.website_open_log.aggregate(
 {$match:{company_id:1}},
 {$group:{_id:"$cookie",
          ip:{$first:"$ip.province"}
	  }
 },
  {$group:{_id:"$ip",
          qty:{$sum:1}
	  }
 },
 {$sort:{qty:-1}},
 {$limit:10}
 )


> db.website_open_log.aggregate({$match:{company_id:1}},
...  {$group:{_id:{"cookie":"$cookie","province":"$ip.province"},
...           qty:{$sum:1}
...   }
...  },
...  {$group:{_id:"$_id.province",
...           qty1:{$sum:1}
...   }
...  },
...  {$sort:{qty1:-1}}
...  );





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值