我有这样的数据,想要计算所有字段的真/假值
{
"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运算符为特定字段计算它,但我不知道如何对集合中的所有字段执行此操作
答案 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
}
}