Map/Reduce给我NaN或错误的结果(由于null)

Map/Reduce gives me NaN or wrong result (due to null)

本文关键字:结果 由于 null 错误 Reduce 给我 NaN Map      更新时间:2023-09-26

我还没有问另一个Map/Reduce问题。

我有一个集合"example",看起来像这样:

{
"userid" : "somehash",
"channel" : "Channel 1"
}

我的Map/Reduce功能如下:

var map = function () {
    emit(this.channel, {user:this.userid, count: 1});
}
var reduce = function (key, values) {
    var result = {total:0, unique:0};
    var temp = [];
    values.forEach(function (value) {
        result.total += value.count;
        if (temp.indexOf(value.user) == -1) {
            temp.push(value.user);
        }
    });
    result.unique += temp.length;
    return result;
}

不幸的是,它给了我一些非常奇怪的结果:

{ "_id" : "Channel 1", "value" : { "total" : NaN, "unique" : 47 } }
{ "_id" : "Channel 2", "value" : { "total" : NaN, "unique" : 12 } }
{ "_id" : "Channel 3", "value" : { "total" : 6, "unique" : 6 } }

似乎value.count解析为null,也似乎"唯一"不是正确的值。我想做的是计算每个通道的所有值,并以这样一种方式计算,即我可以看到每个用户的唯一值。这意味着,此集合中的文档example可能会出现多次。我想知道所有的时代和独特的时代。

我遵循以下指南:http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-ReduceFunction和我不知道为什么我会被null扔到脸上?很奇怪,有什么好主意吗?

谢谢你的建议和智慧。

之所以会发生这种情况,是因为map/reduce有时会在其自身上触发,即reduce是在reduce的结果上触发的。但还原的结果并没有count字段。您必须始终确保贴图发射和减少结果具有相同的格式。请参阅文档中的更多信息。

EDIT以下是如何修复此问题的简单演示:

var map = function () {
    emit(this.channel, { user: [this.userid], count: 1 });
}
var reduce = function (key, values) {
    var result = { user: [], count: 0 };
    values.forEach(function (value) {
        result.count += value.count;
        value.user.forEach(function(usr) {
            if (result.user.indexOf( usr ) == -1) {
                result.user.push( usr );
            }
        });
    });
    return result;
}

现在result.user.length应该为您提供唯一的用户。没有测试,但应该可以。

EDIT 2尽管.indexOf是一个相当昂贵的函数,但它应该很慢。您可以通过制作两个贴图/减少作业来加快速度。首先,你在集合上绘制/减少如下:

var map = function() {
    // make a key unique per channel and userid
    emit( this.channel + '_' + this.userid,
        { count: 1, channel: this.channel }
    );
}
var reduce = function(key, values) {
    var result = { count: 0, channel: null };
    values.forEach(function( value ) {
        result.count += value.count;
        // Don't worry about these substitutions,
        // these values can't change anyway per key.
        result.channel = value.channel;
    });
    return result;
}

现在,这个集合上的count将为您提供一些唯一的条目。为了得到总数,你做了第二次映射/减少的结果如下:

var map = function() {
    // Note the key!!!
    emit( this.value.channel, { count: this.value.count } );
}
var reduce = function(key, values) {
    var result = { count: 0 };
    values.forEach(function( value ) {
        result.count += value.count;
    });
    return result;
}

这应该快得多。

来自文档:

由于reduce函数可能会对同一个键多次调用,因此reduce功能返回的对象的结构必须与map函数的发射值的结构相同

如果不这样做,reduce将返回一个具有totalunique的对象,而不是countuser。您可以将格式更改为表示按用户分组的格式,也可以使用finalize函数。