Skip to content

Commit fc41793

Browse files
christophstroblmp911de
authored andcommitted
Add support for $dateDiff aggregation operator.
Closes: #3713 Original pull request: #3748.
1 parent afef243 commit fc41793

File tree

4 files changed

+166
-0
lines changed

4 files changed

+166
-0
lines changed

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

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,42 @@ public DayOfWeek dayOfWeek() {
340340
return applyTimezone(DayOfWeek.dayOfWeek(dateReference()), timezone);
341341
}
342342

343+
/**
344+
* Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date
345+
* computed by the given {@link AggregationExpression expression}. @param expression must not be {@literal null}.
346+
*
347+
* @param unit the unit of measure. Must not be {@literal null}.
348+
* @return new instance of {@link DateAdd}.
349+
* @since 3.3
350+
*/
351+
public DateDiff diffValueOf(AggregationExpression expression, String unit) {
352+
return applyTimezone(DateDiff.diffValueOf(expression, unit).toDate(dateReference()), timezone);
353+
}
354+
355+
/**
356+
* Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date stored
357+
* at the given {@literal field}. @param expression must not be {@literal null}.
358+
*
359+
* @param unit the unit of measure. Must not be {@literal null}.
360+
* @return new instance of {@link DateAdd}.
361+
* @since 3.3
362+
*/
363+
public DateDiff diffValueOf(String fieldReference, String unit) {
364+
return applyTimezone(DateDiff.diffValueOf(fieldReference, unit).toDate(dateReference()), timezone);
365+
}
366+
367+
/**
368+
* Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date given
369+
* {@literal value}. @param value anything the resolves to a valid date. Must not be {@literal null}.
370+
*
371+
* @param unit the unit of measure. Must not be {@literal null}.
372+
* @return new instance of {@link DateAdd}.
373+
* @since 3.3
374+
*/
375+
public DateDiff diff(Object value, String unit) {
376+
return applyTimezone(DateDiff.diffValue(value, unit).toDate(dateReference()), timezone);
377+
}
378+
343379
/**
344380
* Creates new {@link AggregationExpression} that returns the year portion of a date.
345381
*
@@ -2550,6 +2586,114 @@ protected String getMongoMethod() {
25502586
}
25512587
}
25522588

2589+
/**
2590+
* {@link AggregationExpression} for {@code $dateDiff}.<br />
2591+
* <strong>NOTE:</strong> Requires MongoDB 5.0 or later.
2592+
*
2593+
* @author Christoph Strobl
2594+
* @since 3.3
2595+
*/
2596+
public static class DateDiff extends TimezonedDateAggregationExpression {
2597+
2598+
private DateDiff(Object value) {
2599+
super(value);
2600+
}
2601+
2602+
/**
2603+
* Add the number of {@literal units} of the result of the given {@link AggregationExpression expression} to a
2604+
* {@link #toDate(Object) start date}.
2605+
*
2606+
* @param expression must not be {@literal null}.
2607+
* @param unit must not be {@literal null}.
2608+
* @return new instance of {@link DateAdd}.
2609+
*/
2610+
public static DateDiff diffValueOf(AggregationExpression expression, String unit) {
2611+
return diffValue(expression, unit);
2612+
}
2613+
2614+
/**
2615+
* Add the number of {@literal units} from a {@literal field} to a {@link #toDate(Object) start date}.
2616+
*
2617+
* @param fieldReference must not be {@literal null}.
2618+
* @param unit must not be {@literal null}.
2619+
* @return new instance of {@link DateAdd}.
2620+
*/
2621+
public static DateDiff diffValueOf(String fieldReference, String unit) {
2622+
return diffValue(Fields.field(fieldReference), unit);
2623+
}
2624+
2625+
/**
2626+
* Add the number of {@literal units} to a {@link #toDate(Object) start date}.
2627+
*
2628+
* @param value must not be {@literal null}.
2629+
* @param unit must not be {@literal null}.
2630+
* @return new instance of {@link DateAdd}.
2631+
*/
2632+
public static DateDiff diffValue(Object value, String unit) {
2633+
2634+
Map<String, Object> args = new HashMap<>();
2635+
args.put("unit", unit);
2636+
args.put("endDate", value);
2637+
return new DateDiff(args);
2638+
}
2639+
2640+
/**
2641+
* Define the start date, in UTC, for the addition operation.
2642+
*
2643+
* @param expression must not be {@literal null}.
2644+
* @return new instance of {@link DateAdd}.
2645+
*/
2646+
public DateDiff toDateOf(AggregationExpression expression) {
2647+
return toDate(expression);
2648+
}
2649+
2650+
/**
2651+
* Define the start date, in UTC, for the addition operation.
2652+
*
2653+
* @param fieldReference must not be {@literal null}.
2654+
* @return new instance of {@link DateAdd}.
2655+
*/
2656+
public DateDiff toDateOf(String fieldReference) {
2657+
return toDate(Fields.field(fieldReference));
2658+
}
2659+
2660+
/**
2661+
* Define the start date, in UTC, for the addition operation.
2662+
*
2663+
* @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}.
2664+
* @return new instance of {@link DateAdd}.
2665+
*/
2666+
public DateDiff toDate(Object dateExpression) {
2667+
return new DateDiff(append("startDate", dateExpression));
2668+
}
2669+
2670+
/**
2671+
* Optionally set the {@link Timezone} to use. If not specified {@literal UTC} is used.
2672+
*
2673+
* @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
2674+
* @return new instance of {@link DateAdd}.
2675+
*/
2676+
public DateDiff withTimezone(Timezone timezone) {
2677+
return new DateDiff(appendTimezone(argumentMap(), timezone));
2678+
}
2679+
2680+
/**
2681+
* Set the start day of the week if the unit if measure is set to {@literal week}. Uses {@literal Sunday} by
2682+
* default.
2683+
*
2684+
* @param day must not be {@literal null}.
2685+
* @return new instance of {@link DateDiff}.
2686+
*/
2687+
public DateDiff startOfWeek(Object day) {
2688+
return new DateDiff(append("startOfWeek", day));
2689+
}
2690+
2691+
@Override
2692+
protected String getMongoMethod() {
2693+
return "$dateDiff";
2694+
}
2695+
}
2696+
25532697
@SuppressWarnings("unchecked")
25542698
private static <T extends TimezonedDateAggregationExpression> T applyTimezone(T instance, Timezone timezone) {
25552699
return !ObjectUtils.nullSafeEquals(Timezone.none(), timezone) && !instance.hasTimezone()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ public class MethodReferenceNode extends ExpressionNode {
145145

146146
// DATE OPERATORS
147147
map.put("dateAdd", mapArgRef().forOperator("$dateAdd").mappingParametersTo("startDate", "unit", "amount", "timezone"));
148+
map.put("dateDiff", mapArgRef().forOperator("$dateDiff").mappingParametersTo("startDate", "endDate", "unit","timezone", "startOfWeek"));
148149
map.put("dayOfYear", singleArgRef().forOperator("$dayOfYear"));
149150
map.put("dayOfMonth", singleArgRef().forOperator("$dayOfMonth"));
150151
map.put("dayOfWeek", singleArgRef().forOperator("$dayOfWeek"));

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,21 @@ void rendersDateAddWithTimezone() {
4040
.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
4141
"{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }"));
4242
}
43+
44+
@Test // GH-3713
45+
void rendersDateDiff() {
46+
47+
assertThat(
48+
DateOperators.dateOf("purchaseDate").diffValueOf("delivered", "day").toDocument(Aggregation.DEFAULT_CONTEXT))
49+
.isEqualTo(Document
50+
.parse("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }"));
51+
}
52+
53+
@Test // GH-3713
54+
void rendersDateDiffWithTimezone() {
55+
56+
assertThat(DateOperators.dateOf("purchaseDate").withTimezone(Timezone.valueOf("America/Chicago"))
57+
.diffValueOf("delivered", "day").toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
58+
"{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\", timezone : \"America/Chicago\" } }"));
4359
}
4460
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,11 @@ void shouldRenderDateAdd() {
10441044
assertThat(transform("dateAdd(purchaseDate, 'day', 3)")).isEqualTo(Document.parse("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }"));
10451045
}
10461046

1047+
@Test // GH-3713
1048+
void shouldRenderDateDiff() {
1049+
assertThat(transform("dateDiff(purchaseDate, delivered, 'day')")).isEqualTo(Document.parse("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }"));
1050+
}
1051+
10471052
private Object transform(String expression, Object... params) {
10481053
Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
10491054
return result == null ? null : (!(result instanceof org.bson.Document) ? result.toString() : result);

0 commit comments

Comments
 (0)