Add support for $dateSubtract aggregation operator.

See #4139
Original pull request: #4182.
This commit is contained in:
Christoph Strobl
2022-09-23 10:14:25 +02:00
committed by Mark Paluch
parent 5bbe481e98
commit db12c4ba5a
4 changed files with 209 additions and 0 deletions

View File

@@ -449,6 +449,95 @@ public class DateOperators {
timezone); timezone);
} }
/**
* Creates new {@link AggregationExpression} that subtracts the value of the given {@link AggregationExpression
* expression} (in {@literal units}).
*
* @param expression must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
* @since 4.0
*/
public DateSubtract subtractValueOf(AggregationExpression expression, String unit) {
return applyTimezone(DateSubtract.subtractValueOf(expression, unit).fromDate(dateReference()), timezone);
}
/**
* Creates new {@link AggregationExpression} that subtracts the value of the given {@link AggregationExpression
* expression} (in {@literal units}).
*
* @param expression must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
* @since 4.0
*/
public DateSubtract subtractValueOf(AggregationExpression expression, TemporalUnit unit) {
Assert.notNull(unit, "TemporalUnit must not be null");
return applyTimezone(
DateSubtract.subtractValueOf(expression, unit.name().toLowerCase(Locale.ROOT)).fromDate(dateReference()),
timezone);
}
/**
* Creates new {@link AggregationExpression} that subtracts the value stored at the given {@literal field} (in
* {@literal units}).
*
* @param fieldReference must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
* @since 4.0
*/
public DateSubtract subtractValueOf(String fieldReference, String unit) {
return applyTimezone(DateSubtract.subtractValueOf(fieldReference, unit).fromDate(dateReference()), timezone);
}
/**
* Creates new {@link AggregationExpression} that subtracts the value stored at the given {@literal field} (in
* {@literal units}).
*
* @param fieldReference must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
* @since 4.0
*/
public DateSubtract subtractValueOf(String fieldReference, TemporalUnit unit) {
Assert.notNull(unit, "TemporalUnit must not be null");
return applyTimezone(
DateSubtract.subtractValueOf(fieldReference, unit.name().toLowerCase(Locale.ROOT)).fromDate(dateReference()),
timezone);
}
/**
* Creates new {@link AggregationExpression} that subtracts the given value (in {@literal units}).
*
* @param value must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
* @since 4.0
*/
public DateSubtract subtract(Object value, String unit) {
return applyTimezone(DateSubtract.subtractValue(value, unit).fromDate(dateReference()), timezone);
}
/**
* Creates new {@link AggregationExpression} that subtracts the given value (in {@literal units}).
*
* @param value must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
* @since 4.0
*/
public DateSubtract subtract(Object value, TemporalUnit unit) {
Assert.notNull(unit, "TemporalUnit must not be null");
return applyTimezone(
DateSubtract.subtractValue(value, unit.name().toLowerCase(Locale.ROOT)).fromDate(dateReference()), timezone);
}
/** /**
* Creates new {@link AggregationExpression} that returns the day of the year for a date as a number between 1 and * Creates new {@link AggregationExpression} that returns the day of the year for a date as a number between 1 and
* 366. * 366.
@@ -2733,6 +2822,103 @@ public class DateOperators {
} }
} }
/**
* {@link AggregationExpression} for {@code $dateSubtract}.<br />
* <strong>NOTE:</strong> Requires MongoDB 5.0 or later.
*
* @author Christoph Strobl
* @since 4.0
*/
public static class DateSubtract extends TimezonedDateAggregationExpression {
private DateSubtract(Object value) {
super(value);
}
/**
* Subtract the number of {@literal units} of the result of the given {@link AggregationExpression expression} from
* a {@link #fromDate(Object) start date}.
*
* @param expression must not be {@literal null}.
* @param unit must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
*/
public static DateSubtract subtractValueOf(AggregationExpression expression, String unit) {
return subtractValue(expression, unit);
}
/**
* Subtract the number of {@literal units} from a {@literal field} from a {@link #fromDate(Object) start date}.
*
* @param fieldReference must not be {@literal null}.
* @param unit must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
*/
public static DateSubtract subtractValueOf(String fieldReference, String unit) {
return subtractValue(Fields.field(fieldReference), unit);
}
/**
* Subtract the number of {@literal units} from a {@link #fromDate(Object) start date}.
*
* @param value must not be {@literal null}.
* @param unit must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
*/
public static DateSubtract subtractValue(Object value, String unit) {
Map<String, Object> args = new HashMap<>();
args.put("unit", unit);
args.put("amount", value);
return new DateSubtract(args);
}
/**
* Define the start date, in UTC, for the subtraction operation.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
*/
public DateSubtract fromDateOf(AggregationExpression expression) {
return fromDate(expression);
}
/**
* Define the start date, in UTC, for the subtraction operation.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
*/
public DateSubtract fromDateOf(String fieldReference) {
return fromDate(Fields.field(fieldReference));
}
/**
* Define the start date, in UTC, for the subtraction operation.
*
* @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}.
* @return new instance of {@link DateSubtract}.
*/
public DateSubtract fromDate(Object dateExpression) {
return new DateSubtract(append("startDate", dateExpression));
}
/**
* Optionally set the {@link Timezone} to use. If not specified {@literal UTC} is used.
*
* @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
* @return new instance of {@link DateSubtract}.
*/
public DateSubtract withTimezone(Timezone timezone) {
return new DateSubtract(appendTimezone(argumentMap(), timezone));
}
@Override
protected String getMongoMethod() {
return "$dateSubtract";
}
}
/** /**
* {@link AggregationExpression} for {@code $dateDiff}.<br /> * {@link AggregationExpression} for {@code $dateDiff}.<br />
* <strong>NOTE:</strong> Requires MongoDB 5.0 or later. * <strong>NOTE:</strong> Requires MongoDB 5.0 or later.

View File

@@ -161,6 +161,8 @@ public class MethodReferenceNode extends ExpressionNode {
// DATE OPERATORS // DATE OPERATORS
map.put("dateAdd", map.put("dateAdd",
mapArgRef().forOperator("$dateAdd").mappingParametersTo("startDate", "unit", "amount", "timezone")); mapArgRef().forOperator("$dateAdd").mappingParametersTo("startDate", "unit", "amount", "timezone"));
map.put("dateSubtract",
mapArgRef().forOperator("$dateSubtract").mappingParametersTo("startDate", "unit", "amount", "timezone"));
map.put("dateDiff", mapArgRef().forOperator("$dateDiff").mappingParametersTo("startDate", "endDate", "unit", map.put("dateDiff", mapArgRef().forOperator("$dateDiff").mappingParametersTo("startDate", "endDate", "unit",
"timezone", "startOfWeek")); "timezone", "startOfWeek"));
map.put("dayOfYear", singleArgRef().forOperator("$dayOfYear")); map.put("dayOfYear", singleArgRef().forOperator("$dayOfYear"));

View File

@@ -49,6 +49,21 @@ class DateOperatorsUnitTests {
"{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }"); "{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }");
} }
@Test // GH-4139
void rendersDateSubtract() {
assertThat(DateOperators.dateOf("purchaseDate").subtract(3, "day").toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $dateSubtract: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }");
}
@Test // GH-4139
void rendersDateSubtractWithTimezone() {
assertThat(DateOperators.zonedDateOf("purchaseDate", Timezone.valueOf("America/Chicago")).subtract(3, "day")
.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(
"{ $dateSubtract: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }");
}
@Test // GH-3713 @Test // GH-3713
void rendersDateDiff() { void rendersDateDiff() {

View File

@@ -1163,6 +1163,12 @@ public class SpelExpressionTransformerUnitTests {
.isEqualTo("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }"); .isEqualTo("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }");
} }
@Test // GH-4139
void shouldRenderDateSubtract() {
assertThat(transform("dateSubtract(purchaseDate, 'day', 3)"))
.isEqualTo("{ $dateSubtract: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }");
}
@Test // GH-3713 @Test // GH-3713
void shouldRenderDateDiff() { void shouldRenderDateDiff() {
assertThat(transform("dateDiff(purchaseDate, delivered, 'day')")) assertThat(transform("dateDiff(purchaseDate, delivered, 'day')"))