聚合查询调整时区日期
Aggregation Query adjusting dates for timezone
我想运行这个mongodb查询使用java代码。
查询:
db.log.aggregate([
{ $match : {
"ts" : {
$gte: ISODate("2015-07-31T18:30:00.000Z"),
$lt: ISODate("2015-08-01T18:30:00.000Z")
},
"dup": {$exists:false}
}},
{ $project : {
'lts': {
'$add': ['$ts',5.5*3600*1000]
}
}},
{ $group : {
_id : {
day: { $dayOfMonth: "$lts" },
month: { $month: "$lts" },
year: { $year: "$lts" }
},
count: { $sum: 1 }
}}
])
我已经尝试使用此代码,但它不起作用
DBObject query = QueryBuilder.start().put("ts").greaterThanEquals(startdate).lessThanEquals(enddate).and("dup").exists(false).get();
DBObject match = new BasicDBObject("$match", query);
DBObject project=new BasicDBObject("$project", new BasicDBObject("ts",new BasicDBObject("$add",5.5*3600*1000)));
DBObject group = new BasicDBObject("$group", new BasicDBObject("_id",new BasicDBObject("day", new BasicDBObject("$dayOfMonth", "$ts"))
).append("count", new Document("$sum", 1)));
AggregationOutput output = collection.aggregate(match,group);
for (DBObject result : output.results()) {
System.out.println(result);
}
您的初始查询可以写得更好。据推测,您希望按 "day" 对结果进行分组,同时还从 UTC 调整到本地 timzezone。通常最好坚持使用日期数学而不是混入 date aggregation operators in such a case, and also this means that the $project
here is not required, and you can just go straight to $group
:
db.log.aggregate([
{ "$match": {
"ts": {
"$gte": ISODate("2015-07-31T18:30:00.000Z"),
"$lt": ISODate("2015-08-01T18:30:00.000Z")
},
"dup": { "$exists": false }
}},
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [
{ "$add": [ "$ts", 5.5*1000*60*60 ] },
new Date(0)
]},
{ "$mod": [
{ "$subtract": [
{ "$add": [ "$ts", 5.5*1000*60*60 ] },
new Date(0)
]},
1000*60*60*24
]}
]},
new Date(0)
]
},
"count": { "$sum": 1 }
}}
])
请注意其中的附加 Date(0)
语句。这是 "epoch" 就像你 $add
a numeric value to a date you get a Date
type in return, when you $subtract
你得到整数值一样。因此,通过添加回 "epoch" 进行更正会使所有内容再次成为 Date
类型。
因此 "date math" 到处都被使用,到 "round" 到 "day":
( 1000 millseconds * 60 seconds * 60 minutes * 24 hours )
然后模 $mod
运算符计算出从当前日期值中减去的余数,得出 "rounded" 日期。
全部在 $group
内完成并且效率很高:
翻译成 Java 只是遵循相同的缩进:
Date startdate = new DateTime(2015, 7, 31, 18, 30, 0, DateTimeZone.UTC).toDate();
Date enddate = new DateTime(2015, 8, 1, 18, 30, 0, DateTimeZone.UTC).toDate();
DBObject query = QueryBuilder.start()
.put("ts").greaterThanEquals(startdate)
.lessThan(enddate)
.and("dup").exists(false).get();
DBObject match = new BasicDBObject("$match",query);
DBObject group = new BasicDBObject("$group",
new BasicDBObject("_id",
new BasicDBObject(
"$add", Arrays.asList(
new BasicDBObject(
"$subtract", Arrays.asList(
new BasicDBObject(
"$subtract", Arrays.asList(
new BasicDBObject(
"$add",Arrays.asList( "$ts", 5.5*1000*60*60 )
),
new Date(0)
)
),
new BasicDBObject(
"$mod", Arrays.asList(
new BasicDBObject(
"$subtract", Arrays.asList(
new BasicDBObject(
"$add",Arrays.asList( "$ts", 5.5*1000*60*60 )
),
new Date(0)
)
),
1000*60*60*24
)
)
)
),
new Date(0)
)
)
)
.append("count", new BasicDBObject("$sum",1))
);
AggregationOutput output = collection.aggregate(Arrays.asList(match, group));
for ( DBObject result : output.results() ) {
System.out.println(result);
}
另请注意,您的 "timezeone adjustment" 此处假设 5.5 小时 "behind" UTC,我希望这是正确的。如果您的意思是 "after" 或正数,则该操作是 "subtraction" 而不是将 UTC 小时数更正为天数的附加操作。
所以一切都很好,您的分组键也是真实的 Date
对象,而不是日期聚合运算符返回的复合值。
将字段 ts 更改为东 8 时区:
BasicDBObject addEightHour = new BasicDBObject("$add", Arrays.asList( "$ts", 8*1000*60*60 ));
final BasicDBObject day = new BasicDBObject("$dateToString", new BasicDBObject("format", "\"%Y%m%d\"").append("date", addEightHour));
DBObject groupFields = new BasicDBObject("_id", new BasicDBObject(
"value", day));
DBObject group = new BasicDBObject("$group", groupFields);
我想运行这个mongodb查询使用java代码。
查询:
db.log.aggregate([
{ $match : {
"ts" : {
$gte: ISODate("2015-07-31T18:30:00.000Z"),
$lt: ISODate("2015-08-01T18:30:00.000Z")
},
"dup": {$exists:false}
}},
{ $project : {
'lts': {
'$add': ['$ts',5.5*3600*1000]
}
}},
{ $group : {
_id : {
day: { $dayOfMonth: "$lts" },
month: { $month: "$lts" },
year: { $year: "$lts" }
},
count: { $sum: 1 }
}}
])
我已经尝试使用此代码,但它不起作用
DBObject query = QueryBuilder.start().put("ts").greaterThanEquals(startdate).lessThanEquals(enddate).and("dup").exists(false).get();
DBObject match = new BasicDBObject("$match", query);
DBObject project=new BasicDBObject("$project", new BasicDBObject("ts",new BasicDBObject("$add",5.5*3600*1000)));
DBObject group = new BasicDBObject("$group", new BasicDBObject("_id",new BasicDBObject("day", new BasicDBObject("$dayOfMonth", "$ts"))
).append("count", new Document("$sum", 1)));
AggregationOutput output = collection.aggregate(match,group);
for (DBObject result : output.results()) {
System.out.println(result);
}
您的初始查询可以写得更好。据推测,您希望按 "day" 对结果进行分组,同时还从 UTC 调整到本地 timzezone。通常最好坚持使用日期数学而不是混入 date aggregation operators in such a case, and also this means that the $project
here is not required, and you can just go straight to $group
:
db.log.aggregate([
{ "$match": {
"ts": {
"$gte": ISODate("2015-07-31T18:30:00.000Z"),
"$lt": ISODate("2015-08-01T18:30:00.000Z")
},
"dup": { "$exists": false }
}},
{ "$group": {
"_id": {
"$add": [
{ "$subtract": [
{ "$subtract": [
{ "$add": [ "$ts", 5.5*1000*60*60 ] },
new Date(0)
]},
{ "$mod": [
{ "$subtract": [
{ "$add": [ "$ts", 5.5*1000*60*60 ] },
new Date(0)
]},
1000*60*60*24
]}
]},
new Date(0)
]
},
"count": { "$sum": 1 }
}}
])
请注意其中的附加 Date(0)
语句。这是 "epoch" 就像你 $add
a numeric value to a date you get a Date
type in return, when you $subtract
你得到整数值一样。因此,通过添加回 "epoch" 进行更正会使所有内容再次成为 Date
类型。
因此 "date math" 到处都被使用,到 "round" 到 "day":
( 1000 millseconds * 60 seconds * 60 minutes * 24 hours )
然后模 $mod
运算符计算出从当前日期值中减去的余数,得出 "rounded" 日期。
全部在 $group
内完成并且效率很高:
翻译成 Java 只是遵循相同的缩进:
Date startdate = new DateTime(2015, 7, 31, 18, 30, 0, DateTimeZone.UTC).toDate();
Date enddate = new DateTime(2015, 8, 1, 18, 30, 0, DateTimeZone.UTC).toDate();
DBObject query = QueryBuilder.start()
.put("ts").greaterThanEquals(startdate)
.lessThan(enddate)
.and("dup").exists(false).get();
DBObject match = new BasicDBObject("$match",query);
DBObject group = new BasicDBObject("$group",
new BasicDBObject("_id",
new BasicDBObject(
"$add", Arrays.asList(
new BasicDBObject(
"$subtract", Arrays.asList(
new BasicDBObject(
"$subtract", Arrays.asList(
new BasicDBObject(
"$add",Arrays.asList( "$ts", 5.5*1000*60*60 )
),
new Date(0)
)
),
new BasicDBObject(
"$mod", Arrays.asList(
new BasicDBObject(
"$subtract", Arrays.asList(
new BasicDBObject(
"$add",Arrays.asList( "$ts", 5.5*1000*60*60 )
),
new Date(0)
)
),
1000*60*60*24
)
)
)
),
new Date(0)
)
)
)
.append("count", new BasicDBObject("$sum",1))
);
AggregationOutput output = collection.aggregate(Arrays.asList(match, group));
for ( DBObject result : output.results() ) {
System.out.println(result);
}
另请注意,您的 "timezeone adjustment" 此处假设 5.5 小时 "behind" UTC,我希望这是正确的。如果您的意思是 "after" 或正数,则该操作是 "subtraction" 而不是将 UTC 小时数更正为天数的附加操作。
所以一切都很好,您的分组键也是真实的 Date
对象,而不是日期聚合运算符返回的复合值。
将字段 ts 更改为东 8 时区:
BasicDBObject addEightHour = new BasicDBObject("$add", Arrays.asList( "$ts", 8*1000*60*60 ));
final BasicDBObject day = new BasicDBObject("$dateToString", new BasicDBObject("format", "\"%Y%m%d\"").append("date", addEightHour));
DBObject groupFields = new BasicDBObject("_id", new BasicDBObject(
"value", day));
DBObject group = new BasicDBObject("$group", groupFields);