【问题标题】:MapReduce function to return two outputs. MongoDBMapReduce 函数返回两个输出。 MongoDB
【发布时间】:2019-05-20 18:06:06
【问题描述】:

我目前正在使用 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} }

谢谢

【问题讨论】:

  • 为什么选择 mapReduce?聚合更好更快。
  • 嘿,是的,我听说了,但是我正在学习 MapReduce,所以需要在迁移到聚合之前知道如何操作...

标签: mongodb mapreduce


【解决方案1】:

在任何实现中“map/reduce”的关键规则基本上是same shape of data needs to be emitted by the mapper as is also returned by the reducer. 这样做的关键原因是“map/reduce”在概念上如何通过很可能调用 reducer 多次。这基本上意味着您可以调用您的 reducer 函数,该输出已经从上一次通过 reducer 发出的输出以及来自 mapper 的其他数据.

MongoDB 可以为同一个键多次调用 reduce 函数。在这种情况下,该键的reduce函数的先前输出将成为该键的下一个reduce函数调用的输入值之一。

也就是说,“平均”的最佳方法是总计数据和计数,然后简单地将两者相除。这实际上为“map/reduce”操作添加了另一个步骤,作为 finalize 函数。

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;
    }
  }
)

一切都很好,因为 mapperreducer 都发出具有相同 shape 的数据,并且还期望输入该 shape em> 在 reducer 本身内。当然,finalize 方法只是在所有 "reducing" 最终完成并处理每个结果之后才调用。

尽管如此,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 一起使用的keyalways升序顺序排序。但是您不能以任何其他方式排序输出中的聚合结果,当然在输出到另一个集合时不执行查询,或者通过“在内存中”使用返回的结果服务器。


作为“旁注”(虽然很重要),您可能还应该在“学习”中考虑到现实是“服务器- MongoDB 的侧 JavaScript" 功能实际上是一个解决方法,而不是一个功能。 MongoDB 刚推出时,它使用 JavaScript 引擎进行服务器执行,主要是为了弥补尚未实现的功能

因此弥补许多查询运算符和聚合函数的完整实现(稍后会出现),添加 JavaScript 引擎是一个“快速修复”允许以最少的实现来完成某些事情。

这些年来的结果是那些 JavaScript 引擎功能 正在逐渐被删除。 API 的group() 函数被移除。 API 的 eval() 函数已弃用,并计划在下一个主要版本中删除。对于这些 JavaScript 在服务器功能上的有限未来,写作基本上是“在墙上”,因为明确的模式是 native 功能为某些东西提供支持,然后基本上不再需要继续支持 JavaScript 引擎了。

这里的核心智慧是专注于学习这些服务器特性上的 JavaScript,可能不值得投入时间,除非你有一个目前无法通过任何其他方式解决的紧迫用例.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-27
    • 1970-01-01
    • 2014-08-01
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多