MongoDB 中的 MapReduce 不会一次性减少所有具有相同键的 k-v 对
MapReduce in MongoDB doesn't reduce all the k-v pairs with the same key in one go
我从 csv 导入了一个数据库,其中包含以下信息:
- 国家
- 地区
- 商品
- 价格
- 日期
(这是 csv:https://www.kaggle.com/jboysen/global-food-prices)
csv 中的字符串是这样排序的:
- 国家1,地区1.1,商品X,价格,日期A
- 国家1,地区1.1,商品X,价格,日期B
- 国家1,地区1.1,商品Y,价格,日期A
- 国家 1,地区 1.1,商品 Y,价格,日期 B
- ...
- 国家1,地区1.2,商品X,价格,日期A
- 国家1,地区1.2,商品X,价格,日期B
- 国家1,地区1.2,商品Y,价格,日期A
- 国家1,地区1.2,商品Y,价格,日期B
- ...
- 国家2,地区2.1,商品X,价格,日期A
- ...
我需要为每个国家/地区显示每个产品的最大价格。
我写了:
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 结构上的起始示例:
- 直到 reduce 扫描发出与 "afghanista, region 1, bread" 相关的输出,它会对它们进行 reduce
- 然后它会减少与 "afghanistan, region 1, commodityX"
相关的输出
- 然后它对与 "afghanistan, region 2, bread" 相关的输出进行另一个减少(而不是在单个减少中减少所有 k-v 对与阿富汗+面包)
我是否必须重新归约才能处理所有部分归约作业?
我已经设法解决了这个问题。
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)。
我从 csv 导入了一个数据库,其中包含以下信息:
- 国家
- 地区
- 商品
- 价格
- 日期
(这是 csv:https://www.kaggle.com/jboysen/global-food-prices)
csv 中的字符串是这样排序的:
- 国家1,地区1.1,商品X,价格,日期A
- 国家1,地区1.1,商品X,价格,日期B
- 国家1,地区1.1,商品Y,价格,日期A
- 国家 1,地区 1.1,商品 Y,价格,日期 B
- ...
- 国家1,地区1.2,商品X,价格,日期A
- 国家1,地区1.2,商品X,价格,日期B
- 国家1,地区1.2,商品Y,价格,日期A
- 国家1,地区1.2,商品Y,价格,日期B
- ...
- 国家2,地区2.1,商品X,价格,日期A
- ...
我需要为每个国家/地区显示每个产品的最大价格。
我写了:
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 结构上的起始示例:
- 直到 reduce 扫描发出与 "afghanista, region 1, bread" 相关的输出,它会对它们进行 reduce
- 然后它会减少与 "afghanistan, region 1, commodityX" 相关的输出
- 然后它对与 "afghanistan, region 2, bread" 相关的输出进行另一个减少(而不是在单个减少中减少所有 k-v 对与阿富汗+面包)
我是否必须重新归约才能处理所有部分归约作业?
我已经设法解决了这个问题。 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)。