【问题标题】:Perform aggregate after map on collection in mongo在 mongo 中收集地图后执行聚合
【发布时间】:2014-04-18 01:43:27
【问题描述】:

我希望在将 mongo db 中的一些记录传递给聚合函数之前对其进行操作。特别是,在对这些属性进行求和之前,我需要对集​​合的一些属性进行求和。

属性的总和最初不能在聚合查询中完成,因为属性名称在原始集合中有所不同。例如,我从以下内容开始:

{ timestamp: 1346774400000, foo3: 12, foo45: 13, foo9: 2 }, 
{ timestamp: 1346796000000, foo7: 33, foo2: 5 }

我需要修改每个文档以汇总以“foo”开头的每个属性的值,然后汇总集合中每个文档的所有这些值。

我为此编写了一个地图操作,它会产生如下内容:

{ timestamp: 1346774400000, foo_total: 27 }, 
{ timestamp: 1346796000000, foo_total: 38 } 

...但我无法对 db.collection.map() 的输出执行聚合函数。

有什么方法可以做到这一点,或者有更好的方法吗?我无法更改文档的现有结构,并且我想避免执行 map reduce 操作,并且我不想将此操作卸载到代码中。

【问题讨论】:

  • 目前还不清楚您的最终结果是什么。实际上,您只是说要“聚合”,但您没有说明实际聚合的方式或结果。无论如何,如果您有不同的属性名称,那么 mapReduce 可能会更好地处理您的整个操作。您不能在不明确知道它们是什么的情况下操作聚合管道中的字段名称。或者没有蛮力。
  • 我的错,我知道打开包装可能有点困难。我需要获取每个 foo_total 值并对所有文档求和。在提供的示例中,我的最终结果将是 27 + 38 或 65。​​

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

如上所述,文档中不同键值的问题是聚合不能专门处理这些,至少在不知道所有可能的值并编写很长的语句的情况下。

当然,您目前的方法是在检索集合结果后对其进行处理,实际上并不会产生集合本身,因此无法将其传递给聚合。

所以最好的方法是将整个事情传递给 mapReduce,而且逻辑相当简单。首先是一个映射器:

var mapper = function () {

  var patt = /^([a-z|A-Z]+)/;

  var total = {};

  for ( n in this ) {

      if ( (n == "timestamp") || n == "_id" )
        continue;

      var match = patt.exec(n)[0];
      if (!total.hasOwnProperty(match))
        total[match] = 0;

      total[match] += this[n];

  }

  emit( null, total );

};

非常简单,这只是要“询问”字段名称,同时排除您知道不需要的任何字段名称。在这种情况下,使用正则表达式匹配字段名称中的第一个“字母”字符。我允许字段可能是“foo16”、“bar32”、“baz12”,而这些对操作都没有影响。无论如何,有一些方法可以去除你想要的字段部分。

这些值在每个文档内部添加并发送到缩减器,因为只有一个“键”,即null

所以在reducer中:

var reducer = function (key,values) {

  var reduced = {};

  values.forEach(function(value) {
    for ( var n in value ) {
      if ( !reduced.hasOwnProperty(n) )
        reduced[n] = 0;

      reduced[n] += value[n];
    }
  });

  return reduced;

};

这类似地循环每个发出的文档,并对找到的每个“字段”的结果求和以产生结果:

{
    "results" : [
            {
                    "_id" : null,
                    "value" : {
                            "foo" : 65
                    }
            }
    ],
    "timeMillis" : 7,
    "counts" : {
            "input" : 2,
            "emit" : 2,
            "reduce" : 1,
            "output" : 1
    },
    "ok" : 1,
}

仅基于您拥有的示例文档。

【讨论】:

  • 谢谢尼尔,这就是我最终要做的事情。不幸的是,我正在使用的文档具有这种格式,这使得任务不像我想要的那样简单。这仍然比在代码中执行这种聚合要好,尤其是考虑到我正在处理的数据规模。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-08
  • 1970-01-01
  • 2019-04-13
  • 2018-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多