在这样的集合上:
db.consFieldTest.insert([
{ status : "err" }
{ status : "suc" }
{ status : "res" }
{ status : "res" }
{ status : "err" }
{ status : "err" }
{ status : "err" }
]);
聚合结果应该如下所示:
{ status: "err", maxCons : 3 },
// (optional) { status: "suc", maxCons : 1 },
// (optional) { status: "res", maxCons : 2 }
实际上,如果maxCons
计数器停在3就可以了,我需要知道err
状态是否连续出现3次或更多。
一个解决它的想法:
我想到了一个解决方法,在所有文档上添加一个增量,像这样:
{ status : "err", id : 0 },
{ status : "suc", id : 1 },
{ status : "res", id : 2 },
{ status : "res", id : 3 },
{ status : "err", id : 4 },
{ status : "err", id : 5 },
{ status : "err", id : 6 }
然后按0-2、1-3、2-4、…这会导致这样的结果:
{ _id : 0
res : [
{ status : "err", id : 0 },
{ status : "suc", id : 1 },
{ status : "res", id : 2 }
]
},
{ _id : 1
res : [
{ status : "suc", id : 1 },
{ status : "res", id : 2 },
{ status : "res", id : 3 },
]
},
{
_id : 2
res : [
{ status : "res", id : 2 },
{ status : "res", id : 3 },
{ status : "err", id : 4 },
]
} ...
有了这个,我可以计算状态err
以连续顺序发生的频率。但是我不知道怎么写这个group
阶段。 将此作为聚合框架问题处理的问题是,没有实际的概念将一个文档与另一个文档进行比较,因为所有操作要么一次处理单个文档,要么将文档分组在一起。
所以找到"连续"条目是你需要一个可以跨文档工作的"全局"变量空间。聚合框架没有办法做到这一点,但是这个问题可以用mapReduce来解决:
db.consFieldTest.mapReduce(
function() {
if ( lastSeen != this.status ) {
lastSeen = this.status;
list = [];
counter = 0;
}
list.push(this._id);
counter++;
emit(lastSeen,{ "list": list, "count": counter });
},
function(key,values) {
var mapped = values.map(function(x) { return x.count });
return values[mapped.indexOf(Math.max.apply(Math,mapped))];
},
{
"scope": { "lastSeen": null, "list": [], "counter": 0 },
"out": { "inline": 1 }
}
)
简单地说,这将为当前"状态"值发出一个"键",同时保留跟踪连续出现的"列表"one_answers"计数器"的全局变量。列表将在这里建立,仅使用_id
中的数值作为示例,但可以是任何值:
{ "_id": "err", "values": { "list": [1], "count": 1 }}
{ "_id": "suc", "values": { "list": [2], "count": 1 }}
{ "_id": "res", "values": { "list": [3], "count": 1 }}
{ "_id": "res", "values": { "list": [3,4], "count": 2 }}
{ "_id": "err", "values": { "list": [5], "count": 1 }}
{ "_id": "err", "values": { "list": [5,6], "count": 2 }}
{ "_id": "err", "values": { "list": [5,6,7], "count":3 }}
本质上是映射器发出的内容。注意全局变量的构建。
在reduce函数中,所有相同的键被一起处理,或者至少在"组"中处理相同的键,因为这里的reducer在工作。所以reduce函数就是在组中找到计数最大的值并返回匹配索引处的奇异项。
你得到的结果基本上是:
{ "_id": "err", "value": { "list": [5,6,7], "count":3 }}
{ "_id": "res", "value": { "list": [3,4], "count": 2 }}
{ "_id": "suc", "value": { "list": [2], "count": 1 }}
mapReduce按照键的顺序输出它的最终结果。
是的,JavaScript计算比聚合框架运行得慢一点,但是如果没有在文档范围内跟踪全局变量的能力,这是无法完成的。