Mongodb聚合,如何按间隔标准计算文件?

时间:2014-02-23 07:48:47

标签: mongodb aggregation-framework

我的MongoDB文档如下所示:

{StatCode : "...", LoadTime  : "..."}

例如,数据可能如下所示:

+-----+----------+------------+
| _id | StatCode |  LoadTime  |
+-----+----------+------------+
|   1 |      200 |      0,345 |
|   2 |      200 |      0,234 |
|   3 |      200 |      0,396 |
|   4 |      200 |      1,234 |
|   5 |      200 |      2,564 |
|   6 |      200 |      0,437 |
|   7 |      301 |      0,523 |
|   8 |      301 |      0,628 |
|   9 |      301 |      0,712 |
|  10 |      200 |      1,784 | 
+-----+----------+------------+

我希望通过LoadTime值获取计数组,如下所示: 最慢(超过2),慢(1和2之间),中(低于0.5和1)和快(低于0.5)

使用Data示例,结果如下所示:

+----------+-------+
| Info_id  | Count |
+----------+-------+
| Slowest  |     1 |
| Slow     |     2 |
| Medium   |     3 |
| Fast     |     4 |
+----------+-------+

编辑:备注Neil Lunn,来自MongoDB的示例文档

{
    "_id" : 1,
    "LoadTime" : NumberLong(345),
    "StatCode" : 200
}
{
    "_id" : 2,
    "LoadTime" : NumberLong(234),
    "StatCode" : 200
}
....
{
    "_id" : 9,
    "LoadTime" : NumberLong(712),
    "StatCode" : 301
}
{
    "_id" : 10,
    "LoadTime" : NumberLong( 1784),
    "StatCode" : 200
}

如何使用MongoDB聚合框架实现此目的?

1 个答案:

答案 0 :(得分:5)

您需要的是$cond运算符以及$and的多个嵌套条件。但这应该可以满足您的需求。

db.collection.aggregate([
    {"$group": {
      "_id": {"$cond": [
          {"$gte": ["$LoadTime", 2000] },
          "Slowest",                                   // return "Slowest" where true
          {"$cond": [
              {"$and": [
                  {"$lt": ["$LoadTime", 2000] },
                  {"$gte": ["$LoadTime", 1000] }
              ]},
              "Slow",                                  // then "Slow" here where true
              {"$cond": [
                  {"$and": [
                      {"$lt": ["$LoadTime", 1000] },
                      {"$gte": ["$LoadTime", 500 ] }
                  ]},
                  "Medium",                            // then "Medium" where true
                  "Fast"                               // and finally "Fast" < 500
              ]}
          ]}
      ]},
      "count": {"$sum": 1}
    }},
    {"$sort": { "count": 1 }}
])

由于您的时间整个毫秒,您可以看到我要求编辑的原因。

因为 $ cond 是一个ternary运算符,它需要三个参数:

  • 要评估的条件,返回布尔
  • 条件为 true
  • 的返回值
  • 条件为 false
  • 的返回值

因此,我们的想法是你嵌套整个条件,转到 false 上的 next 测试,直到找到匹配的条件,以及要返回的值。

$和部分是要包含的条件的数组。这为您提供了范围。所以在最长的部分:

          {"$cond": [                             // Evaluate here
              {"$and": [                          // Within the range of the next 2
                  {"$lt": ["$LoadTime", 2000] },
                  {"$gte": ["$LoadTime", 1000] }
              ]},
              "Slow",                            // true condition - return
              {"$cond": [                        // false - move to next eval

在{500}的情况下,times为“快速”留下了“快速”。

这些keys中的每一个都会发送到该组,我们只需{ $sum: 1 }即可将它们组合在一起进行计数。

如果您需要在自己的语言实现中使用

中的整个pipeline内容
  

aggregate(..)

只是JSON,所以你可以将它解析为你的原生数据结构,如果手工翻译让你失望,或者像我这样只是懒惰。

修改

由于评论,似乎有必要解释所提出查询的表单。所以这里是编辑附录以供澄清。

学习使用聚合管道,并且确实良好实践用于写出和测试一系列复杂的阶段或逻辑时,我通过一次执行一部分来发现可视化结果非常有用。因此,在编写此类内容的情况下,我的第一个步骤如下:

db.collection.aggregate([
    {"$group": {
      "_id": {"$cond": [
          {"$gte": ["$LoadTime", 2000] },
          "Slowest",
          null
       ]}
    }}
])

现在,这会让我按照我的预期计算“最慢”,然后其他所有内容转移到null。所以到目前为止我有一个阶段可以看到结果。但是当测试时,我会在继续构建链之前实际做这样的事情:

db.collection.aggregate([
    {"$group": {
      "_id": {"$cond": [
          {"$and": [
              {"$lt": ["$LoadTime", 2000] },
              {"$gte": ["$LoadTime", 1000] }
          ]},
          "Slow",
          null
      ]}
    }}
])

所以我只是将“慢”( 2000和1000之间)的结果与null桶中的其他所有内容相比较。所以我的总体数量保持不变。

最终查询中,正如所指出的那样,在嵌套的ternary条件中,第一个阶段已经已经 评估false 下一个运算符正在测试的项目。这意味着它们 大于已经在第一个阶段中测试过的值,并且无需测试该条件所以可以写成如下:

db.collection.aggregate([
    {"$group": {
      "_id": {"$cond": [
          {"$gte": ["$LoadTime", 2000] },       // Caught everything over 2000
          "Slowest",
          {"$cond": [
              {"$gte": ["$LoadTime", 1000] }    // Catch things still over 1000
              "Slow",
              {"$cond": [                       // Things under 1000 go here

              // and so on

短路评估,因为没有真实需要测试那些无法通过下一个逻辑条件的事情。

纯粹 视觉原因以及剪切和粘贴逻辑的纯粹懒惰,我们最终使用 $和条件换行范围。但是对于那些不习惯 ternary形式的使用,有一个清晰的视觉提示,在这个阶段匹配的结果将落在 2000ms1000ms的值,依此类推,这就是您希望在每个范围内获得的结果。

正如我所说,由于逻辑的工作方式不必要,但 是一个开发阶段,并且清晰给尚未的人>了解使用 $ cond 提供的ternary表单。