我在问这个问题之前经常搜索,但找不到任何相关信息。
所以这是:
我的问题是我有以下格式的mongo集合:
{
"_id" : ObjectId("55bf4031eb8ac118a4b3110e"),
"sid" : 1,
"plugin_count" : [
0,
0,
0,
1,
0,
1,
0,
0,
0,
0
],
},
{
"_id" : ObjectId("55bf4031eb8ac118a4b3110e"),
"sid" : 1,
"plugin_count" : [
2,
1,
0,
6,
0,
10,
12,
0,
16,
22
],
}
现在我想做的是,我想分别添加非关联数组中的所有元素并输出它们:
{
"result": [
{
"_id": 1,
"plugin_count": [
2,
1,
0,
7,
0,
11,
12,
0,
16,
22
]
}
],
"ok": 1
}
我的查询如下
db.plugin_table.aggregate([
{
"$match" : {
"sid" : {
"$in" : [1]
}
}
},
{
"$unwind" : "$plugin_count"
},
{
"$group" : {
"_id": 1,
"plugin_count" : {
"$sum" : "$plugin_count"
}
}
}
]);
但我得到以下输出:
{
"result": [
{
"_id": 1,
"plugin_count": 0
}
],
"ok": 1
}
请帮助我从字面上拉出我的头发。 :(
答案 0 :(得分:0)
使用聚合框架确实没有理智的方法。这里的问题是跟踪数组元素的“索引”位置,其中没有任何东西可以做到这一点。
这里最好的选择是mapReduce,这可以非常简单地处理问题,并可以很好地扩展到任意数量的分组键:
db.plugin_table.mapReduce(
function () {
emit(this.sid, { plugin_count: this.plugin_count });
},
function (key,values) {
var result = { plugin_count: [] };
values.forEach(function(value) {
value.plugin_count.forEach(function(plugin,idx) {
if ( result.plugin_count[idx] === undefined ) {
result.plugin_count[idx] = plugin;
} else {
result.plugin_count[idx] += plugin;
}
});
});
return result;
},
{
"query": { "sid": 1 },
"out": { "inline": 1 }
}
)
这会生成所需的输出,albiet格式为mapReduce始终使用顶级键_id
和value
生成输出:
{
"_id" : 1,
"value" : {
"plugin_count" : [
2,
1,
0,
7,
0,
11,
12,
0,
16,
22
]
}
}
请注意,此处的reduce算法也可以轻松处理不同长度的数组,因为所有内容都与索引位置配对,或者如果每个键的组合结果中不存在该位置,则会创建其他数据。
聚合框架可以处理的唯一真实方式是将数据实际上放在“关联数组”中,或者至少将每个元素作为关联数组本身,就像这样:
{
"_id" : ObjectId("55d32ffde12af47feb19bce7"),
"sid" : 1,
"plugin_count" : [
{
"pos" : 0,
"value" : 0
},
{
"pos" : 1,
"value" : 0
},
{
"pos" : 2,
"value" : 0
},
{
"pos" : 3,
"value" : 1
},
{
"pos" : 4,
"value" : 0
},
{
"pos" : 5,
"value" : 1
},
{
"pos" : 6,
"value" : 0
},
{
"pos" : 7,
"value" : 0
},
{
"pos" : 8,
"value" : 0
},
{
"pos" : 9,
"value" : 0
}
]
}
这是这些方面的基本转变:
db.junk.find().forEach(function(doc) {
doc.plugin_count = doc.plugin_count.map(function(value,idx) {
return { "pos": idx, "value": value };
});
db.newjunk.insert(doc);
});
然后你有一个基本的聚合,只需在“pos”元素上分组“first”并对结果求和。然后可以通过分组回“sid”来形成最终的数组:
db.newjunk.aggregate([
{ "$match": { "sid": 1 } },
{ "$unwind": "$plugin_count" },
{ "$group": {
"_id": {
"sid": "$sid",
"pos": "$plugin_count.pos"
},
"value": { "$sum": "$plugin_count.value" }
}},
{ "$sort": { "_id": 1 } },
{ "$group": {
"_id": "$_id.sid",
"plugin_count": { "$push": "$value" }
}}
])
它为您提供与之前相同的输出:
{ "_id" : 1, "plugin_count" : [ 2, 1, 0, 7, 0, 11, 12, 0, 16, 22 ] }
此处还注意到您可以通过保留关联信息来避免$sort
阶段。使用$group
并不能保证位置保持不变,但使用关联信息并不是必需的。
所以这一切都取决于你能忍受的东西。如果要将普通数组保留在数据中,那么您将需要mapReduce来获取结果。但是,如果您愿意更改数据格式,则完全可以使用聚合方法。
然而,这可能是一个很好的例子,在“大规模”的情况下,mapReduce进程可以通过避免处理$unwind
的开销来击败聚合过程。