00x1 group
使用group可以执行相对复杂的聚合,先选定分组所依据的键,而后mongoDB就会将集合依据选定键进行分组,然后对每一个分组内的文档进行聚合,以得到结果文档。
1.1 group结构
|
|
Group有传入的命令中共有六个参数,其中三个是JavaScript函数,因此每次查询到匹配的数据,都会被转换为对象传入函数。从运行效率上来说,Group比Aggregate差一大截。
1.2 使用场景
对返回数据最多只包含20000个元素,最多支持20000独立分组。
00x2 aggregate
aggregate是mongoDB中经常提起的“管道”。主要用于处理数据(如求和,统计平均值等),并返回计算后的数据结构。
aggreagte是一个数组,其中包含多个对象(命令),通过遍历Pipleline数组对collection中的数据进行操作。
下面介绍一下aggregate的聚合管道比较常用的几种操作:
2.1 $project
修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
|
|
2.2 $match
用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
2.3 $limit
用来限制MongoDB聚合管道返回的文档数。
2.4 $skip
在聚合管道中跳过指定数量的文档,并返回余下的文档。
2.5 $unwind
将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
2.6 $group
将集合中的文档分组,可用于统计结果。
2.7 $sort
将输入文档排序后输出
2.8 使用场景
应用于常用的聚合操作;对聚合响应性能有一定要求时(索引及组合优化);管道操作在中完成,由于内存有大小限制,处理的数据集大小有限。
00x3 MapReduce
3.1 MapReduce结构
mapreduce是mongoDB中提供的用于数据聚合的一种方式。通过对集合中的各个满足条件的文档进行预处理,整理出想要的数据然后统计得到最终的统计结果。
mapreduce的结构如下:
使用MapReduce主要需要实现两个函数:Map函数和Reduce函数。接下来详细介绍这两个函数。
3.2 Map函数
可以将Map函数理解为分组,调用emit(key,values),遍历collection中所有的记录。其中,emit中的key为分组依据;values为分组后需要保留的数据,为1时则统计该分组的值的个数。
key对应最后结果集中的_id。经过Map函数处理的集合,每条数据中只有”key”和”values”两个字段。
3.3 Reduce函数
Reduce为统计函数,接受Map函数处理后返回的key和values作为参数,将key-values变成key-value,也就是把values数组变成一个个单一的value。当key-values中的values数组过大时,会被再切分成很多个小的key-values,再对这些小的key-values分别执行Reduce,再将多个块的结果组合成一个新的数组,作为Reduce函数的第二个参数,继续Reduce操作。这个类似于多阶的归并排序。
3.4 out和keeptemp
out:
在文档输出时,output是可选的,一般结构为{ “out”: option }。
option可以有以下几个选项。
通常结构为{“out”:”collection name”},如果collection不存在,就新建一个集合。
keeptemp
值只能为true或者false,表明输出到的collection是否是临时的,如果想在连接关闭后任然保留这个集合,则需要指定keeptemp的值为”true”。在使用output的情况下,不必指定keeptemp为true。
3.5 使用场景
聚合要求复杂;大型数据集
00x4 三者比较
group | aggregate | MapReduce | |
---|---|---|---|
是否使用JavaScript引擎 | 是,定制reduce函数 | 是,不能编写自定义函数 | 是,MapReduce函数是用JavaScript编写的 |
返回结果集保存位置 | 内联,结果必须符合BSON文档的限制(当前是16Mb) | 内联,服务器支持的最大文档大小(16Mb),超过时会报错 | 内联、新集合、合并、替换、减少 |
处理数据集大小 | 将不会分组到一个超过10,000个键的结果集 | 操作在内存中完成,有内存大小限制,处理数据集大小有限 | 大型数据集,超过20000的独立分组建议采用MapReduce |
处理性能 | 低于aggregate | 较高,管道可重复使用 | 低于aggregate |
灵活度 | 低于MapReduce | 低于MapRduce | 较高,能使用JavaScript |
00x5 从一个小例子具体分析
为公司的每个用户分配一张卡(有唯一的卡号”_id”);持有该卡的用户可以使用这张卡在不同的超市消费,每个超市都有一个标识码,用”identlist”存放用户消费过的超市标识码;持有该卡的用户名字用”name”表示,默认在该公司中,每个人的姓名都是唯一的,与”_id”一一对应;”eventline”列举用户每次消费购买的物品;”timeline”记录每次消费的时间;”newtimeline”为用户最近一次消费的时间。
示例数据如下:
知道最近有几家超市做促销(数组A),要求(1)获取在A中任意一家或多家超市消费过的卡的持有者;(2)这些卡的最新消费时间;(3)这些卡的累积消费次数;(4)根据最新消费时间/累积消费次数对获取到的这些卡的数据进行排序;对排序后的数据进行分页。
分析:要求中最难的是第一步:遍历数组A,将A中的每一条数据,作为分组依据(可能将原来的一条数据拆分成几条);再对分组后的数据以”_id”进行聚合。
5.1 使用MapReduce实现以上要求。
|
|
5.2 使用aggregate
|
|
以上两个小例子经测试后均能实现要求,这里不做详细解释,需要的请自取~