有几种方法可以做到这一点。
第一种方法是使用日期聚合运算符,它允许您分解文档中的“日期”值。 特别是针对“分组”作为主要意图:
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
分组之后,您可以在“分组运算符”部分放入任何其他内容。我只是使用基本的“count”示例,代替您真正想做什么的陈述。
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 }
}}
])
这样更短,并且不需要在定义管道时将外部BSON日期作为常量来定义“epoch”值,因此对于所有语言实现而言非常一致。
这些只是类型转换的两个“辅助”方法,它们都与$convert
方法相关,这是一种“较长”的实现形式,允许在转换时进行自定义处理以应对null
或错误情况。
通过这种转换方式,甚至可以从主键的ObjectId
中获取Date
信息,因为这将是一个可靠的“创建”日期来源:
db.collection.aggregate([
{ "$group": {
"_id": {
"$toDate": {
"$subtract": [
{ "$toLong": { "$toDate": "$_id" } },
{ "$mod": [ { "$toLong": { "$toDate": "$_id" } }, 1000 * 60 * 15 ] }
]
}
},
"count": { "$sum": 1 }
}}
])
因此,“casting types”通过这种转换可以成为一个非常强大的工具。
警告 - ObjectId
值仅限于内部时间值的精确到秒,这部分数据构成了它们的一部分,允许$toDate
转换。实际插入的“时间”很可能取决于正在使用的驱动程序。在需要精度的情况下,仍建议使用离散的BSON日期字段,而不是依赖于ObjectId
值。