如何归还未知领域名称的总和?

时间:2016-04-30 14:10:26

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

我有这样的数据,想要计算所有字段的真/假值

{
    "key1" : true,
    "key2" : false,
    "key3" : true
},
{
    "key1" : false,
    "key2" : true,
    "key3" : true
}

预期结果是

{
  key1: { true: 1, false: 1 },
  key2: { true: 1, false: 1 },
  key3: { true: 2, false: 0 }
}

我可以使用group by运算符为特定字段计算它,但我不知道如何对集合中的所有字段执行此操作

3 个答案:

答案 0 :(得分:2)

你能得到的最近的是:

db.foo.aggregate([{
    $group: {
        _id: null,
        key1 : { 
            $sum: {
                $cond: {
                    if: "$key1",
                    then: 1,
                    else: 0
                }
            }
        },
        key2 : { 
            $sum: {
                $cond: {
                    if: "$key2",
                    then: 1,
                    else: 0
                }
            }
        },
        key3 : { 
            $sum: {
                $cond: {
                    if: "$key3",
                    then: 1,
                    else: 0
                }
            }
        }
    }
}])

哪个会给你:

{
    "_id" : null,
    "key1" : 1,
    "key2" : 1,
    "key3" : 2
}

答案 1 :(得分:2)

如果您不知道文档中的键名称数量,那么您不能使用聚合框架,而是您需要的mapReduce并将map reduce结果输出到新的集合中使用out option或使用inline: 1在shell中显示结果。这里我们使用了该选项,因为我们需要额外的处理步骤才能获得期望的结果。

db.collection.mapReduce(
    function() { 
        var keys = Object.keys(this); 
        for(var ind=0; ind<keys.length; ind++) { 
            if (keys[ind] !== "_id") {
                var d = {}; 
                d.name = keys[ind], 
                d.value= this[keys[ind]]; 
                emit(d, 1);
            }
        }
    },
    function(key, values) { return Array.sum(values); }, 
    { "out": "mresult" }
)

返回如下内容:

{
        "result" : "mresult",
        "timeMillis" : 566,
        "counts" : {
                "input" : 2,
                "emit" : 6,
                "reduce" : 1,
                "output" : 5
        },
        "ok" : 1
}

保存在新创建的集合中的五个文档,如mapReduce输出所示。您可以使用.find()

轻松完成此操作
db.mresult.find()

产生类似这样的东西:

{ "_id" : { "name" : "key1", "value" : false }, "value" : 1 }
{ "_id" : { "name" : "key1", "value" : true }, "value" : 1 }
{ "_id" : { "name" : "key2", "value" : false }, "value" : 1 }
{ "_id" : { "name" : "key2", "value" : true }, "value" : 1 }
{ "_id" : { "name" : "key3", "value" : true }, "value" : 2 }

正如您所看到的那样,即使使用mapReduce,我们也无法获得预期的结果,这有点令人讨厌,但这些文档可以使用.aggregate方法轻松处理。

您管道中的第一个阶段是$project阶段,您基本上使用$cond条件运算符来“设置”“true”和“false”的值。管道的最后一个阶段是$group阶段,您可以在其中对文档进行分组,并使用$sum累加器运算符返回每个组的总和。

db.mresult.aggregate([
    { "$project": { 
        "true": { 
            "$cond": [
                { "$eq": [ "$_id.value", true ] }, 
                "$value", 
                0
            ]
        }, 
        "false": { 
            "$cond": [
                { "$eq": [ "$_id.value", false ] }, 
                "$value", 
                0
            ]
        }
    }}, 
    { "$group": { 
        "_id": "$_id.name", 
        "true": { "$sum": "$true" }, 
        "false": { "$sum": "$false" } 
    }}
])

产生类似这样的东西:

{ "_id" : "key3", "true" : 2, "false" : 0 }
{ "_id" : "key2", "true" : 1, "false" : 1 }
{ "_id" : "key1", "true" : 1, "false" : 1 }

当然,这并不是你预期的输出,而是更好,因为一般来说使用数据作为关键并不是一个好主意。

答案 2 :(得分:1)

我会将回调传递给find查询。它会给你更多的灵活性。

    Sample.find({}, function (err, docs) {
        if (!err) {
            var result = {};
            for (var i = 0; i < docs.length; i++) {
                var currDoc = docs[i]._doc;
                delete currDoc._id;
                Object.keys(currDoc).forEach(function (key) {
                    var processedKey = result[key] ? result[key] : {"false": 0, "true": 0};
                    var count = (processedKey["" + currDoc[key]] | 0 ) + 1;
                    processedKey["" + currDoc[key]] = count;
                    result[key] = processedKey;
                });
            }
            console.log(result);
            process.exit();
        } else {
            throw err;
        }
    });
输入

[
    {
        "key1": true,
        "key2": false,
        "key3": true
    },
    {
        "key1": true,
        "key2": false,
        "key3": true
    }
]

将输出

{
    key3: {
        false: 0,
        true: 2
    },
    key2: {
        false: 2,
        true: 0
    },
    key1: {
        false: 0,
        true: 2
    }
}