我希望在将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操作,我不想将此操作卸载到代码中。
答案 0 :(得分: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 );
};
所以很简单,这只是“询问”字段名称,同时排除任何你不知道的字段名称。在这种情况下,使用正则表达式匹配字段名称中的第一个“alpha”字符。我允许字段可能是“foo16”,bar32“,”baz12“的可能性,并且这些都不会对操作产生影响。无论如何,一些方法可以剥离你想要的字段部分。
这些值在每个文档内部添加并发送到reducer,因为只有一个“key”,即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,
}
仅基于您拥有的示例文档。