聚合项目组按提取的日月年 Spring 数据
Aggregation Project Group By extracted day, month and year with Spring Data
直接说,我该怎么做:
group._id = {
year: { $year : [{ $subtract: [ "$timestamp", 25200000 ]}] },
month: { $month : [{ $subtract: [ "$timestamp", 25200000 ]}] },
day: { $dayOfMonth : [{ $subtract: [ "$timestamp", 25200000 ]}] }
};
与 spring 数据
我已经尝试过这个和其他一些形式,但没有成功
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(c),
Aggregation.project("programa", "custo", "duracao", "dataHora")
.andExpression("dataHora").minus(25200000).extractDayOfMonth().as("dia")
.andExpression("dataHora").minus(25200000).extractMonth().as("mes")
.andExpression("dataHora").minus(25200000).extractYear().as("ano"),
Aggregation.group("programa", "ano", "mes", "dia")
.count().as("count")
.sum("custo").as("valorTotal")
.sum("duracao").as("duracaoTotal")
.first("dataHora").as("dataHora"),
Aggregation.sort(Direction.ASC, "dataHora")
);
我需要在 mongodb 中按日、月和年分组,否则我将需要在代码中转换所有这些分组数据。
提前致谢
您目前 运行 在单个 $project
stage, and indeed you are likely already writing as a separate $project
since you discover that you cannot project custom named fields directly in a $group
_id
中可以进行的字段计算受到 spring mongo 的限制.
因此,您最好将所有这些都保留在 $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")
);
由于自定义 class 从内置辅助方法使用的相同基本 class 中抽象出来,因此它可以与它们一起使用,如图所示。
日期数学的基本过程在这里是如何工作的,当你 $subtract
one BSON Date object from another then the result is the milliseconds of difference, and in this case from the epoch date ( Date(0) ) which just extracts the milliseconds value. This allows you to do the math to round to the current date value by the modulo ( $mod
) 从一天中的毫秒数。
就像您最初尝试的那样,当您 $add
那个毫秒值到 BSON 日期对象时,返回的值又是一个 BSON 日期。因此,向代表纪元 returns 的对象添加一个新的日期对象,但四舍五入到当前日期。
这通常比通过 date aggregation operators 提取部分有用得多,而且代码也更短一些,尤其是在调整 UTC 时间时(如您在此处所做的那样)。
虽然这里 $group
的构造比 spring mongo 试图避免的辅助函数要简洁一些,但最终效率要高得多而不是 运行 一个单独的 $project
阶段来转换您真正只想要在 $group
阶段中的字段值。
直接说,我该怎么做:
group._id = {
year: { $year : [{ $subtract: [ "$timestamp", 25200000 ]}] },
month: { $month : [{ $subtract: [ "$timestamp", 25200000 ]}] },
day: { $dayOfMonth : [{ $subtract: [ "$timestamp", 25200000 ]}] }
};
与 spring 数据
我已经尝试过这个和其他一些形式,但没有成功
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(c),
Aggregation.project("programa", "custo", "duracao", "dataHora")
.andExpression("dataHora").minus(25200000).extractDayOfMonth().as("dia")
.andExpression("dataHora").minus(25200000).extractMonth().as("mes")
.andExpression("dataHora").minus(25200000).extractYear().as("ano"),
Aggregation.group("programa", "ano", "mes", "dia")
.count().as("count")
.sum("custo").as("valorTotal")
.sum("duracao").as("duracaoTotal")
.first("dataHora").as("dataHora"),
Aggregation.sort(Direction.ASC, "dataHora")
);
我需要在 mongodb 中按日、月和年分组,否则我将需要在代码中转换所有这些分组数据。
提前致谢
您目前 运行 在单个 $project
stage, and indeed you are likely already writing as a separate $project
since you discover that you cannot project custom named fields directly in a $group
_id
中可以进行的字段计算受到 spring mongo 的限制.
因此,您最好将所有这些都保留在 $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")
);
由于自定义 class 从内置辅助方法使用的相同基本 class 中抽象出来,因此它可以与它们一起使用,如图所示。
日期数学的基本过程在这里是如何工作的,当你 $subtract
one BSON Date object from another then the result is the milliseconds of difference, and in this case from the epoch date ( Date(0) ) which just extracts the milliseconds value. This allows you to do the math to round to the current date value by the modulo ( $mod
) 从一天中的毫秒数。
就像您最初尝试的那样,当您 $add
那个毫秒值到 BSON 日期对象时,返回的值又是一个 BSON 日期。因此,向代表纪元 returns 的对象添加一个新的日期对象,但四舍五入到当前日期。
这通常比通过 date aggregation operators 提取部分有用得多,而且代码也更短一些,尤其是在调整 UTC 时间时(如您在此处所做的那样)。
虽然这里 $group
的构造比 spring mongo 试图避免的辅助函数要简洁一些,但最终效率要高得多而不是 运行 一个单独的 $project
阶段来转换您真正只想要在 $group
阶段中的字段值。