我还没有问另一个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
扔在我的脸上?很奇怪,有关这个问题的任何好主意吗?
感谢您的建议和更好的智慧。
答案 0 :(得分:4)
发生这种情况的原因是因为map / reduce有时会触发自身,即reduce会因reduce的结果而触发。但是reduce的结果没有count
字段。您必须始终确保map emit和reduce结果具有相同的格式。在文档中阅读更多相关内容。
编辑以下是一个简单的演示如何解决此问题:
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
应该为您提供独特的用户。没有测试它,但它应该工作。
编辑2 虽然它应该很慢,.indexOf
是一个相当昂贵的功能。您可以通过制作两个map / reduce作业来加快速度。首先,您可以像这样映射/缩小集合:
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;
}
这应该快得多。
答案 1 :(得分:3)
来自the docs:
因为对于同一个键可能会多次调用reduce函数,所以reduce函数返回的对象的结构必须与map函数的发射值的结构相同
你不这样做,你的reduce会返回一个total
和unique
而不是count
和user
的对象。您可以将格式更改为代表用户分组的格式,也可以使用finalize function。