MapReduce聚合基于文档外部包含的属性

时间:2011-09-07 13:36:07

标签: mongodb mapreduce

假设我有一系列'活动',每个活动都有名称,费用和位置:

{_id : 1 , name: 'swimming', cost: '3.40', location: 'kirkstall'}
{_id : 2 , name: 'cinema', cost: '6.50', location: 'hyde park'}
{_id : 3 , name: 'gig', cost: '10.00', location: 'hyde park'}

我还有一个people集合,为每项活动记录他们计划在一年内完成的次数:

{_id : 1 , name: 'russell', activities : { {1 : 9} , {2 : 4} , {3 : 21} }}

出于多种原因,我不想通过将它们放入人员集合来对活动的属性进行非规范化。

首先,这是关于计划,因此如果活动的费用发生变化,则需要在人员收集中进行更改。所以我必须更新所有人的记录。

其次,我可能希望在某些时候向活动集合添加一些其他属性,并且希望避免在我这样做时将它们添加到人物集合中每个记录中的每个活动。

但是,现在我想做一个MapReduce,找出所有人计划的活动总数,按位置分组。

这意味着在人员集合的MapReduce期间,我需要知道他们计划的活动的位置。谁能想到一个很好的方法呢?

此刻我最好的镜头(非常垃圾)是创建一个存储的javascript函数,它接受一系列activity_ids,查询activity集合,并将activity_id的map返回到location。然后我将其粘贴在map函数中并从中查找位置。这可能很垃圾,但正如我所说,activities集合上的相同查询将针对people集合中的每个项目运行一次。

1 个答案:

答案 0 :(得分:0)

我是通过将MapReduce包装在一些存储的javascript中来实现的。

function (query) {

  var one = db.people.findOne(query);
  var activity_ids = [];
  for (var k in one.activities){
    activity_ids.push(parseInt(k));
  }

  var activity_location_map = {};
  db.activities.find({id : {$in : activity_ids}}).forEach(function(a){
    activity_location_map[a.id] = a.location;
  });


  return db.people.mapReduce(
    function map(){
      for (var k in this.activities){
        emit({location : activity_location_map[k]} , { total: this.activities[k] });
        emit({location: activity_location_map[k]} , { total: this.activities[k] });
      }
    },
    function reduce(key, values){
      var reduced = {total: 0};
      values.forEach(function(value){
        reduced.total += value.total;
      });

      return reduced;
    },
    {out : {inline: true}, scope : { activity_location_map : activity_location_map }}
  ).results;
}

烦人,凌乱,但它有效,我想不到更好。