MongoDB中的MapReduce函数 - 按ID

时间:2015-08-20 14:52:35

标签: mongodb mapreduce mongodb-query aggregation-framework

我试图在MongoDB中学习MapReduce函数。我想使用MapReduce函数按照自己定义的键对文档进行分组,而不是使用聚合。

我的收藏酷是:

  

/ * 1 * / {       " _id" :ObjectId(" 55d5e7287e41390ea7e83a55"),       " ID" :" a",       "凉爽" :" a1" }

     

/ * 2 * / {       " _id" :ObjectId(" 55d5e7287e41390ea7e83a56"),       " ID" :" a",       "凉爽" :" a2" }

     

/ * 3 * / {       " _id" :ObjectId(" 55d5e7287e41390ea7e83a57"),       " ID" :" b",       "凉爽" :" b1" }

     

/ * 4 * / {       " _id" :ObjectId(" 55d5e7287e41390ea7e83a58"),       " ID" :" b",       "凉爽" :" b2" }

     

/ * 5 * / {       " _id" :ObjectId(" 55d5e7287e41390ea7e83a59"),       " ID" :" c",       "凉爽" :" c1" }

     

/ * 6 * / {       " _id" :ObjectId(" 55d5e7287e41390ea7e83a5a"),       " ID" :" d",       "凉爽" :" d1" }

这是我的MapReduce函数:

db.Cool.mapReduce(
    function(){emit(this.id, this.cool)},
    function(key, values){
        var res = [];
        values.forEach(function(v){
            res.push(v);
            });
        return {cools: res};
        },
    {out: "MapReduce"}     
)

我希望得到这样的结果:

  

/ * 1 * / {       " _id" :" a",       "值" :{           "冷却" :[               " A1&#34 ;,               " A2"           ]       }}

但是在返回的集合中,有:

  

/ * 1 * / {       " _id" :" a",       "值" :{           "冷却" :[               " A1&#34 ;,               " A2"           ]       }}

     

/ * 2 * / {       " _id" :" b",       "值" :{           "冷却" :[               " B1&#34 ;,               " B2"           ]       }}

     

/ * 3 * / {       " _id" :" c",       "值" :" c1" }

     

/ * 4 * / {       " _id" :" d",       "值" :" d1" }

问题是:为什么文件与#34; id":" a" (" id":" a")和" id"的文档有多个文件:" c" (只有一个文件" id":" c")

感谢您的任何建议,并抱歉我的英语不好。

2 个答案:

答案 0 :(得分:2)

map函数和reduce函数中的返回值必须相同。否则,您的集合中的单个值将按照您在地图函数中指定的值返回。这是由于优化而发生的,因为对于在映射阶段返回单个值的键,将不执行reduce函数。以下是如何做到这一点:

db.Cool.mapReduce(
    function () {
        emit(this.id, {cools: [this.cool]}) // same data structure as  in your reduce function
    },
    function (key, values) {
        var res = {cools: []}; // same data structure as the value of map phase
        values.forEach(function (v) {
            res.cools = res.cools.concat(v.cools);
        });
        return res;
    },
    {out: "MapReduce"}
)

答案 1 :(得分:2)

在您的学习中,您可能错过了mapReduce上的核心手册页。您错过或未阅读和学习的信息有vital piece个:

  

MongoDB可以为同一个密钥多次调用reduce函数。在这种情况下,该键的reduce函数的先前输出将成为该键的下一个reduce函数调用的输入值之一。

之后有点:

  

返回对象的类型必须与map函数发出的值的类型相同。

所以这基本上意味着因为“reducer”实际上不会同时处理所有唯一键的“全部”,所以它需要相同的“输入”,因为它给出了“输出”,因为输出可以是再次反馈到减速机。

出于同样的原因,“映射器”需要准确输出预期的“reducer”输出,它也是reducer“input”。所以你根本没有“改变”数据结构,而只是“减少”它。

db.Cool.mapReduce(
    function(){emit(this.id, { "cools": [this.cool] })},
    function(key, values){
        var res = [];
        values.forEach(function(cool){
            cool.cools.forEach(function(v) {
                res.push(v);
            });
        });
        return {cools: res};
    },
    {out: "MapReduce"}     
)

现在您将输入作为数组处理,也是输出,然后返回预期结果。

接下来要学习的是,在大多数情况下,mapReduce并不是您想要使用的,而是您应该使用aggregation framework

与mapReduce相反,它使用“本机编码”运算符,不需要运行JavaScript解释。这在很大程度上意味着它“更快”,并且通常在构造上更加简单。

以下是与.aggregate()相同的操作:

db.Cool.aggregate([
    { "$group": {
        "_id": "$id",
        "cools": { "$push": "$cool" }
    }}
])

同样的事情,更少的编码和更快的速度。

输出到您使用$out的另一个集合:

db.Cool.aggregate([
    { "$group": {
        "_id": "$id",
        "cools": { "$push": "$cool" }
    }},
    { "$out": "reduced" }
])

对于记录,这是mapReduce输出:

{ "_id" : "a", "value" : { "cools" : [ "a1", "a2" ] } }
{ "_id" : "b", "value" : { "cools" : [ "b1", "b2" ] } }
{ "_id" : "c", "value" : { "cools" : [ "c1" ] } }
{ "_id" : "d", "value" : { "cools" : [ "d1" ] } }

总产量。与mapReduce _idvalue的唯一区别在于,密钥是反转的,因为$group不保证订单(但通常被视为反向堆栈):< / p>

{ "_id" : "d", "cools" : [ "d1" ] }
{ "_id" : "c", "cools" : [ "c1" ] }
{ "_id" : "b", "cools" : [ "b1", "b2" ] }
{ "_id" : "a", "cools" : [ "a1", "a2" ] }