在MongoDb中按15分钟的时间间隔对结果进行分组

Hei*_*tet 41 mongodb mongodb-query aggregation-framework

我有一个像这个结构的"状态"集合 -

{
    _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")
}
....
....
Run Code Online (Sandbox Code Playgroud)

我需要从该集合中获得15分钟间隔的结果.

Nei*_*unn 109

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

第一个是日期聚合运算符,它允许您剖析文档中的"日期"值.特别是"分组"作为主要意图:

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 }
  }}
])
Run Code Online (Sandbox Code Playgroud)

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

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 }
    }}
])
Run Code Online (Sandbox Code Playgroud)

因此,它取决于您希望分组间隔的输出格式类型.两者基本上代表相同的东西,并有足够的数据重新构建为代码中的"日期"对象.

在分组后,您可以在"分组运算符"部分中放置任何其他内容_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 }
  }}
])
Run Code Online (Sandbox Code Playgroud)

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

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

甚至可以通过这种转换DateObjectId主键获取信息,因为这将是"创建"日期的可靠来源:

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

因此,使用这种转换的"投射类型"可以是非常强大的工具.

  • 太糟糕了,我无法接受他 - 真的很有用的答案! (10认同)
  • 我完全同意@Petrov (2认同)
  • 感谢您提供这些好的解决方案!我想你的第一个例子可能会有一个小错误.你错过了按小时分组(为了检索15分钟的间隔 - 我认为 - 应该按小时).所以你需要在`dayOfYear`行之后加上`"hour":{"$ hour":"$ created_at"},` (2认同)
  • @AnthonyWinzlet,他于2018年4月26日编辑了答案。 (2认同)

Bla*_*ven 15

我喜欢这里的另一个答案,主要是使用日期数学而不是聚合日期操作符,虽然有用也可能有点模糊.

我想在这里添加的唯一内容是,您也可以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 }
    }}
])
Run Code Online (Sandbox Code Playgroud)

Date(0)JavaScript中contructs这里表示在更短的形式相同的"时期"日期,如从历元0毫秒是划时代.但主要的一点是,当使用数字标识符完成对另一个BSON日期对象的"添加"时,所描述条件的反转为真,最终结果实际为现在为a Date.

所有驱动程序都会Date通过此方法将本机类型返回到其语言.


Ser*_*kiy 8

另一个有用的方法:

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

对于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" } },
  }},
])
Run Code Online (Sandbox Code Playgroud)


小智 7

mongo db.version()<3.0更美观一点

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}}
])
Run Code Online (Sandbox Code Playgroud)


小智 6

MongoDB 5.x 及更高版本

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

{
  $group: {
   "_id": { "$dateTrunc": { date: "$created_at", unit: "minute", binSize: 15 } },
   "count" : { $sum: 1 }
  }
},
Run Code Online (Sandbox Code Playgroud)

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