您在单个$project
阶段中可以为字段计算执行的操作遇到了 spring mongo 的限制,实际上您可能已经单独编写,$project
因为您发现目前也无法直接在 a中投影自定义命名字段。$group
_id
因此,最好将所有这些都保留在 中$group
,并使用不同的方法将调整后的日期四舍五入为当地时间。
因此,编写你的更好的方法$group
是:
{ "$group": {
"_id": {
"programa": "$programa",
"dataHora": {
"$add": [
{ "$subtract": [
{ "$subtract": [{ "$subtract": ["$dataHora", new Date(0)] }, 25200000 ] },
{ "$mod": [
{ "$subtract": [{ "$subtract": ["$dataHora", new Date(0)] }, 25200000 ] },
1000 * 60 * 60 * 24
]}
]},
new Date(0)
]
}
},
"count": { "$sum": 1 },
"valorTotal": { "$sum": "$custo" },
"duracaoTotal": { "$sum": "$duracao" },
"dataHora": { "$first": "$dataHora" }
}}
当然,要将这种结构与 spring-mongo 一起使用,您需要自定义实现聚合阶段操作,该操作可以定义DBObject
:
public class CustomGroupOperation implements AggregationOperation {
private DBObject operation;
public CustomGroupOperation (DBObject operation) {
this.operation = operation;
}
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}
然后在这样的上下文中使用它:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(c),
new CustomGroupOperation(
new BasicDBObject("$group",
new BasicDBObject("_id",
new BasicDBObject("programa","$programa")
.append("dataHora",
new BasicDBObject("$add",Arrays.asList(
new BasicDBObject("$subtract",Arrays.asList(
new BasicDBObject("$subtract",Arrays.asList(
new BasicDBObject("$subtract",Arrays.asList(
"$dataHora", new Date(0)
)),
25200000
)),
new BasicDBObject("$mod",Arrays.asList(
new BasicDBObject("$subtract",Arrays.asList(
new BasicDBObject("$subtract",Arrays.asList(
"$dataHora", new Date(0)
)),
25200000
)),
1000 * 60 * 60 * 24
))
)),
new Date(0)
))
)
)
.append("count",new BasicDBObject("$sum",1))
.append("valorTotal",new BasicDBObject("$sum","$custo"))
.append("duracaoTotal",new BasicDBObject("$sum","$duracao"))
.append("dataHora",new BasicDBObject("$first","$dataHora"))
)
),
Aggregation.sort(Direction.ASC,"_id.dataHora")
);
由于自定义类从内置辅助方法使用的相同基本类中抽象出来,因此可以与它们一起使用,如图所示。
此处使用日期数学的基本过程是如何工作的,当您将$subtract
一个 BSON Date 对象与另一个对象分开时,结果是毫秒的差异,在这种情况下,来自仅提取毫秒值的纪元日期( Date(0) )。$mod
这使您可以进行数学运算,以一天中的毫秒数为模 ( ) 舍入到当前日期值。
就像您最初尝试的那样,当您$add
将该毫秒值传递给 BSON 日期对象时,返回的值再次是 BSON 日期。因此,添加到表示纪元的对象会返回一个新的日期对象,但会四舍五入为当前日期。
这通常比通过日期聚合运算符提取部分有用得多,而且代码也更短一些,尤其是当您在这里调整 UTC 时间时。
尽管$group
这里的构造比 spring mongo 试图避免的辅助函数更简洁,但最终它比运行单独的$project
阶段来转换你真正只需要在$group
阶段中的字段值要高效得多反正。