Mongodb MapReduce 介绍、示例及特别说明 - zhouted/zhouted.github.io GitHub Wiki

标签: mongodb mapreduce javascript


介绍

Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。 MongoDB提供的Map-Reduce非常灵活,可以高效的进行大规模数据的统计分析。

语法

格式一

db.collection.mapReduce(
   function() {emit(key,value)},//map 函数
   function(key,values) {return reduced}, //reduce 函数
   <collection>//out collection
)
//第三个参数也可以传入更多选项:
{
    finalize: function(key, reduced){return finalized}
    out: <collection>|{inline:true},
    query: <document>,
    sort: <document>,
    limit: <number>,
}

格式二

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

示例

示例集合person存储各省市居民的姓名、性别等记录

1.{
    "name" : "姓名1",
    "gender" : "男",
    "city" : "城市a",
    "province" : "省份A"
}
2.{
    "name" : "姓名2",
    "gender" : "女",
    "city" : "城市b",
    "province" : "省份B"
}
3...

现用mapReduce统计各省市人口性别比例:

db.person.mapReduce(
    function(){//map
        var key = {province:this.province, city:this.city}
        var value = {total: 1}
        if (this.gender == '男'){
            value.male = 1
        }else if (this.gender == '女'){
            value.female = 1
        }else{
            value.unknown = 1
        }
        emit(key, value)
    },
    function(key, values){//reduce
        var value = {}//累计各性别的数量
        values.forEach(function(item){
            //item里可能是单个值,也可能已经是累计值
            for (var k in item){
                value[k] = (value[k]||0) + item[k]
            }
        })
        return value//reduce返回值不支持数组,若需多值请用对象格式
    },{
        query:{},//指定过滤源数据的查询条件
        sort:{province:1, city:1},//按key排序可减少reduce的次数,加快执行速度
        finalize: function(key, rValue){
            for (var k in rValue){
                if (k !== 'total'){//数量转为比例
                    rValue[k] = rValue[k]/rValue.total
                }
            }
            return rValue
        },
        out:{inline:true},
    }
).find()

执行结果如下:

[
    {
        "_id" : {
            "province" : "省份A",
            "city" : "城市a"
        },
        "value" : {
            "total" : 40.0,
            "male" : 0.425,
            "female" : 0.575
        }
    },
    {
        "_id" : {
            "province" : "省份A",
            "city" : "城市b"
        },
        "value" : {
            "total" : 1.0,
            "male" : 1.0
        }
    },
    {
        "_id" : {
            "province" : "省份A",
            "city" : "城市c"
        },
        "value" : {
            "total" : 150.0,
            "male" : 0.526,
            "female" : 0.473
        }
    },
    ...
]

特别说明

  1. 当key对应的value只有一组时不会触发reduce。所以,map函数emit的value值的格式最好和reduce函数return值一致,避免最终的结果不一致。
  2. 同一个key可能会触发多次reduce,即reduce接收的values中有些item元素可能是已经reduced的,累积时要考虑这种情况(即values包含直接emit的value,也包含reduce返回的value)。
  3. reduce返回值不支持数组,若需多值请用对象格式。
  4. 按key排序可以减少reduce的次数,提高处理效率。
  5. out可以指定输出到一个collection集合,但这样会导致写操作;指定为{inline:true}时mongodb会用runReadCommand代替runCommand——if inline output is specified, we need to apply readPreference on the command as it could be run on a secondary.
⚠️ **GitHub.com Fallback** ⚠️