我目前正在使用MongoDB做一些基本的mapReduce。
我目前有如下数据:
db.football_team.insert({name: "Tane Shane", weight: 93, gender: "m"});
db.football_team.insert({name: "Lily Jones", weight: 45, gender: "f"});
...
我想创建一个mapReduce函数以按性别分组数据并显示
我可以创建一个map / reduce函数来分别执行每个函数,只是无法理解如何显示这两个函数的输出。我猜因为分组是基于性别的,所以地图功能应该保持不变,只是在缩小部分后进行一些更改...
到目前为止工作
var map1 = function()
{var key = this.gender;
emit(key, {count:1});}
var reduce1 = function(key, values)
{var sum=0;
values.forEach(function(value){sum+=value["count"];});
return{count: sum};};
db.football_team.mapReduce(map1, reduce1, {out: "gender_stats"});
输出
db.football_team.find()
{"_id" : "f", "value" : {"count": 12} }
{"_id" : "m", "value" : {"count": 18} }
谢谢
答案 0 :(得分:1)
任何实现中“映射/减少”的关键规则基本上是same shape of data needs to be emitted by the mapper as is also returned by the reducer.的主要原因是“映射/减少”概念上如何工作的一部分,方法是很可能调用 reducer < / em>多次。基本上,这意味着您可以对上一次通过 reducer 传递的输出以及 mapper 中的其他数据调用 reducer 函数。
MongoDB可以为同一键多次调用reduce函数。在这种情况下,该键的reduce函数先前的输出将成为该键的下一个reduce函数调用的输入值之一。
也就是说,最好的“平均”方法是总计数据和计数,然后简单地将两者相除。实际上,这是作为 finalize 函数的“ map / reduce”操作的另一步骤。
db.football_team.mapReduce(
// mapper
function() {
emit(this.gender, { count: 1, weight: this.weight });
},
// reducer
function(key,values) {
var output = { count: 0, weight: 0 };
values.forEach(value => {
output.count += value.count;
output.weight += value.weight;
});
return output;
},
// options and finalize
{
"out": "gender_stats", // or { "inline": 1 } if you don't need another collection
"finalize": function(key,value) {
value.avg_weight = value.weight / value.count; // take an average
delete value.weight; // optionally remove the unwanted key
return value;
}
}
)
一切都很好,因为 mapper 和 reducer 都发出具有相同 shape 的数据,并且期望输入该 shape 在 reducer 本身中的em>。当然,在所有“减少” 完成之后,只需调用 finalize 方法并处理每个结果。
如前所述,aggregate()
方法实际上可以更有效地完成此操作,并且使用本机编码的方法也不会造成服务器端JavaScript解释和执行的开销(和潜在的安全风险):
db.football_team.aggregate([
{ "$group": {
"_id": "$gender",
"count": { "$sum": 1 },
"avg_weight": { "$avg": "$weight" }
}}
])
基本上就是这样。此外,您实际上可以在$group
流水线阶段(或与此相关的任何阶段)之后继续执行并做其他事情,这些方式是您无法使用MongoDB mapReduce
实现的。值得注意的是,将$sort
应用于结果:
db.football_team.aggregate([
{ "$group": {
"_id": "$gender",
"count": { "$sum": 1 },
"avg_weight": { "$avg": "$weight" }
}},
{ "$sort": { "avg_weight": -1 } }
])
mapReduce
唯一允许的排序仅仅是与emit
一起使用的键总是 以升顺序排序。但是您不能以任何其他方式对输出中的汇总结果进行排序,当然,在输出到另一个集合时当然也不执行查询,或者通过“在内存中”处理返回的结果服务器。
作为“附带说明” (尽管很重要),您可能还应该在“学习” 中考虑现实是“服务器- MongoDB的侧边JavaScript” 功能实际上是替代方法,而不是功能。最初引入MongoDB时,它为服务器执行应用了JavaScript引擎,主要是弥补尚未实现的功能。
因此,弥补了缺少许多查询运算符和聚合函数的完整实现(随后会出现),添加JavaScript引擎是“快速修复” 以允许某些事情以最小的实现来完成。
多年来的结果是 JavaScript引擎功能被逐渐删除。 API的group()
功能已删除。 API的eval()
函数已弃用,并计划在下一个主要版本中删除。对于服务器功能中的这些JavaScript有限的未来,基本上是“在墙上” ,因为清晰的模式是 native 功能为某些功能提供了支持,然后基本上不再需要继续支持JavaScript引擎。
这里的核心智慧是,在服务器功能上专注于学习这些JavaScript可能真的不值得花时间,除非您有一个紧迫的用例,目前无法通过任何其他方式解决