是否可以使用MongoDB'对多个列进行分组和求和;s的聚合框架?
鉴于此MongoDB集合:是否可以使用MongoDB'对多个列进行分组和求和;s的聚合框架?,mongodb,aggregation-framework,Mongodb,Aggregation Framework,鉴于此MongoDB集合: [ { character: 'broquaint', race: 'Halfling', class: 'Hunter' }, { character: 'broquaint', race: 'Halfling', class: 'Hunter' }, { character: 'broquaint', race: 'Halfling', class: 'Rogue' }, { character: 'broquaint', race: 'Naga'
[
{ character: 'broquaint', race: 'Halfling', class: 'Hunter' },
{ character: 'broquaint', race: 'Halfling', class: 'Hunter' },
{ character: 'broquaint', race: 'Halfling', class: 'Rogue' },
{ character: 'broquaint', race: 'Naga', class: 'Fighter' },
{ character: 'broquaint', race: 'Naga', class: 'Hunter' }
]
我想得到每一个种族和等级的计数,即
{
race: { 'Halfling': 3, 'Naga': 2 },
class: { 'Hunter': 3, 'Rogue': 1, 'Fighter': 1 }
}
我一直在尝试使用聚合框架(来
替换现有的映射/减少),但只能达到
作为组合的计数,即
{ '_id': { race: 'Halfling', class: 'Hunter' }, count: 2 }
{ '_id': { race: 'Halfling', class: 'Rogue' } count: 1 }
{ '_id': { race: 'Naga', class: 'Fighter' }, count: 1 }
{ '_id': { race: 'Naga', class: 'Hunter' }, count: 1 }
这非常简单,可以通过编程方式将
结果,但我希望能把这个留给MongoDB
以下是我目前掌握的代码供参考:
db.games.aggregate(
{ '$match': { character: 'broquaint' } },
{
'$group': {
_id: { race: '$race', background: '$background'},
count: { '$sum': 1 }
}
}
)
因此,问题是——给定示例集合,我可以得出我的结论吗
纯粹通过MongoDB的聚合框架获得所需的输出
对于可能提供的任何帮助,请提前表示感谢 是的,您可以使用聚合框架来实现这一点。它不会很漂亮,但仍然会比使用mapreduce快得多 简而言之(输出的格式与您给出的格式不同,但内容相同):
由于MongoDB 3.4,使用带有
$facet
的多个聚合管道可以更简单地实现这一点
摘自:
$facet
在服务器上的单个阶段内处理多个聚合管道
同一组输入文档。每个子管道都有自己的字段
输出文档,其结果存储为
文件
因此,对于您的用例,这将通过以下方式实现:
const aggregatorOpts = [
{ $match: { character: 'broquaint' } }, // Match the character
{
// Seperate into 2 or more pipes that will count class and
// race seperatly
$facet: {
race: [
// Group by race and get the count:
// [
// {
// _id: 'Halfling',
// count: 3
// }
// {
// _id: 'Naga',
// count: 2
// }
// ]
// $sortByCount is the same as
// { $group: { _id: <expression>, count: { $sum: 1 } } },
// { $sort: { count: -1 } }
{ $sortByCount: '$race' },
// Now we want to transform the array in to 1 document,
// where the '_id' field is the key, and the 'count' is the value.
// To achieve this we will use $arrayToObject. According the the
// docs, we have to first rename the fields to 'k' for the key,
// and 'v' for the value. We use $project for this:
{
$project: {
_id: 0,
k: '$_id',
v: '$count',
},
},
],
// Same as above but for class instead
class: [
{ $sortByCount: '$class' },
{
$project: {
_id: 0,
k: '$_id',
v: '$count',
},
},
],
},
},
{
// Now apply the $arrayToObject for both class and race.
$addFields: {
// Will override the existing class and race arrays
// with their respective object representation instead.
class: { $arrayToObject: '$class' },
race: { $arrayToObject: '$race' },
},
},
];
db.races.aggregate(aggregatorOpts)
[
{
"race": {
"Halfling": 3,
"Naga": 2
},
"class": {
"Hunter": 3,
"Rogue": 1,
"Fighter": 1,
}
}
]
如果您对@Asya提供的输出格式感到满意,那么您可以删除$project
和$addFields
阶段,只需在每个子管道中保留$sortByCount
部分
有了这些新功能,通过增加计数,聚合更容易扩展,
只需在$facet
中添加另一个聚合管道。
统计子组甚至更容易一些,但这将是一个单独的问题。什么是“$background”?类?很抱歉,在代码中应该是“类”而不是“背景”,列名实际上是“背景”,但我选择了“类”为了简洁起见,我只是在一致性方面失败了。如果你知道可能的种族和职业的完整集合,可能会有不同的答案-在这种情况下,很容易生成你问题中的确切格式。当我终于准备好发布答案时,我看到你已经做到了。。。再快40分钟。。。而且少了两个命令只要叫我聚合框架大师就行了。呃,我是说女主人。如果可以用它来做。。。我可能已经做过了。非常感谢你,效果非常好,@AsyaKamsky!现在我不得不去做一件令人羡慕的事情:了解它是如何工作的:)@AsyaKamsky:它回答了这个问题,但这真的是个好主意吗?这似乎效率很低。实际上,运行两个简单的$group聚合不是更好吗?我无法想象使用这种技术在计数中增加第三或第四个值。
[
{
"race": {
"Halfling": 3,
"Naga": 2
},
"class": {
"Hunter": 3,
"Rogue": 1,
"Fighter": 1,
}
}
]