第六章 – 数据聚合
最后更新于:2022-04-01 00:55:05
## 聚合管道(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`](http://docs.mongodb.org/manual/reference/operator/aggregation/project/#pipe._S_project) (类似于 `find`),不但允许你拿到指定字段,还可以根据现存字段进行创建或计算一个新字段。比如,可以用数学操作,在做平均运算之前,对几个字段进行加法运算,或者你可以用字符串操作创建一个新的字段,用于拼接现有字段。
这只是用聚合所能做到的众多功能中的皮毛, 2.6 的聚合拥有了更强大的力量,比如聚合命令可以返回结果集的游标(我们已经在第一章学过了) 或者可以将结果写到另外一个新集合中,通过 `$out` 管道操作。你可以从 [MongoDB 手册](http://docs.mongodb.org/manual/core/aggregation-pipeline/) 得到关于管道操作和表达式操作更多的例子。
## [](https://github.com/geminiyellow/the-little-mongodb-book/blob/master/zh-cn/mongodb.markdown#mapreduce)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](http://openmymind.net/2011/1/20/Understanding-Map-Reduce/) 和 [MongoDB 手册](http://docs.mongodb.org/manual/core/map-reduce/)。
## [](https://github.com/geminiyellow/the-little-mongodb-book/blob/master/zh-cn/mongodb.markdown#小结-5)小结
在这章中我们介绍了 MongoDB 的 [聚合功能(aggregation capabilities)](http://docs.mongodb.org/manual/aggregation/)。 一旦你理解了聚合管道(Aggregation Pipeline)的构造,它还是相对容易编写的,并且它是一个聚合数据的强有力工具。 MapReduce 更难理解一点,不过它强力无边,就像你用 JavaScript 写的代码一样。