【问题标题】:MapReduce function in MongoDB - Grouping document by IDMongoDB 中的 MapReduce 函数 - 按 ID 对文档进行分组
【发布时间】:2015-08-20 14:52:34
【问题描述】:

我正在尝试在 MongoDB 中学习 MapReduce 功能。我不想使用聚合,而是想通过自己使用 MapReduce 函数定义的键对集合中的文档进行分组。

我的收藏很酷:

/* 1 */ { "_id" : ObjectId("55d5e7287e41390ea7e83a55"), “身份证”:“一”, “酷”:“a1”}

/* 2 */ { "_id" : ObjectId("55d5e7287e41390ea7e83a56"), “身份证”:“一”, “酷”:“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”:“一个”, “价值” : { “酷”:[ "a1", “a2” ] } }

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

/* 1 */ { “_id”:“一个”, “价值” : { “酷”:[ "a1", “a2” ] } }

/* 2 */ { “_id”:“b”, “价值” : { “酷”:[ "b1", “b2” ] } }

/* 3 */ { “_id”:“c”, “价值”:“c1”}

/* 4 */ { “_id”:“d”, “价值”:“d1”}

问题是:为什么文档“id”:“a”(“id”:“a”的文档不止一个)和“id”:“c”的文档(只有一个"id":"c") 的一份文件

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

【问题讨论】:

    标签: mongodb mapreduce mongodb-query aggregation-framework


    【解决方案1】:

    在您的学习过程中,您可能错过了mapReduce 上的核心手册页。有一个vital piece 的信息你要么错过了,要么没有阅读和学习:

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

    然后过了一会儿:

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

    所以这基本上意味着因为“reducer”实际上并没有一次处理“所有”唯一键,所以它期望与它给出“输出”相同的“输入”,因为该输出可以是再次反馈到reducer中。

    出于同样的原因,“mapper”需要准确地输出“reducer”输出的预期内容,这也是 reducer“输入”。所以你实际上根本没有“改变”数据结构,而只是“减少”它。

    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 _id 和 value 强制输出的唯一区别是键是反转的,因为 $group 不保证顺序(但通常被视为反向堆栈):

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

    【讨论】:

      【解决方案2】:

      您在 map 函数和 reduce 函数中的返回值必须相同。否则,集合中的单个值将作为您在 map 函数中指定的值返回。这是由于优化导致的,因为在 map 阶段返回单个值的键不会执行 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"}
      )
      

      【讨论】:

      • 这就是答案的一部分。但是现在reducer需要什么?
      • 好吧,我认为 OP 需要解决单值键的 map reduce 函数的特殊行为。 reduce 部分确实不是主要问题的部分,但我还是添加了。
      • 谢谢 cubbuk,我已经按照你的建议修改了 Map 函数,但结果是: /* 1 /{ "_id" : "a", "value" : { "cools " : [ [ "a1" ], [ "a2" ] ] } } / 2 /{ "_id" : "b", "value" : { "cools" : [ [ "b1" ], [ "b2" ] ] } } / 3 /{ "_id" : "c", "value" : [ "c1" ] } / 4 */{ "_id " : "d", "value" : [ "d1" ] } 接下来我该如何使用 reduce 函数?
      • 我改了地图功能,一不小心忘了加酷:function(){emit(this.id, {cools: [this.cool]})}
      猜你喜欢
      • 2019-11-21
      • 1970-01-01
      • 1970-01-01
      • 2017-10-24
      • 2023-01-12
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多