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

标签 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 分钟间隔分组的结果。

最佳答案

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

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

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

Warning - ObjectId values are limited to precision to the second only for the internal time value that makes up part of their data allowing the $toDate conversion. The actual inserted "time" is most probably dependent on the driver in use. Where precision is required, it's still recommended to use a discrete BSON Date field instead of relying on ObjectId values.

关于mongodb - 在 MongoDb 中按 15 分钟的时间间隔对结果进行分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26814427/

相关文章:

python - 如何将 pymongo.cursor.Cursor 转换为字典?

mongodb - 按匹配条件的嵌入数组元素的数量查找文档

javascript - MongoDB mapReduce 方法意外结果

node.js - 未捕获的验证错误; Mongoose 和 Mocha

mongodb - 我可以转换 MongoDB 文档以在数组中包含一个数组吗?

mongodb - 如何使用 MongoDB 的 Map/Reduce 对多个键进行分组?发出多个键?

MongoDB 过滤文档并仅返回匹配的子文档(如果存在),否则返回 null 或空数组或空对象

javascript - '数据库名称必须是一个字符串'连接我的连接字符串时出错

node.js - Mongoose:嵌套数组结构中的聚合查询

node.js - node-mongo-native 迁移框架