计算连续字段出现的最高数量

时间:2014-08-04 11:09:29

标签: javascript mongodb mapreduce mongodb-query aggregation-framework

在这样的集合上:

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阶段。

1 个答案:

答案 0 :(得分:2)

将此问题视为aggregation framework问题的问题在于,没有将一个文档与另一个文档进行比较的实际概念,因为所有操作既可以一次处理单个文档,也可以将文档分组到一起

因此,找到“连续”条目是您需要一个“全局”变量空间,可以跨文档工作。聚合框架没有任何内容可以做到这一点,但问题可以通过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 }
    }
)

简单地说,这将为当前“状态”值发出“键”,同时保持跟踪连续出现的“列表”和“计数器”的全局变量。列表将在此处构建,仅使用_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评估的运行速度比聚合框架慢一点,但是如果没有能力跟踪文档范围内的全局变量,那么这只是其他事情无法完成的事情。