在 MongoDB 中进行聚合分组
Grouping With aggregation in MongoDB
目前我在 MongoDB 中使用聚合。在我的 collections 中有一个带有省份和宗教的字段。我正在做这个
const data = await submit.aggregate([
{ "$group": { _id: { province: "$province" ,religion:"$religion"}, count: { $sum: 1 } } },
])
我的输出看起来像这样:
[
{ _id: { religion: 'a', province: 'aa' }, count: 1 },
{ _id: { religion: b, province: 'bb' }, count: 2 },
{ _id: { religion: 'c', province: 'bb'}, count: 2 },
{ _id: { religion: 'd', province: 'cc' }, count: 1 }
]
预期输出:
[
{ _id: { religion: 'a ' }, count: 1 },
{ _id: { religion: 'a' }, count: 1 },
{ _id: { religion: null }, count: 6 },
{ _id: { religion: 'c' }, count: 1 },
{ _id: { religion: 'd' }, count: 2 },
{ _id: { religion: 'e' }, count: 6 },
{ _id: { religion: 'f' }, count: 15 },
{ _id: { religion: 'g' }, count: 2 },
] [
{ _id: { province: 'aa' }, count: 19 },
{ _id: { province: 'bb' }, count: 2 },
{ _id: { province: 'cc' }, count: 21 },
]
您同时寻找 2 个不同的 $group
-- 这正是 $facet
的用途。将 $facet
想象成“multi-group”。给定类似于以下的输入集:
{ religion: 'a', province: 'aa' },
{ religion: 'b', province: 'aa' },
{ religion: 'c', province: 'aa' },
{ religion: 'c', province: 'bb' },
{ religion: 'd', province: 'bb' },
{ religion: 'e', province: 'cc' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'cc' }
然后这个管道:
db.foo.aggregate([
{$facet: {
"by_religion": [
{$group: {_id: '$religion', N:{$sum:1}}}
],
"by_province": [
{$group: {_id: '$province', N:{$sum:1}}}
],
}}
]);
产生这个输出:
{
"by_religion" : [
{
"_id" : "b",
"N" : 1
},
{
"_id" : "e",
"N" : 1
},
{
"_id" : "d",
"N" : 1
},
{
"_id" : "a",
"N" : 1
},
{
"_id" : "f",
"N" : 4
},
{
"_id" : "c",
"N" : 2
}
],
"by_province" : [
{
"_id" : "bb",
"N" : 2
},
{
"_id" : "cc",
"N" : 2
},
{
"_id" : "aa",
"N" : 6
}
]
}
OP 试图通过做一些 data-as-LVAL 处理来进一步优化输出,虽然这通常被认为是一种糟糕的设计实践,但它有某些有用的应用程序。在$facet
之后添加这个阶段:
,{$project: {
// Reading this from insider-out:
// We use $map to turn the array of objects:
// [ {_id:'d',N:1},{_id:'f',N:4}, ... ]
// into an array of K-v pairs (array of array):
// [ ['d',1] , ['f',4] , ... ]
// That sets us up for $arrayToObject which will take
// that array of arrays and turn it into an object:
// {'d':1, 'f':4, ... }
// The target field name is the same as the input so
// we are simply overwriting the field.
"by_religion": {$arrayToObject: {$map: {
input: '$by_religion',
in: [ '$$this._id', '$$this.N' ]
}}
},
"by_province": {$arrayToObject: {$map: {
input: '$by_province',
in: [ '$$this._id', '$$this.N' ]
}}
}
}}
产生:
{
"by_religion" : {
"d" : 1,
"b" : 1,
"c" : 2,
"f" : 4,
"a" : 1,
"e" : 1
},
"by_province" : {
"bb" : 2,
"cc" : 2,
"aa" : 6
}
}
lval/rval 后处理的一个变体使用这个 $project
而不是上面的那个:
,{$project: {
"by_religion": {$map: {
input: '$by_religion',
in: {$arrayToObject: [ [{k:'$$this._id',v:'$$this.N'}] ]}
}},
"by_province": {$map: {
input: '$by_province',
in: {$arrayToObject: [ [{k:'$$this._id',v:'$$this.N'}] ]}
}},
}}
产生一个数组:
{
"by_religion" : [
{"b" : 1},
{"c" : 2},
{"a" : 1},
{"f" : 4},
{"d" : 1},
{"e" : 1}
],
"by_province" : [
{"cc" : 2},
{"aa" : 6},
{"bb" : 2}
]
}
目前我在 MongoDB 中使用聚合。在我的 collections 中有一个带有省份和宗教的字段。我正在做这个
const data = await submit.aggregate([
{ "$group": { _id: { province: "$province" ,religion:"$religion"}, count: { $sum: 1 } } },
])
我的输出看起来像这样:
[
{ _id: { religion: 'a', province: 'aa' }, count: 1 },
{ _id: { religion: b, province: 'bb' }, count: 2 },
{ _id: { religion: 'c', province: 'bb'}, count: 2 },
{ _id: { religion: 'd', province: 'cc' }, count: 1 }
]
预期输出:
[
{ _id: { religion: 'a ' }, count: 1 },
{ _id: { religion: 'a' }, count: 1 },
{ _id: { religion: null }, count: 6 },
{ _id: { religion: 'c' }, count: 1 },
{ _id: { religion: 'd' }, count: 2 },
{ _id: { religion: 'e' }, count: 6 },
{ _id: { religion: 'f' }, count: 15 },
{ _id: { religion: 'g' }, count: 2 },
] [
{ _id: { province: 'aa' }, count: 19 },
{ _id: { province: 'bb' }, count: 2 },
{ _id: { province: 'cc' }, count: 21 },
]
您同时寻找 2 个不同的 $group
-- 这正是 $facet
的用途。将 $facet
想象成“multi-group”。给定类似于以下的输入集:
{ religion: 'a', province: 'aa' },
{ religion: 'b', province: 'aa' },
{ religion: 'c', province: 'aa' },
{ religion: 'c', province: 'bb' },
{ religion: 'd', province: 'bb' },
{ religion: 'e', province: 'cc' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'aa' },
{ religion: 'f', province: 'cc' }
然后这个管道:
db.foo.aggregate([
{$facet: {
"by_religion": [
{$group: {_id: '$religion', N:{$sum:1}}}
],
"by_province": [
{$group: {_id: '$province', N:{$sum:1}}}
],
}}
]);
产生这个输出:
{
"by_religion" : [
{
"_id" : "b",
"N" : 1
},
{
"_id" : "e",
"N" : 1
},
{
"_id" : "d",
"N" : 1
},
{
"_id" : "a",
"N" : 1
},
{
"_id" : "f",
"N" : 4
},
{
"_id" : "c",
"N" : 2
}
],
"by_province" : [
{
"_id" : "bb",
"N" : 2
},
{
"_id" : "cc",
"N" : 2
},
{
"_id" : "aa",
"N" : 6
}
]
}
OP 试图通过做一些 data-as-LVAL 处理来进一步优化输出,虽然这通常被认为是一种糟糕的设计实践,但它有某些有用的应用程序。在$facet
之后添加这个阶段:
,{$project: {
// Reading this from insider-out:
// We use $map to turn the array of objects:
// [ {_id:'d',N:1},{_id:'f',N:4}, ... ]
// into an array of K-v pairs (array of array):
// [ ['d',1] , ['f',4] , ... ]
// That sets us up for $arrayToObject which will take
// that array of arrays and turn it into an object:
// {'d':1, 'f':4, ... }
// The target field name is the same as the input so
// we are simply overwriting the field.
"by_religion": {$arrayToObject: {$map: {
input: '$by_religion',
in: [ '$$this._id', '$$this.N' ]
}}
},
"by_province": {$arrayToObject: {$map: {
input: '$by_province',
in: [ '$$this._id', '$$this.N' ]
}}
}
}}
产生:
{
"by_religion" : {
"d" : 1,
"b" : 1,
"c" : 2,
"f" : 4,
"a" : 1,
"e" : 1
},
"by_province" : {
"bb" : 2,
"cc" : 2,
"aa" : 6
}
}
lval/rval 后处理的一个变体使用这个 $project
而不是上面的那个:
,{$project: {
"by_religion": {$map: {
input: '$by_religion',
in: {$arrayToObject: [ [{k:'$$this._id',v:'$$this.N'}] ]}
}},
"by_province": {$map: {
input: '$by_province',
in: {$arrayToObject: [ [{k:'$$this._id',v:'$$this.N'}] ]}
}},
}}
产生一个数组:
{
"by_religion" : [
{"b" : 1},
{"c" : 2},
{"a" : 1},
{"f" : 4},
{"d" : 1},
{"e" : 1}
],
"by_province" : [
{"cc" : 2},
{"aa" : 6},
{"bb" : 2}
]
}