Skip to content

Commit dec7c12

Browse files
christophstroblmp911de
authored andcommitted
Add support for $dateTrunc aggregation operator.
See #4139 Original pull request: #4182.
1 parent db12c4b commit dec7c12

File tree

4 files changed

+178
-0
lines changed

4 files changed

+178
-0
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java

+155
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,32 @@ public DateSubtract subtract(Object value, TemporalUnit unit) {
538538
DateSubtract.subtractValue(value, unit.name().toLowerCase(Locale.ROOT)).fromDate(dateReference()), timezone);
539539
}
540540

541+
/**
542+
* Creates new {@link AggregationExpression} that truncates a date to the given {@literal unit}.
543+
*
544+
* @param unit the unit of measure. Must not be {@literal null}.
545+
* @return new instance of {@link DateTrunc}.
546+
* @since 4.0
547+
*/
548+
public DateTrunc truncate(String unit) {
549+
550+
Assert.notNull(unit, "TemporalUnit must not be null");
551+
return applyTimezone(DateTrunc.truncateValue(dateReference()).to(unit), timezone);
552+
}
553+
554+
/**
555+
* Creates new {@link AggregationExpression} that truncates a date to the given {@literal unit}.
556+
*
557+
* @param unit the unit of measure. Must not be {@literal null}.
558+
* @return new instance of {@link DateTrunc}.
559+
* @since 4.0
560+
*/
561+
public DateTrunc truncate(TemporalUnit unit) {
562+
563+
Assert.notNull(unit, "TemporalUnit must not be null");
564+
return truncate(unit.name().toLowerCase(Locale.ROOT));
565+
}
566+
541567
/**
542568
* Creates new {@link AggregationExpression} that returns the day of the year for a date as a number between 1 and
543569
* 366.
@@ -3027,6 +3053,135 @@ protected String getMongoMethod() {
30273053
}
30283054
}
30293055

3056+
/**
3057+
* {@link AggregationExpression} for {@code $dateTrunc}.<br />
3058+
* <strong>NOTE:</strong> Requires MongoDB 5.0 or later.
3059+
*
3060+
* @author Christoph Strobl
3061+
* @since 4.0
3062+
*/
3063+
public static class DateTrunc extends TimezonedDateAggregationExpression {
3064+
3065+
private DateTrunc(Object value) {
3066+
super(value);
3067+
}
3068+
3069+
/**
3070+
* Truncates the date value of computed by the given {@link AggregationExpression}.
3071+
*
3072+
* @param expression must not be {@literal null}.
3073+
* @return new instance of {@link DateTrunc}.
3074+
*/
3075+
public static DateTrunc truncateValueOf(AggregationExpression expression) {
3076+
return truncateValue(expression);
3077+
}
3078+
3079+
/**
3080+
* Truncates the date value of the referenced {@literal field}.
3081+
*
3082+
* @param fieldReference must not be {@literal null}.
3083+
* @return new instance of {@link DateTrunc}.
3084+
*/
3085+
public static DateTrunc truncateValueOf(String fieldReference) {
3086+
return truncateValue(Fields.field(fieldReference));
3087+
}
3088+
3089+
/**
3090+
* Truncates the date value.
3091+
*
3092+
* @param value must not be {@literal null}.
3093+
* @return new instance of {@link DateTrunc}.
3094+
*/
3095+
public static DateTrunc truncateValue(Object value) {
3096+
return new DateTrunc(Collections.singletonMap("date", value));
3097+
}
3098+
3099+
/**
3100+
* Define the unit of time.
3101+
*
3102+
* @param unit must not be {@literal null}.
3103+
* @return new instance of {@link DateTrunc}.
3104+
*/
3105+
public DateTrunc to(String unit) {
3106+
return new DateTrunc(append("unit", unit));
3107+
}
3108+
3109+
/**
3110+
* Define the unit of time via an {@link AggregationExpression}.
3111+
*
3112+
* @param unit must not be {@literal null}.
3113+
* @return new instance of {@link DateTrunc}.
3114+
*/
3115+
public DateTrunc to(AggregationExpression unit) {
3116+
return new DateTrunc(append("unit", unit));
3117+
}
3118+
3119+
/**
3120+
* Define the weeks starting day if {@link #to(String)} resolves to {@literal week}.
3121+
*
3122+
* @param day must not be {@literal null}.
3123+
* @return new instance of {@link DateTrunc}.
3124+
*/
3125+
public DateTrunc startOfWeek(java.time.DayOfWeek day) {
3126+
return startOfWeek(day.name().toLowerCase(Locale.US));
3127+
}
3128+
3129+
/**
3130+
* Define the weeks starting day if {@link #to(String)} resolves to {@literal week}.
3131+
*
3132+
* @param day must not be {@literal null}.
3133+
* @return new instance of {@link DateTrunc}.
3134+
*/
3135+
public DateTrunc startOfWeek(String day) {
3136+
return new DateTrunc(append("startOfWeek", day));
3137+
}
3138+
3139+
/**
3140+
* Define the numeric time value.
3141+
*
3142+
* @param binSize must not be {@literal null}.
3143+
* @return new instance of {@link DateTrunc}.
3144+
*/
3145+
public DateTrunc binSize(int binSize) {
3146+
return binSize((Object) binSize);
3147+
}
3148+
3149+
/**
3150+
* Define the numeric time value via an {@link AggregationExpression}.
3151+
*
3152+
* @param expression must not be {@literal null}.
3153+
* @return new instance of {@link DateTrunc}.
3154+
*/
3155+
public DateTrunc binSize(AggregationExpression expression) {
3156+
return binSize((Object) expression);
3157+
}
3158+
3159+
/**
3160+
* Define the numeric time value.
3161+
*
3162+
* @param binSize must not be {@literal null}.
3163+
* @return new instance of {@link DateTrunc}.
3164+
*/
3165+
public DateTrunc binSize(Object binSize) {
3166+
return new DateTrunc(append("binSize", binSize));
3167+
}
3168+
3169+
/**
3170+
* Optionally set the {@link Timezone} to use. If not specified {@literal UTC} is used.
3171+
*
3172+
* @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
3173+
* @return new instance of {@link DateTrunc}.
3174+
*/
3175+
public DateTrunc withTimezone(Timezone timezone) {
3176+
return new DateTrunc(appendTimezone(argumentMap(), timezone));
3177+
}
3178+
3179+
@Override
3180+
protected String getMongoMethod() {
3181+
return "$dateTrunc";
3182+
}
3183+
}
3184+
30303185
/**
30313186
* Interface defining a temporal unit for date operators.
30323187
*

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java

+2
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ public class MethodReferenceNode extends ExpressionNode {
165165
mapArgRef().forOperator("$dateSubtract").mappingParametersTo("startDate", "unit", "amount", "timezone"));
166166
map.put("dateDiff", mapArgRef().forOperator("$dateDiff").mappingParametersTo("startDate", "endDate", "unit",
167167
"timezone", "startOfWeek"));
168+
map.put("dateTrunc",
169+
mapArgRef().forOperator("$dateTrunc").mappingParametersTo("date", "unit", "binSize", "startOfWeek", "timezone"));
168170
map.put("dayOfYear", singleArgRef().forOperator("$dayOfYear"));
169171
map.put("dayOfMonth", singleArgRef().forOperator("$dayOfMonth"));
170172
map.put("dayOfWeek", singleArgRef().forOperator("$dayOfWeek"));

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java

+16
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717

1818
import static org.springframework.data.mongodb.test.util.Assertions.*;
1919

20+
import java.time.DayOfWeek;
2021
import java.time.ZoneId;
2122
import java.time.ZoneOffset;
2223
import java.time.temporal.ChronoUnit;
2324
import java.util.TimeZone;
2425

2526
import org.junit.jupiter.api.Test;
2627

28+
import org.springframework.data.mongodb.core.aggregation.DateOperators.TemporalUnit;
2729
import org.springframework.data.mongodb.core.aggregation.DateOperators.Timezone;
2830

2931
/**
@@ -102,4 +104,18 @@ void rendersTimezoneFromTimeZoneId() {
102104
void rendersTimezoneFromZoneId() {
103105
assertThat(DateOperators.Timezone.fromZone(ZoneId.of("America/Chicago")).getValue()).isEqualTo("America/Chicago");
104106
}
107+
108+
@Test // GH-4139
109+
void rendersDateTrunc() {
110+
111+
assertThat(DateOperators.dateOf("purchaseDate").truncate("week").binSize(2).startOfWeek(DayOfWeek.MONDAY).toDocument(Aggregation.DEFAULT_CONTEXT))
112+
.isEqualTo("{ $dateTrunc: { date: \"$purchaseDate\", unit: \"week\", binSize: 2, startOfWeek : \"monday\" } }");
113+
}
114+
115+
@Test // GH-4139
116+
void rendersDateTruncWithTimezone() {
117+
118+
assertThat(DateOperators.zonedDateOf("purchaseDate", Timezone.valueOf("America/Chicago")).truncate("week").binSize(2).startOfWeek(DayOfWeek.MONDAY).toDocument(Aggregation.DEFAULT_CONTEXT))
119+
.isEqualTo("{ $dateTrunc: { date: \"$purchaseDate\", unit: \"week\", binSize: 2, startOfWeek : \"monday\", timezone : \"America/Chicago\" } }");
120+
}
105121
}

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java

+5
Original file line numberDiff line numberDiff line change
@@ -1220,6 +1220,11 @@ void shouldRenderMinN() {
12201220
assertThat(transform("minN(3, \"$score\")")).isEqualTo("{ $minN : { n : 3, input : \"$score\" }}");
12211221
}
12221222

1223+
@Test // GH-4139
1224+
void shouldRenderDateTrunc() {
1225+
assertThat(transform("dateTrunc(purchaseDate, \"week\", 2, \"monday\")")).isEqualTo("{ $dateTrunc : { date : \"$purchaseDate\", unit : \"week\", binSize : 2, startOfWeek : \"monday\" }}");
1226+
}
1227+
12231228
private Document transform(String expression, Object... params) {
12241229
return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
12251230
}

0 commit comments

Comments
 (0)