是否可以使用MongoDB'对多个列进行分组和求和;s的聚合框架?

是否可以使用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'

鉴于此MongoDB集合:

[
  { 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,
    }
  }
]