2

我知道我在 MongoDB 中缺少 MapReduce 的一些东西。我正在尝试建立一个标签频率集合,我得到了不同的结果,即使看起来mapreduce功能是“相同的”。

示例文档(忘记值 100、45...我没有使用它们):

{
    ...
    tags: [['Rock', 100], ['Indie Pop', 45], ...]
}

发出一个标量值1

var map = function () {
    if (this.tags) {
        this.tags.forEach(function (tag) {
            emit(tag[0], 1); // Emit just 1
        });
    }
};

var reduce = function (key, vals) { // Vals should be [1, ...]
    return vals.length; // Count the length of the array
};

db.tracks.mapReduce(map, reduce, { out: 'mapreduce_out' });
db.mapreduce_out.find().sort({ value: -1 }).limit(3);

输出是:

{ "_id" : "rubyrigby1", "value" : 9 }
{ "_id" : "Dom", "value" : 7 }
{ "_id" : "Feel Better", "value" : 7 }

发射一个对象{ count: 1 }

var map = function () {
    if (this.tags) {
         this.tags.forEach(function (tag) {
            emit(tag[0], { count: 1 }); // Emit an object
         });
    }
};

var reduce = function (key, vals) { // vals should be [{ count: 1 }, ...]
    var count = 0;

    vals.forEach(function (val) {
        count += val.count; // Accumul
    });

    return { count: count };
};

db.tracks.mapReduce(map, reduce, { out: 'mapreduce_out' });
db.mapreduce_out.find().sort({ 'value.count': -1 }).limit(3);

结果不同,似乎是“正确的”:

{ "_id" : "rock", "value" : { "count" : 9472 } }
{ "_id" : "pop", "value" : { "count" : 7103 } }
{ "_id" : "electronic", "value" : { "count" : 5727 } }

第一种方法有什么问题?

4

1 回答 1

4

考虑一千个带有标签“tagname”的文档的集合:

for (var i = 0; i < 1000; i++) {
    db.collection.insert({tags: [['tagname']]});
}

如果我写一个正确的 mapReduce 我应该得到输出{"_id": "tagname", "count": 1000}。但是如果我使用你的 map 和 reduce 函数,我会得到 101 而不是 1000。

原因是,MongoDB 使用中间结果重复调用您的 reduce 函数,以避免在内存中保留太多的结果。实际上,您可以通过在 reduce 中添加 print 语句来看到这一点:

var reduce = function (key, vals) {
    print(vals);
    return vals.length; // Count the length of the array
};

打印输出出现在服务器日志中。使用前 100 个 1 调用 reduce 函数,它返回 100。到目前为止一切顺利。然后 MongoDB 用第一个 reduce 的输出加上接下来的 100 个 1 再次调用它:

reduce([100, 1, 1, ..., 1]) // 100 plus 100 more 1's

所以现在它返回 101,因为那是数组的长度。但显然它应该返回 200,即数组的总和。因此,要获得正确的结果,请更改您的 reduce 函数:

reduce = function (key, vals) {
    var sum = 0;
    vals.forEach(function(val) { sum += val; });
    return sum;
}
于 2013-05-29T21:05:31.447 回答