那么,不要使用日期聚合运算符,而是应用“日期计算”来舍入日期对象。这通常是可取的,因为所有驱动程序都以一种常用于所有支持日期操作的语言的BSON日期形式表示:
db.datetest.aggregate([
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [ "$date", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$date", new Date(0) ] },
1000 * 60 * 60 * 24
]}
]},
new Date(0)
]
},
"click": { "$sum": 1 }
}}
])
如果问题中所暗示的分组间隔是15天的“桶”,那么只需将此应用于$mod
中的数值即可:
db.datetest.aggregate([
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [ "$date", new Date(0) ] },
{ "$mod": [
{ "$subtract": [ "$date", new Date(0) ] },
1000 * 60 * 60 * 24 * 15
]}
]},
new Date(0)
]
},
"click": { "$sum": 1 }
}}
])
基本的数学应用是,当你
$subtract
两个
Date
对象时,返回的结果将是毫秒差。因此,纪元由
Date(0)
表示为在任何语言构造函数中进行转换的基础。
使用数字值时,"模数" (
$mod
) 用于将日期舍入(从除法中减去余数)到所需的间隔。可以是:
1000 毫秒 x 60 秒 * 60 分钟 * 24 小时 = 1 天
或者
1000 毫秒 x 60 秒 * 60 分钟 * 24 小时 * 15 天 = 15 天
因此,它可以适应您需要的任何间隔。
同样,对于一个“数字”值和一个Date
对象之间的$add
操作将返回一个Date
对象,其等效于两个对象组合的毫秒值(纪元为0,因此0加上差异是转换后的日期)。
可以在以下列表中轻松表示并再现:
var now = new Date();
var bulk = db.datetest.initializeOrderedBulkOp();
for ( var x = 0; x < 60; x++ ) {
bulk.insert({ "date": new Date( now.valueOf() + ( 1000 * 60 * 60 * 24 * x ))});
}
bulk.execute();
并以15天为间隔运行第二个示例:
{ "_id" : ISODate("2016-04-14T00:00:00Z"), "click" : 12 }
{ "_id" : ISODate("2016-03-30T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-03-15T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-02-29T00:00:00Z"), "click" : 15 }
{ "_id" : ISODate("2016-02-14T00:00:00Z"), "click" : 3 }
根据运行列表时的当前日期,或类似的分布方式,当然,自纪元日期以来的15天间隔将保持一致。使用"Math"方法更容易调整,特别是如果您想要在聚合输出中为不同的时区调整时间段,在这种情况下,您可以通过添加/减去与UTC的数值差异来进行类似的数字调整。