如何计算mongo reduce函数中两个字段的计数和唯一计数

时间:2012-01-31 16:49:44

标签: mongodb mapreduce

我有一个链接跟踪表,其中包含track_redirect和track_userid(以及其他字段)。我想输出给定链接的总计数,以及唯一计数 - 按用户ID计算重复数。因此,如果有人点击了同一个链接5次,我们可以区分。

我已尝试在键和值部分中发出this.track_userid,但无法掌握如何在reduce函数中正确访问它们。

因此,如果我回滚到它实际工作的时候,我会在下面提供非常简单的代码 - 就像它在'我的第一个mapreduce函数'示例中一样

地图

function() {
  if(this.track_redirect) {
    emit(this.track_redirect,1); 
  }
}

减少

function(k, vals) {
  var sum = 0;
  for (var i in vals) {
    sum += vals[i];
  } 
  return sum;
}

我想知道发出额外的用户ID信息并在mapreduce中访问它的正确方法。或者我是以错误的方式思考它?

如果不清楚,我不想计算用户ID所做的总点击次数,而是计算每个网址+用户ID的唯一点击次数 - 不计算用户ID在每个链接上的任何重复点击次数

有人能指出我正确的方向吗?谢谢!

1 个答案:

答案 0 :(得分:4)

您实际上可以在emit调用的第二个参数上传递任意对象。这意味着您可以利用此功能并将用户标识存储在其中。例如,您的地图功能可能如下所示:

var mapFunc = function() {
  if (this.track_redirect) {
    var tempDoc = {};
    tempDoc[this.track_userid] = 1;

    emit(this.track_redirect, {
      users_clicked: tempDoc,
      total_clicks: 1
    });
  }
};

你的reduce函数可能如下所示:

var reduceFunc = function(key, values) {
  var summary = {
    users_clicked: {},
    total_clicks: 0
  };

  values.forEach(function (doc) {
    summary.total_clicks += doc.total_clicks;
    // Merge the properties of 2 objects together
    // (and these are actually the userids)
    Object.extend(summary.users_clicked, doc.users_clicked);
  });

  return summary;
};

摘要对象的users_clicked属性基本上将每个用户的id存储为属性(因为您不能拥有重复的属性,您可以保证它将存储唯一的用户)。另请注意,您必须注意以下事实:传递给reduce函数的某些值可能是先前reduce的结果,并且上面的示例代码将此考虑在内。您可以在文档here中找到有关所述行为的更多信息。

为了获得唯一计数,您可以传递在reduce阶段完成时调用的终结函数:

var finalFunc = function(key, value) {
  // Counts the keys of an object. Taken from:
  // http://stackoverflow.com/questions/18912/how-to-find-keys-of-a-hash
  var countKeys = function(obj) {
    var count = 0;

    for(var i in obj) {
      if (obj.hasOwnProperty(i))
      {
        count++;
      }
    }

    return count;
  };

  return {
    redirect: key,
    total_clicks: value.total_clicks,
    unique_clicks: countKeys(value.users_clicked)
  };
};

最后,您可以像这样执行map reduce作业(修改out属性以满足您的需要):

db.users.mapReduce(mapFunc, reduceFunc, { finalize: finalFunc, out: { inline: 1 }});