MongoDB聚合,按值间隔分组,

时间:2018-04-17 09:12:41

标签: mongodb aggregation-framework

MongoDB文档:

[{
  _id: '123213',
  elevation: 2300,
  area: 25
},
{
  _id: '343221',
  elevation: 1600,
  area: 35,
},
{
  _id: '545322',
  elevation: 500
  area: 12,
},
{
  _id: '234234',
  elevation: null,
  area: 5
}]

我希望在给定的高程间隔上对这些进行分组并总结区域属性。

  • 组1:< 0
  • 第2组:0 - 1500
  • 第3组:1501 - 3000,
  • 第4组:> 3000

所以预期的输出是:

[{
   interval: '1501-3000',
   count: 2,
   summarizedArea: 60 
},
{
   interval: '0-1500',
   count: 1,
   summarizedArea: 12,
},
{
   interval: 'N/A',
   count: 1,
   summarizedArea: 5
}]

如果可能,我想使用aggregation pipeline

也许有$range的东西?或$gte$lte

的组合

2 个答案:

答案 0 :(得分:1)

您可以使用MongoDB 3.4中引入的$bucket来实现此目的:

db.collection.aggregate([
  {
    $bucket: {
      groupBy: "$elevation",
      boundaries: [
        0,
        1500,
        3000,
        5000
      ],
      default: 10000,
      output: {
        "count": {
          $sum: 1
        },
        "summarizedArea": {
          $sum: "$area"
        }
      }
    }
  }
])

输出:

[
  {
    "_id": 0,
    "count": 1,
    "summarizedArea": 12
  },
  {
    "_id": 1500,
    "count": 2,
    "summarizedArea": 60
  },
  {
    "_id": 10000,
    "count": 1,
    "summarizedArea": 5
  }
]

你可以在这里试试:mongoplayground.net/p/xFe7ZygMqaY

答案 1 :(得分:1)

正如Feliix建议$bucket应该做的那样,但边界应该略有不同,以便与负值和N/A值一起使用:

db.collection.aggregate([
  {
    $bucket: {
      groupBy: "$elevation",
      boundaries: [ -Number.MAX_VALUE, 0, 1501, 3001, Number.POSITIVE_INFINITY ],
      default: Number.NEGATIVE_INFINITY,
      output: {
        "count": { $sum: 1 },
        "summarizedArea" : { $sum: "$area" }
      }
    }
  }
])

可以将下面的格式化阶段添加到管道中以调整响应的形状:

  { $group: {
      _id: null,
      documents: { $push: {          
          interval: { $let: {
              vars: {
                  idx: { $switch: {
                      branches: [
                          { case: { $eq: [ "$_id", -Number.MAX_VALUE ] }, then: 3 },
                          { case: { $eq: [ "$_id", 0 ] }, then: 2 },
                          { case: { $eq: [ "$_id", 1501 ] }, then: 1 },
                          { case: { $eq: [ "$_id", 3001 ] }, then: 0 }
                      ],
                      default: 4
                  } }
              },
              in: { $arrayElemAt: [ [ ">3000", "1501-3000", "0-1500", "<0", "N/A" ], "$$idx" ] } 
          } },
          count: "$count",
          summarizedArea: "$summarizedArea"
      } }
  } }

$group _id: null $push将所有群组转换为单个文档的数组。

$let将前一阶段的$_id映射到数组[ ">3000", "1501-3000", "0-1500", "<0", "N/A" ]中定义的区间的文本标签。为此,它使用$switch计算标签的idx索引。

在应用程序级别实现逻辑必须更简单,除非你绝对需要在管道中执行它。