MongoDB 中的 MapReduce 不会一次性减少所有具有相同键的 k-v 对

MapReduce in MongoDB doesn't reduce all the k-v pairs with the same key in one go

我从 c​​sv 导入了一个数据库,其中包含以下信息:

(这是 csv:https://www.kaggle.com/jboysen/global-food-prices

csv 中的字符串是这样排序的:

我需要为每个国家/地区显示每个产品的最大价格。

我写了:

1) 带有关键国家+商品和价值价格的地图

var map = function() {
   emit({country: this.country_name, commodity: this.commodity_name}, {price: this.price});
};

2) 扫描与键相关的价格并检查最高价格的减少

var reduce = function(key, values) {

   var maxPrice = 0.0;

   values.forEach(function(doc) {
      var thisPrice = parseFloat(doc.price);
      if( typeof doc.price != "undefined") {
            if (thisPrice > maxPrice) {
               maxPrice = thisPrice; 
            }
      }
   });

   return {max_price: maxPrice};
};

3) 我将 map reduce 的输出发送到集合 "mr"

db.prices.mapReduce(map, reduce, {out: "mr"});

问题:

例如,如果我打开 csv 并手动排序:

我可以查一下(举个数据例子)在阿富汗商品面包的最高价格是 65.25

不过,当我检查 M-R 时,结果是阿富汗面包的最高价格为 0。

发生了什么:

csv 中有 10 个区域记录了阿富汗的面包。 我在 reduce 的最后一行添加了:

print("reduce with key: " + key.country + ", " + key.commodity + "; max price: " + maxPrice);

理论上,如果我在 mongodb 日志中搜索,我应该只会找到一个带有 "reduce with key: Afghanistan, Bread; max price: ???" 的入口。 相反,我看到十行(相同数量的区域),每行都有不同的最高价格。 最后一个有"max price 0".

我的假设:

看起来,在发出之后,当调用 reduce 时,它​​没有寻找具有相同键的所有 k-v 对,而是考虑了接近的子组。

因此,回顾一下我在 csv 结构上的起始示例:

我是否必须重新归约才能处理所有部分归约作业?

我已经设法解决了这个问题。 MongoDB 不一定一次性减少所有具有相同密钥的 k-v 对。

可能会发生这种情况(如本例)MongoDB 将对与特定键相关的 k-v 对的子集执行归约,然后发送第一个归约的输出。对与同一键相关的另一个子集进行第二次归约。

我的代码不起作用,因为:

  • MongoDB 对与键 "Afghanistan, Bread" 相关的 k-v 对的子集执行了归约,输出中的变量名为 "maxPrice"
  • MongoDB 将继续减少其他子集
  • MongoDB,当面对 "Afghanistan, Bread" 的另一个子集时,将采用第一个 reduce 的输出,并将其用作值
  • reduce 的输出命名为 "maxPrice",但其他值命名为 "price"
  • 由于我要求值 "doc.price",当我扫描包含 "maxPrice" 的文档时,它被忽略了

有两种方法可以解决这个问题:

1) 您对 reduce 输出变量使用与 emit 输出值相同的名称

2) 索引选为键的属性,并在 mapReduce() 上使用 "sort" 选项,以便一次性减少与键相关的所有 k-v 对

第二种方法是,如果您不想放弃使用不同的名称作为 reduce 输出的名称(而且它具有更好的性能,因为它只对每个键进行一次 reduce)。