如何在 MongoDb 中聚合非关联数组中的数据
How to aggregate data within non associative array in MongoDb
问这个问题之前查了很多资料,都没有找到相关资料
这里是:
我的问题是我有一个 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
}
请帮帮我,我简直是在拔头发。 :(
使用聚合框架确实没有明智的方法来做到这一点。这里的问题是跟踪数组元素的 "index" 位置,其中没有什么可以做到这一点。
你最好的选择是 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 }
}
)
这会生成所需的输出,尽管 mapReduce 总是生成具有 _id
和 value
:
的顶级键的输出形式
{
"_id" : 1,
"value" : {
"plugin_count" : [
2,
1,
0,
7,
0,
11,
12,
0,
16,
22
]
}
}
请注意,这里的 reduce 算法也可以轻松处理不同长度的数组,因为所有内容都按索引位置配对,或者如果该位置尚未出现在每个键的组合结果中,则以其他方式创建。
唯一真正聚合框架可以处理这个问题的方法是已经将数据实际放在"associative array"中,或者至少将每个元素作为关联数组本身,像这样:
{
"_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
stage here by keeping the associative information. Using $group
并不能保证位置保持不变,但是对于关联信息,这并不是真正需要的。
所以这一切都取决于你能忍受什么。如果您想将普通数组保留在数据中,那么您将需要 mapReduce 来获取结果。但是如果你愿意改变数据格式,那么聚合方法是完全可以的。
然而,这可能只是一个很好的案例,在 "large scale" 时,mapReduce 进程将通过避免处理 $unwind
.
的开销来击败聚合进程
问这个问题之前查了很多资料,都没有找到相关资料
这里是:
我的问题是我有一个 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
}
请帮帮我,我简直是在拔头发。 :(
使用聚合框架确实没有明智的方法来做到这一点。这里的问题是跟踪数组元素的 "index" 位置,其中没有什么可以做到这一点。
你最好的选择是 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 }
}
)
这会生成所需的输出,尽管 mapReduce 总是生成具有 _id
和 value
:
{
"_id" : 1,
"value" : {
"plugin_count" : [
2,
1,
0,
7,
0,
11,
12,
0,
16,
22
]
}
}
请注意,这里的 reduce 算法也可以轻松处理不同长度的数组,因为所有内容都按索引位置配对,或者如果该位置尚未出现在每个键的组合结果中,则以其他方式创建。
唯一真正聚合框架可以处理这个问题的方法是已经将数据实际放在"associative array"中,或者至少将每个元素作为关联数组本身,像这样:
{
"_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
stage here by keeping the associative information. Using $group
并不能保证位置保持不变,但是对于关联信息,这并不是真正需要的。
所以这一切都取决于你能忍受什么。如果您想将普通数组保留在数据中,那么您将需要 mapReduce 来获取结果。但是如果你愿意改变数据格式,那么聚合方法是完全可以的。
然而,这可能只是一个很好的案例,在 "large scale" 时,mapReduce 进程将通过避免处理 $unwind
.