mongodb map减少:“第一/最低”值?

时间:2011-09-19 08:12:55

标签: mongodb mapreduce

我有这样的文件:

{
        "_id" : "someid",
        "name" : "somename",
        "action" : "do something",
        "date" : ISODate("2011-08-19T09:00:00Z")
}

我想将地图缩小为以下内容:

{
        "_id" : "someid",
        "value" : {
            "count" : 100,
            "name" : "somename",
            "action" : "do something",
            "date" : ISODate("2011-08-19T09:00:00Z")
            "firstEncounteredDate" : ISODate("2011-07-01T08:00:00Z")
        }
}

我想通过“名称”,“操作”和“日期”对地图缩小文档进行分组。但是每个文档都应该包含这个“firstEncounteredDate”,其中包含最早的“日期”(实际上按“名称”和“动作”分组)。

如果我按姓名,行动和日期分组,firstEncounteredDate将始终是日期,这就是为什么我想知道是否有任何方法可以获得“最早的日期”(按“名称”和“行动”分组)在做map-reduce时整个文档。

我如何在map reduce中执行此操作?

编辑:关于firstEncounteredDate的更多细节(由@ beny23提供)

1 个答案:

答案 0 :(得分:2)

似乎是两遍地图 - 减少符合条例草案,有点类似于这个例子:http://cookbook.mongodb.org/patterns/unique_items_map_reduce/

在#1传递中,将原始“name”x“action”x“date”文档分组为“name”和“action”,在reduce期间将各种“日期”值收集到“dates”数组中。使用'finalize'函数查找收集日期的最小值。

未经测试的代码:

// phase i map function : 

function () {
  emit( { "name": this.name, "action": this.action } , 
        { "count": 1, "dates": [ this.date ] } );
}

// phase i reduce function : 

function( key, values ) {
  var result = { count: 0, dates: [ ] };

  values.forEach( function( value ) {
    result.count += value.count;
    result.dates = result.dates.concat( value.dates );
  }

  return result;
}

// phase i finalize function : 

function( key, reduced_value ) {
  var earliest = new Date( Math.min.apply( Math, reduced_value.dates ) );
  reduced_value.firstEncounteredDate = earliest ;
  return reduced_value;
}

在第2遍中,使用在#1传递中生成的文档作为输入。对于每个“name”x“action”文档,为每个收集的日期发出一个新的“name”x“action”x“date”文档,以及现在确定的“name”x“action”对共有的最小日期。按“名称”x“操作”x“日期”分组,总计减少期间每个日期的计数。

同样未经测试的代码:

// phase ii map function : 

function() {
  this.dates.forEach( function( d ) {
    emit( { "name": this.name, "action": this.action, "date" : d } ,
          { "count": 1, "firstEncounteredDate" : this.firstEncounteredDate } );
  }
}

// phase ii reduce function : 

function( key, values ) {
  // note: value[i].firstEncounteredDate should all be identical, so ... 
  var result = { "count": 0, 
                 "firstEncounteredDate": values[0].firstEncounteredDate };

  values.forEach( function( value ) {
    result.count += value.count;
  }

  return result;
}
通过#2通行证并没有做很多繁重的工作,显然 - 它主要是复制每个文件N次,每个独立日期一次。我们可以轻松地在第1关的减少步骤中建立一个独特日期的地图,以确定它们的发生率。 (事实上​​,如果我们这样做,那么在传递#1的值中有一个“计数”字段就没有意义了。)但是做第二遍是一种相当轻松的方式。生成包含所需文档的完整目标集合。