MongoDB:Map Reduce期间的E11000重复键错误收集



我想计算数组的所有最后元素。数组包含整数,可能为空。

以下 Map Reduce 示例在大型集合(> 1000 万个条目)上崩溃,并出现重复键错误:

var map = function() {
    if(this.path.length > 0) {
        emit(this.path.slice(-1)[0], 1);
    }
};
var reduce = function(id, values) {
    var sum = 0;
    values.forEach(function(value) {
        sum += value;
    });
    return sum;
};
db.input.mapReduce(map, reduce, {out: 'output'})

蒙戈版本是3.2,带有WT引擎。该示例在较小的集合上运行良好(例如 ~500k 个条目)

完全错误:

2016-02-25T19:20:09.078+0100 E QUERY    [thread1] Error: map reduce failed:{
    "ok" : 0,
    "errmsg" : "E11000 duplicate key error collection: my_db.tmp.mr.input_10 index: _id_ dup key: { : 174.0 }",
    "code" : 11000
} :
_getErrorWithCode@src/mongo/shell/utils.js:23:13
DBCollection.prototype.mapReduce@src/mongo/shell/collection.js:1300:1
@(shell):1:1

mapReduce 示例失败的原因是数组的最后一个元素可以是整数或数组本身。我不确定错误消息试图告诉我什么。

我找到了原因,这要归功于Blakes Seven,他提出了聚合管道。它不仅更快、代码更少,而且还会崩溃并出现清晰易懂的错误:

assert: command failed: {
    "ok" : 0,
    "errmsg" : "insert for $out failed: { connectionId: 599, err: "can't use an array for _id", code: 2, n: 0, ok: 1.0 }",
    "code" : 16996
} : aggregate failed

既然你使用的是MongoDB 3.2,那么mapReduce无论如何都是完全错误的工具,你应该取消.aggregate()

MongoDB 3.2引入了$slice聚合框架,该框架与.slice()基本相同,而且$arrayElemAt更好,后者可以仅将数组的最后一个元素作为单个值返回,就像您想要的那样:

db.input.aggregate([
    { "$match": { "path.0": { "$exists": true } } },
    { "$group": {
        "_id": { "$arrayElemAt": [ "$path", -1 ] },
        "count": { "$sum": 1 }
    }},
    { "$out": "output" }
])

所以这是在数组的最后一个元素上分组($group),并像你想要的那样总结这些元素的每个值的计数。

此外,使用带有$match的初始查询来过滤掉空数组(测试是否存在 0 索引位置意味着它必须具有一些定义的长度)比您使用的暴力代码测试更有效。有争议的是,mapReduce操作将受益于相同的"查询"输入进行过滤。

然后是$out,这是可选的,因为与mapReduce不同的.aggregate()方法可以返回更大的结果集的"游标"。因此,除非您真的希望输出进入另一个集合,否则您甚至可能不需要它。

这里的主要教训是"使用聚合语句"。与mapReduce不同,运算符都使用本机代码,而不是解释JavaScript。结果是它运行得更快,而且比相应的mapReduce快得多


仅供记录,因此,当像这样编写时,您的mapReduce更有效(但不如聚合)

db.input.mapReduce(
    function() {
        emit(this.path.slice(-1)[0],1);
    },
    function(key,values) {
        return Array.sum(values);
    },
    {
        "out": "output",
        "query": { "path.0": { "$exists": true } }
    }
)

此外,为了记录,mapReduce 中的"out"始终覆盖集合,除非您显式设置"merge""reduce"选项。

相关内容

  • 没有找到相关文章

最新更新