MapReduce函数返回两个输出。 MongoDB

时间:2019-05-20 03:25:02

标签: mongodb mapreduce

我目前正在使用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函数以按性别分组数据并显示

  1. 男女性别总数
  2. 每个性别的平均体重

我可以创建一个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} }

谢谢

1 个答案:

答案 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可能真的不值得花时间,除非您有一个紧迫的用例,目前无法通过任何其他方式解决