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

时间:2014-11-08 06:18:08

标签: 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")
}
....
....

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

5 个答案:

答案 0 :(得分:99)

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

第一个是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 }
    }}
])

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

您可以在"分组运算符"中添加您想要的任何其他内容。分组_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上进行自定义处理或转换错误的实现形式。

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

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

所以"铸造类型"这种转换可以是非常强大的工具。

  

警告 - ObjectId值仅限于的精确度,仅适用于构成其数据一部分的内部时间值{{3转换。实际插入"时间"很可能取决于使用中的驱动程序。如果需要精度,仍建议使用离散的BSON日期字段,而不是依赖ObjectId值。

答案 1 :(得分: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 }
    }}
])

此处JavaScript中的Date(0)构造代表相同的" epoch"日期为较短的形式,距离纪元0毫秒是时代。但主要的一点是,当"添加"使用数字标识符完成另一个BSON日期对象,然后所描述条件的反转为真,最终结果实际为Date

所有驱动程序都会通过此方法将原生Date类型返回其语言。

答案 2 :(得分: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}}
])

答案 3 :(得分:3)

另一种有用的方式:

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

min 小时间隔更容易:

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" } },
  }},
])

答案 4 :(得分:1)

@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 }
   }}
])