【问题标题】:MongoDB aggregation group by queryMongoDB 聚合分组查询
【发布时间】:2020-05-13 05:15:19
【问题描述】:

我有一个 mongoDB 集合,我想做一个聚合查询。
我按alert_type 字段分组,但我还希望将那些alert_type 的列表作为输出中的单独字段。

集合看起来像这样:

db.test.insertMany([
  {
    "output_data": {
      "alert_type": "UAlert",
      "overallImpact": {
        "margin": 0.1,
        "workingCapital": 3.33
      }
    }
  },
  {
    "output_data": {
      "alert_type": "CAlert",
      "overallImpact": {
        "margin": 0.1,
        "workingCapital": 3.33
      }
    }
  },
  {
    "output_data": {
      "alert_type": "UAlert",
      "overallImpact": {
        "margin": 0.1,
        "workingCapital": 3.33
      }
    }
  }
])

我尝试过的查询

db.test.aggregate([
  {$group: {
      "_id": "$output_data.alert_type",
      "alert_type": {
        "$first": "$output_data.alert_type"
      },
      "margin": {
        "$sum": "$output_data.overallImpact.margin"
      },
      "workingCapital": {
        "$sum": "$output_data.overallImpact.workingCapital"
      },
      "alert_types": {
        "$addToSet": "$output_data.alert_type"
      }
    }
  },
  {$project: {'_id': 0
    }
  }
])

电流输出

{
  "alert_type": "UAlert",
  "margin": 0.2,
  "workingCapital": 6.66,
  "alert_types": [
    "UAlert"
  ]
}
{
  "alert_type": "CAlert",
  "margin": 0.1,
  "workingCapital": 3.33,
  "alert_types": [
    "CAlert"
  ]
}

所需输出

{
  "data": [
    {
      "alert_type": "UAlert",
      "margin": 0.2,
      "workingCapital": 6.66,
    },
    {
      "alert_type": "CAlert",
      "margin": 0.1,
      "workingCapital": 3.33,
    }
  ],
  "alert_types": [
    "UAlert",
    "CAlert"
  ]
}

谁能帮我解决这个问题?

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    您可以尝试以下聚合查询:

    db.collection.aggregate([
        {
          $group: {
            "_id": "$output_data.alert_type",
            alert_type: { $first: "$output_data.alert_type" },
            margin: { $sum: "$output_data.overallImpact.margin" },
            workingCapital: { $sum: "$output_data.overallImpact.workingCapital" }
          }
        },
        /** Optional stage - Just to exclude `_id` inside each object of data array from final output */
        {
          $project: { _id: 0 }
        },
        /** Grouping on all docs, For this group stage we will have lesser docs compared to prior Group stage */
        {
          $group: {
            _id: "", // Group without any condition
            data: {  $push: "$$ROOT" }, // Pushing all docs into an array
            alert_types: { $addToSet: "$alert_type" } // Adding unique values
          }
        },
        /** Optional stage - Just to exclude `_id` final output doc */
        {
          $project: { _id: 0 }
        }
      ])
    

    测试: mongoplayground

    【讨论】:

    • 感谢@whoami 的回答。这可能是一个比公认的更简单的解决方案,这也解决了我的问题。但我认为$facet 将在更改输入 JSON 的结构方面为我提供更大的灵活性。请纠正我我错了,因为我以前从未使用过$facet
    • @mukesh.kumar:$facet 的唯一问题是 facet 中的每个阶段都将在集合的整个数据集上执行!所以你需要谨慎,我发现在你的情况下不需要 - 我并不是说它不起作用,但就性能而言,我们对整个数据集进行了两次分组,但如果效果很好,那么酷:-)
    • 这是一个非常好的观点。我不知道$facet 将针对整个数据集执行。我需要用更多文件检查性能。感谢您的信息。 :)
    【解决方案2】:

    您必须使用$facet 来实现这一点,在一个阶段您执行分组阶段以获取数据,而在另一个阶段您可以找到所有可用的警报类型。

    db.collection.aggregate([
      {
        $facet: {
          data: [
            {
              $group: {
                "_id": "$output_data.alert_type",
                "alert_type": {
                  "$first": "$output_data.alert_type"
                },
                "margin": {
                  "$sum": "$output_data.overallImpact.margin"
                },
                "workingCapital": {
                  "$sum": "$output_data.overallImpact.workingCapital"
                },
              }
            },
            {
              $project: {
                "_id": 0
              }
            }
          ],
          "alert_types": [
            {
              $group: {
                _id: null,
                "names": {
                  "$addToSet": "$output_data.alert_type"
                }
              }
            }
          ]
        }
      },
      {
        $project: {
          data: 1,
          alert_types: "$alert_types.names"
        }
      }
    ]) 
    

    你可以测试一下here

    【讨论】:

    • 谢谢。我将不得不进一步了解$facet,因为这看起来是一个非常强大的工具。
    • 刚刚注意到这个查询给了我 alert_types 作为数组的数组。