Mongodb MapReduce

1.前言

在上一节中说到了appregate聚合功能,聚合功能已经非常强大了,但是如果你还是无法通过聚合解决问题的话,那么你可能需要使用MapReduce了。

MapReduce提供了Javascript的解释器,所以非常的强大,并且MapReduce可以在多台服务器之间并行的执行,将一个大问题拆分为多个小问题然后分发执行并返回,但是这样的代价就是牺牲了速度,所以才产品发布环境中尽量不要使用MapReduce,因为会很慢很慢。

MapReduce分为两个部分,一个为map一个为reduce,它们两个都是一个纯Js函数,map分别对作用的集合里的每一个文档传入自身函数进行调用返回了一个不同键值对组成的一个列表,就像下面这样:

[
    a: [1,3,4],
    b: [5,3,4]
]

reduce就是对map执行完后的一个列表进行统计,最后返回。Reduce非常像appregate中的group分组。

如果上面的文字你还是听着有点模糊不清,那么下面这张图可以很好的帮助你理解MapReduce,然后看完文章你一定要在执行环境下实验,才能更好的理解强大的MapReduce,因为MapReduce不止存在于Mongodb中,它是由Google解决分布式计算提出的一种概念,Mongodb只是对这个概念的实现,所以MapReduce会出现在任何的一种数据库中。

理解mapreduce

图片原地址:壮壮熊

2.官方语法

官方文档中MapReduce的语法如下:

db.collection.mapReduce(
     <map>,
     <reduce>,
     {
       out: <collection>,
       query: <document>,
       sort: <document>,
       limit: <number>,
       finalize: <function>,
       scope: <document>,
       jsMode: <boolean>,
       verbose: <boolean>,
       bypassDocumentValidation: <boolean>
     }
);

除了上面所说的mapreduce函数之外,Mongodb还增加了一个选项,在这里选项里面我们可以快速的筛选一部分文档,给MapReduce,这么做的愿意是尽可能的让MapReduce执行更快,比如上面的query可以过滤一些不符合的文档、sort排序、limit限制返回的文档数量等,如果你需要筛选文档、排序、限制等操作,尽量在选项中进行操作,因为如果你让1000文档每个文档遍历给Js函数快还是让Mongodb内部查询1000个文档快?当然是后者。

3.Map

map函数的作用是对集合中的每个文档进行调用,map函数是一个纯Js函数,它接受一个this对象,这个this对象代表的就是每个文档,我们可以通过this.xxx来调用文档里面的键值,并在内部调用一个名为emit的函数用于生成列表传给reduce函数,emit函数接受两个参数keyvalue,然后会将相同键名的合成一个键,并把值储存在一个数组当中,通常可以将这个过程称为“洗牌”。

function map(){
    emit(this.num, this.str);
}

通常返回的文档大概为:

[
    5: ['a', 'b'],
    6: ['b', 'd']
]

4.Reduce

reduce函数通过map函数里面的洗牌,接受一个keyvalues,返回的所有文档中只会存在一个唯一的key,如果相同key的值则储存在数组中,在这一步我们可以将key键名相同的值进行操作然后返回一个对象,最后得到结果,这一步一般称为简化。

function reduce(key, values){
    return {values: values}
}

5.实例

mapReduce函数返回的是一个集合的引用,我们可以通过.运算符直接运行Mongodb提供的一些集合操作方法。除此之外我们还可以通过选项中的out设置一个临时的集合,我们可以通过db.xxx来访问这个临时的集合。

下面的例子是通过MapReduce找到含有num5的文档,并返回所有含有num5的文档中str键的值。

通过下面的代码我们插入100个文档

for(var i = 0; i < 100; i++){
  db.test.insert({
    num: Math.floor(Math.random()* 10),
    str: 'a' + Math.floor(Math.random()* 10)
  })
}

然后设计我们的MapReduce函数

function map(){
    emit(this.num, this.str);
}

function reduce(key, values){
    return {values: values}
}

db.test.mapReduce(map, reduce, {
    query: {
        num: 5
    },
    out: 'te'
});

然后查看我们返回的集合查看返回的文档

db.te.find({});

返回的文档为:

{ "_id" : 5, "value" : { "values" : [ "a0", "a8", "a6", "a6", "a6", "a1", "a8", "a4", "a3" ] } }

6.参考

MongoDB权威指南(第2版)

Mongodb Docs

玩转mongodb(八):分布式计算–MapReduce

文档信息