【问题标题】:Group result by 15 minutes time interval in MongoDb在 MongoDb 中按 15 分钟的时间间隔对结果进行分组
【发布时间】:2015-01-05 00:33:06
【问题描述】:

我有一个像这样的结构的“状态”集合 -

{
    _id: ObjectId("545a0b63b03dbcd1238b4567"),
    status: 1004,
    comment: "Rem dolor ipsam placeat omnis non. Aspernatur nobis qui nisi similique.",
    created_at: ISODate("2014-11-05T11:34:59.804Z")
},
{
    _id: ObjectId("545a0b66b03dbcd1238b4568"),
    status: 1001,
    comment: "Sint et eos vero ipsa voluptatem harum. Hic unde voluptatibus et blanditiis quod modi.",
    created_at: ISODate("2014-11-05T11:35:02.814Z")
}
....
....

我需要从该集合中获取按 15 分钟间隔分组的结果。

【问题讨论】:

  • 所提供的答案中的内容是否不清楚或不适用于您的情况?注意到它仍然不被接受。
  • 别费心了,他已经回答过了,何必费心接受回答呢。

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

有几种方法可以做到这一点。

第一个是Date Aggregation Operators,它允许您剖析文档中的“日期”值。专门针对“分组”作为主要意图:

db.collection.aggregate([
  { "$group": {
    "_id": {
      "year": { "$year": "$created_at" },
      "dayOfYear": { "$dayOfYear": "$created_at" },
      "hour": { "$hour": "$created_at" },
      "interval": {
        "$subtract": [ 
          { "$minute": "$created_at" },
          { "$mod": [{ "$minute": "$created_at"}, 15] }
        ]
      }
    }},
    "count": { "$sum": 1 }
  }}
])

第二种方法是使用一个小技巧,即从另一个日期对象中减去一个日期对象(或其他直接数学运算),然后结果是一个表示两个对象之间的纪元时间戳毫秒的数值。因此,只需使用纪元日期即可获得纪元毫秒表示。然后使用日期数学作为间隔:

db.collection.aggregate([
    { "$group": {
        "_id": {
            "$subtract": [
                { "$subtract": [ "$created_at", new Date("1970-01-01") ] },
                { "$mod": [ 
                    { "$subtract": [ "$created_at", new Date("1970-01-01") ] },
                    1000 * 60 * 15
                ]}
            ]
        },
        "count": { "$sum": 1 }
    }}
])

所以这取决于你想要分组间隔的输出格式。两者基本上代表相同的事物,并且有足够的数据在您的代码中重新构造为“日期”对象。

您可以在分组_id 之后的“分组运算符”部分中添加任何其他内容。我只是使用基本的“计数”示例来代替你自己关于你真正想做的任何真实陈述。


MongoDB 4.x 及更高版本

自最初编写以来,日期聚合运算符添加了一些内容,但从 MongoDB 4.0 开始,将有实际的“真正的类型转换”,而不是此处使用 BSON 日期转换完成的基本数学技巧。

例如,我们可以在这里使用$toLong$toDate 作为新的助手:

db.collection.aggregate([
  { "$group": {
    "_id": {
      "$toDate": {
        "$subtract": [
          { "$toLong": "$created_at" },
          { "$mod": [ { "$toLong": "$created_at" }, 1000 * 60 * 15 ] }
        ]
      }
    },
    "count": { "$sum": 1 }
  }}
])

这有点短,并且不需要将“epoch”值的外部 BSON 日期定义为定义管道时的常量,因此它对于所有语言实现都非常一致。

这些只是类型转换的“辅助”方法中的两个,它们都与$convert 方法相关联,这是一种“更长”的实现形式,允许对null 进行自定义处理或转换错误。

甚至可以通过这种转换从主键的ObjectId 中获取Date 信息,因为这将是“创建”日期的可靠来源:

db.collection.aggregate([
  { "$group": {
    "_id": {
      "$toDate": {
        "$subtract": [
          { "$toLong": { "$toDate": "$_id" }  },
          { "$mod": [ { "$toLong": { "$toDate": "$_id" } }, 1000 * 60 * 15 ] }
        ]
      }
    },
    "count": { "$sum": 1 }
  }}
])

因此,使用这种转换的“转换类型”可能是非常强大的工具。

警告 - ObjectId 值仅限于精确到 ,仅适用于构成其数据的一部分的内部时间值,允许 $toDate 转换。实际插入的“时间”很可能取决于使用的驱动程序。在需要 精度 的地方,仍然建议使用离散的 BSON 日期字段,而不是依赖 ObjectId 值。

【讨论】:

  • 太糟糕了,我不能接受他 - 非常有用的答案!
  • 我完全同意@Petrov
  • 感谢您提供这些好的解决方案!我认为您的第一个示例中可能存在一个小错误。你错过了按小时分组(为了检索 15 分钟的间隔——我假设——应该按小时)。所以你需要在dayOfYear-line 之后添加"hour": { "$hour": "$created_at" },
  • Mongodb 4.0 已于 2018 发布,您知道 2014 的这些聚合...如何???
  • @AnthonyWinzlet,他在 2018 年 4 月 26 日编辑了他的答案。
【解决方案2】:

我喜欢这里的另一个答案,主要是为了使用日期数学而不是聚合日期运算符,这虽然有帮助,但也可能有点晦涩。

我想在这里添加的唯一一件事是,您还可以通过这种方法从聚合框架返回一个Date 对象,而不是作为结果的“数字”时间戳。使用$add,只是在相同原理上进行了一点额外的数学运算:

db.collection.aggregate([
    { "$group": {
        "_id": {
            "$add": [
                { "$subtract": [
                    { "$subtract": [ "$current_date", new Date(0) ] },
                    { "$mod": [ 
                        { "$subtract": [ "$current_date", new Date(0) ] },
                        1000 * 60 * 15
                    ]}
                ] },
                new Date(0)
            ]
        },
        "count": { "$sum": 1 }
    }}
])

这里 JavaScript 中的 Date(0) 结构以较短的形式表示相同的“纪元”日期,因为从纪元开始的 0 毫秒就是纪元。但要点是,当使用数字标识符对另一个 BSON 日期对象进行“添加”时,所描述的条件的反面为真,最终结果现在实际上是 Date

所有驱动程序都会通过这种方法将原生 Date 类型返回到他们的语言。

【讨论】:

    【解决方案3】:

    对 mongo db.version()

    db.collection.aggregate([
        {$match: {created_at:{$exists:1}}},
        {$group: {
            _id: {$add:[
                {$dayOfYear: "$created_at" },
                {$multiply: [{$year: "$created_at"}, 1000]}
            ]},
            count: {$sum: 1 }
        }},
        {$sort:{_id:-1}}
    ])
    

    【讨论】:

      【解决方案4】:

      另一个有用的方法:

      db.collection.aggregate([
        {$group: {
          _id: { 
            overallTime: { 
              $dateToString: { format: "%Y-%m-%dT%H", date: "$created_at" } 
            },
            interval: { $trunc: { $divide: [{ $minute: "$created_at" }, 15 ]}}
          },
        }},
      ])
      

      minhourday 间隔更容易:

      var format = "%Y-%m-%dT%H:%M"; // 1 min
      var format = "%Y-%m-%dT%H"; // 1 hour
      var format = "%Y-%m-%d"; // 1 day
      
      db.collection.aggregate([
        {$group: {
          _id: { $dateToString: { format: format, date: "$created_at" } },
        }},
      ])
      

      【讨论】:

        【解决方案5】:

        @Neil Lunn 在https://stackoverflow.com/a/26814496/8474325 对 MongoDb 4.x 以上的回答非常棒。但是代码中有一个小错误,他使用 ObjectId 进行聚合。必须将行 { "$toDate": "_id" } 更改为 { "$toDate": "$_id" } 才能使代码正常工作。

        这是更正后的代码。

        db.collection.aggregate([
            { "$group": {
              "_id": {
                  "$toDate": {
                      "$subtract": [
                          { "$toLong": { "$toDate": "$_id" }  },
                          { "$mod": [ { "$toLong": { "$toDate": "$_id" } }, 1000 * 60 * 15 ] }
                      ]
                  }
              },
              "count": { "$sum": 1 }
           }}
        ])
        

        【讨论】:

          【解决方案6】:

          MongoDB 5.x 及更高版本

          现在在聚合管道中支持日期截断,例如:

          {
            $group: {
             "_id": { "$dateTrunc": { date: "$created_at", unit: "minute", binSize: 15 } },
             "count" : { $sum: 1 }
            }
          },
          

          您还可以找到有关窗口函数和 dateTrunc here的有用信息

          【讨论】:

          • { $count: {} } 而不是{ $sum: 1 }
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-02-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多