MongoDB 中的 MapReduce 函数 - 按 ID 对文档进行分组

MapReduce function in MongoDB - Grouping document by ID

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

我的collection酷是:

/* 1 */ { "_id" : ObjectId("55d5e7287e41390ea7e83a55"), "id" : "a", "cool" : "a1" }

/* 2 */ { "_id" : ObjectId("55d5e7287e41390ea7e83a56"), "id" : "a", "cool" : "a2" }

/* 3 */ { "_id" : ObjectId("55d5e7287e41390ea7e83a57"), "id" : "b", "cool" : "b1" }

/* 4 */ { "_id" : ObjectId("55d5e7287e41390ea7e83a58"), "id" : "b", "cool" : "b2" }

/* 5 */ { "_id" : ObjectId("55d5e7287e41390ea7e83a59"), "id" : "c", "cool" : "c1" }

/* 6 */ { "_id" : ObjectId("55d5e7287e41390ea7e83a5a"), "id" : "d", "cool" : "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", "value" : { "cools" : [ "a1", "a2" ] } }

但是返回的collection中有:

/* 1 */ { "_id" : "a", "value" : { "cools" : [ "a1", "a2" ] } }

/* 2 */ { "_id" : "b", "value" : { "cools" : [ "b1", "b2" ] } }

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

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

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

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

您在 map 函数和 reduce 函数中的 return 值需要相同。否则,您集合中的单个值将被 return 编辑为您在地图函数中指定的值。这是由于优化而发生的,因为 reduce 函数不会对映射阶段中 return 单个值的键执行。方法如下:

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"}
)

在您的学习过程中,您可能错过了关于 mapReduce. There is one vital piece 的核心手册页,其中包含您错过或未阅读和学习的信息:

MongoDB can invoke the reduce function more than once for the same key. In this case, the previous output from the reduce function for that key will become one of the input values to the next reduce function invocation for that key.

再过一会儿:

the type of the return object must be identical to the type of the value emitted by the map function.

所以这基本上意味着因为 "reducer" 实际上并没有一次处理所有唯一键的 "all",所以它期望与它给出的 "input" 相同=54=],因为该输出可以再次反馈到减速器中。

出于同样的原因,"mapper" 需要输出与 "reducer" 输出完全相同的预期,这也是减速器 "input"。所以你实际上根本 "change" 数据结构,而只是 "reduce" 它。

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 相反,它使用 "natively coded" 运算符并且不需要 JavaScript 对 运行 的解释。这在很大程度上意味着它 "faster" 并且在构造上通常要简单得多。

这里是和.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 madatory 输出的唯一区别是键被反转,因为 $group 不保证顺序(但通常被观察为反向堆栈):

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