Ruby on rails Mongoid MapReduce给出递归reduce函数的不规则结果

Ruby on rails Mongoid MapReduce给出递归reduce函数的不规则结果,ruby-on-rails,mongodb,mapreduce,mongoid,Ruby On Rails,Mongodb,Mapreduce,Mongoid,我有一个项目模型,它有一个属性类别。我希望项目计数按类别分组。我为这个功能写了一个MapReduce。它工作得很好。我最近写了一个脚本来创建5000个项目。现在我意识到我的map reduce只给出最后80条记录的结果。以下是mapreduce函数的代码 map = %Q{ function(){ emit({},{category: this.category}); } } reduce = %Q{ function(key, values){ var categ

我有一个
项目
模型,它有一个属性
类别
。我希望项目计数按类别分组。我为这个功能写了一个MapReduce。它工作得很好。我最近写了一个脚本来创建5000个项目。现在我意识到我的map reduce只给出最后80条记录的结果。以下是mapreduce函数的代码

map = %Q{
  function(){
    emit({},{category: this.category});
  }
}

reduce = %Q{
  function(key, values){
    var category_count = {};
    values.forEach(function(value){
      if(category_count.hasOwnProperty(value.category))
        category_count[value.category]++;  
      else
        category_count[value.category] = 1 
    })
    return category_count;
  }
}

Item.map_reduce(map,reduce).out(inline: true).first.try(:[],"value")

经过研究,我发现。如何实现我想要的功能

我同意尼尔·伦的评论

从提供的信息中我可以看到,如果您使用的MongoDB版本大于或等于2.2,那么可以使用聚合框架而不是map reduce

db.items.aggregate([
  { $group: { _id: '$category', category_count: { $sum: 1 } }
])

在MongoDB()中编写map reduce代码时,必须遵循一条规则。一个是emit(它发出键/值对)必须与reduce函数返回的值具有相同的格式

如果
emit(this.key,this.value)
则reduce必须返回与
this.value
完全相同的类型。如果
emit({},1)
则reduce必须返回一个数字。如果
emit({},{category:this.category})
则reduce必须返回格式为
{category:“string”}
(假设category是字符串)的文档

所以这显然不是你想要的,因为你想要的是总数,所以让我们看看reduce返回了什么,并从中计算出你应该排放什么

最后,您似乎希望累积一个文档,其中每个类别都有一个键名,其值是一个表示其出现次数的数字。比如:

{category_name1:total, category_name2:total}
如果是这种情况,那么正确的映射函数将
emit({},{“this.category”:1})
,在这种情况下,reduce将需要为每个类别对应的键添加数字

以下是地图的外观:

map=function (){
     category = { };
     category[this.category]=1;
     emit({},category);
}
下面是正确的相应减少:

reduce=function (key,values) {
     var category_count = {};
     values.forEach(function(value){
        for (cat in value) {
           if( !category_count.hasOwnProperty(cat) ) category_count[cat]=0;
           category_count[cat] += value[cat];
        }
     });
     return category_count;
}
请注意,它满足-如果从不调用reduce函数,它将正常工作(如果集合中只有一个文档,则会出现这种情况),如果reduce函数被多次调用,它将正常工作(当您有100多个文档时会发生这种情况)

一种更为传统的方法是将category name作为键,将number作为值。这简化了映射并减少了:

map=function() { 
   emit(this.category, 1);
}

reduce=function(key,values) {
    var count=0;
    values.forEach(function(val) {
        count+=val;
    }
    return count;
}
这将合计每个类别出现的次数。这也满足了MapReduce的要求——如果从不调用reduce函数,它将正常工作(对于只出现一次的任何类别都是如此),如果reduce函数被多次调用,它将正常工作(如果任何类别出现超过100次,则会发生这种情况)

正如其他人所指出的,聚合框架使相同的工作变得更加简单:

db.collection.aggregate({$group:{_id:"$category",count:{$sum:1}}})

尽管这与我展示的第二个mapReduce的格式相匹配,而不是您原来的格式,即将类别名称作为键输出。然而。

也许你们的头脑中并没有得到足够的关注,但这个问题在输入文档和期望结果方面也缺乏足够的细节,以便得到回答。没有人回答,因为没有足够的细节来回答。你现在没有累积接近票数的唯一原因是因为你提供了悬赏。用细节编辑你们的问题,以便得到答案。有足够的细节可以回答——我在我的答案中说明他做错了什么,如何正确地做,以及有什么更好的方法来做。