在 MongoDb 中按 15 分钟的时间间隔对结果进行分组
Posted
技术标签:
【中文标题】在 MongoDb 中按 15 分钟的时间间隔对结果进行分组【英文标题】:Group result by 15 minutes time interval in MongoDb 【发布时间】: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 分钟间隔分组的结果。
【问题讨论】:
所提供的答案中的内容是否不清楚或不适用于您的情况?注意到它仍然不被接受。 别费心了,他已经回答过了,何必费心接受回答呢。 【参考方案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 ]
,
,
])
min、hour、day 间隔更容易:
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://***.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
以上是关于在 MongoDb 中按 15 分钟的时间间隔对结果进行分组的主要内容,如果未能解决你的问题,请参考以下文章
mongoDB:$dateToString 格式以 15 分钟间隔显示时间
mongoDB:$dateToString 格式以 15 分钟间隔显示时间