第六章 - 数据聚合
聚合管道(Aggregation Pipeline)
聚合管道提供了一种方法用于转换整合文档到集合。你可以通过管道来传递文档,就像 Unix 的 "pipe" 一样,将一个命令的输出传递到另第二个,第三个,等等。
最简单的聚合,应该是你在 SQL 中早已熟悉的 group by
操作。我们已经看过 count()
方法,那么假设我们怎么才能知道有多少匹公独角兽,有多少匹母独角兽呢?
db.unicorns.aggregate([{$group:{_id:'$gender',
total: {$sum:1}}}])
在 shell 中,我们有 aggregate
辅助类,用来执行数组的管道操作。对于简单的对某物进行分组计数,我们只需要简单的调用 $group
。这和 SQL 中的 GROUP BY
完全一致,我们用来创建一个新的文档,以 _id
字段表示我们以什么来分组(在这里是以 gender
) ,另外的字段通常被分配为聚合的结果,在这里,我们对匹配某一性别的各文档使用了 $sum
1 。你应该注意到了 _id
字段被分配为 '$gender'
而不是 'gender'
- 字段前面的 '$'
表示,该字段将会被输入的文档中的有同样名字的值所代替,一个占位符。
我们还可以用其他什么管道操作呢?在 $group
之前(之后也很常用)的一个是 $match
- 这和 find
方法完全一样,允许我们获取文档中某个匹配的子集,或者在我们的结果中对文档进行筛选。
db.unicorns.aggregate([{$match: {weight:{$lt:600}}},
{$group: {_id:'$gender', total:{$sum:1},
avgVamp:{$avg:'$vampires'}}},
{$sort:{avgVamp:-1}} ])
这里我们介绍另外一个管道操作 $sort
,作用和你想的完全一致,还有和它一起用的 $skip
和 $limit
。以及用$group
操作 $avg
。
MongoDB 数组非常强大,并且他们不会阻止我们往保存中的数组中写入内容。我们需要可以 "flatten" 他们以便对所有的东西进行计数:
db.unicorns.aggregate([{$unwind:'$loves'},
{$group: {_id:'$loves', total:{$sum:1},
unicorns:{$addToSet:'$name'}}},
{$sort:{total:-1}},
{$limit:1} ])
这里我们可以找出独角兽最喜欢吃的食物,以及拿到独角兽们喜欢吃的食物名单。 $sort
和 $limit
的组合能让你拿到 "top N" 这种查询的结果。
还有另外一个强大的管道操作叫做 $project
(类似于 find
),不但允许你拿到指定字段,还可以根据现存字段进行创建或计算一个新字段。比如,可以用数学操作,在做平均运算之前,对几个字段进行加法运算,或者你可以用字符串操作创建一个新的字段,用于拼接现有字段。
这只是用聚合所能做到的众多功能中的皮毛, 2.6 的聚合拥有了更强大的力量,比如聚合命令可以返回结果集的游标(我们已经在第一章学过了) 或者可以将结果写到另外一个新集合中,通过 $out
管道操作。你可以从 MongoDB 手册 得到关于管道操作和表达式操作更多的例子。
MapReduce
MapReduce 分两步进行数据处理。首先是 map,然后 reduce。在 map 步骤中,转换输入文档和输出一个 key=>value 对(key 和/或 value 可以很复杂)。然后, key/value 对以 key 进行分组,有同样的 key 的 value 会被收入一个数组中。在 reduce 步骤中,获取 key 和该 key 的 value 的数组,生成最终结果。map 和 reduce 方法用 JavaScript 来编写。
在 MongoDB 中我们对一个集合使用 mapReduce
命令。 mapReduce
执行 map 方法, reduce 方法和 output 指令。在我们的 shell 中,我们可以创建输入一个 JavaScript 方法。许多库中,支持字符串方法 (有点丑)。第三个参数设置一个附加参数,比如说我们可以过滤,排序和限制那些我们想要分析的文档。我们也可以提供一个 finalize
方法来处理 reduce
步骤之后的结果。
在你的大多数聚合中,也许无需用到 MapReduce , 但如果需要,你可以读到更多关于它的内容,从 我的 blog 和 MongoDB 手册。
小结
在这章中我们介绍了 MongoDB 的 聚合功能(aggregation capabilities)。 一旦你理解了聚合管道(Aggregation Pipeline)的构造,它还是相对容易编写的,并且它是一个聚合数据的强有力工具。 MapReduce 更难理解一点,不过它强力无边,就像你用 JavaScript 写的代码一样。
更多建议: