diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java index a3fd38a0a..7281440f6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java @@ -22,6 +22,7 @@ import java.util.Map; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * Gateway to {@literal Date} aggregation operations. @@ -178,7 +179,7 @@ public class DateOperators { * Create a {@link Timezone} for the {@link AggregationExpression} resulting in the Olson Timezone Identifier or UTC * Offset. * - * @param value the {@link AggregationExpression} resulting in the timezone. + * @param expression the {@link AggregationExpression} resulting in the timezone. * @return new instance of {@link Timezone}. */ public static Timezone ofExpression(AggregationExpression expression) { @@ -380,6 +381,17 @@ public class DateOperators { return applyTimezone(DateToString.dateToString(dateReference()).toString(format), timezone); } + /** + * Creates new {@link AggregationExpression} that converts a date object to a string according to the server default + * format. + * + * @return new instance of {@link DateToString}. + * @since 2.1 + */ + public DateToString toStringWithDefaultFormat() { + return applyTimezone(DateToString.dateToString(dateReference()).defaultFormat(), timezone); + } + /** * Creates new {@link AggregationExpression} that returns the weekday number in ISO 8601-2018 format, ranging from 1 * (for Monday) to 7 (for Sunday). @@ -1352,6 +1364,11 @@ public class DateOperators { Assert.notNull(format, "Format must not be null!"); return new DateToString(argumentMap(value, format, Timezone.none())); } + + @Override + public DateToString defaultFormat() { + return new DateToString(argumentMap(value, null, Timezone.none())); + } }; } @@ -1395,15 +1412,55 @@ public class DateOperators { return new DateToString(argumentMap(get("date"), get("format"), timezone)); } + /** + * Optionally specify the value to return when the date is {@literal null} or missing.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param value must not be {@literal null}. + * @return new instance of {@link DateToString}. + * @since 2.1 + */ + public DateToString onNullReturn(Object value) { + return new DateToString(append("onNull", value)); + } + + /** + * Optionally specify the field holding the value to return when the date is {@literal null} or missing.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param fieldReference must not be {@literal null}. + * @return new instance of {@link DateToString}. + * @since 2.1 + */ + public DateToString onNullReturnValueOf(String fieldReference) { + return onNullReturn(Fields.field(fieldReference)); + } + + /** + * Optionally specify the expression to evaluate and return when the date is {@literal null} or missing.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link DateToString}. + * @since 2.1 + */ + public DateToString onNullReturnValueOf(AggregationExpression expression) { + return onNullReturn(expression); + } + @Override protected String getMongoMethod() { return "$dateToString"; } - private static java.util.Map argumentMap(Object date, String format, Timezone timezone) { + private static java.util.Map argumentMap(Object date, @Nullable String format, Timezone timezone) { java.util.Map args = new LinkedHashMap(2); - args.put("format", format); + + if (StringUtils.hasText(format)) { + args.put("format", format); + } + args.put("date", date); if (!ObjectUtils.nullSafeEquals(timezone, Timezone.none())) { @@ -1421,6 +1478,16 @@ public class DateOperators { * @return */ DateToString toString(String format); + + /** + * Creates new {@link DateToString} using the server default string format ({@code %Y-%m-%dT%H:%M:%S.%LZ}) for + * dates.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @return new instance of {@link DateToString}. + * @since 2.1 + */ + DateToString defaultFormat(); } } @@ -2269,6 +2336,20 @@ public class DateOperators { return new DateFromString(appendTimezone(argumentMap(), timezone)); } + /** + * Optionally set the date format to use. If not specified {@code %Y-%m-%dT%H:%M:%S.%LZ} is used.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param format must not be {@literal null}. + * @return new instance of {@link DateFromString}. + * @throws IllegalArgumentException if given {@literal format} is {@literal null}. + */ + public DateFromString withFormat(String format) { + + Assert.notNull(format, "Format must not be null!"); + return new DateFromString(append("format", format)); + } + @Override protected String getMongoMethod() { return "$dateFromString"; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java index 2d724c0fe..ff4079c9b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java @@ -1204,6 +1204,18 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { return this.operation.and(DateOperators.DateToString.dateOf(getRequiredName()).toString(format)); } + /** + * Generates a {@code $dateToString} expression that takes the date representation of the previously mentioned field + * using the server default format.
+ * strong>NOTE: Requires MongoDB 4.0 or later. + * + * @return + * @since 2.1 + */ + public ProjectionOperationBuilder dateAsFormattedString() { + return this.operation.and(DateOperators.DateToString.dateOf(getRequiredName()).defaultFormat()); + } + /** * Generates a {@code $let} expression that binds variables for use in the specified expression, and returns the * result of the expression. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java index 82622092f..5ce8ef1cd 100755 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java @@ -1317,6 +1317,14 @@ public class ProjectionOperationUnitTests { Document.parse("{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\" } } } }")); } + @Test // DATAMONGO-2047 + public void shouldRenderDateToStringWithoutFormatOption() { + + Document agg = project().and("date").dateAsFormattedString().as("time").toDocument(Aggregation.DEFAULT_CONTEXT); + + assertThat(agg).isEqualTo(Document.parse("{ $project: { time: { $dateToString: { date: \"$date\" } } } }")); + } + @Test // DATAMONGO-1536 public void shouldRenderDateToStringAggregationExpression() { @@ -1338,6 +1346,17 @@ public class ProjectionOperationUnitTests { "{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\", \"timezone\" : \"America/Chicago\" } } } } } }")); } + @Test // DATAMONGO-2047 + public void shouldRenderDateToStringWithOnNull() { + + Document agg = project() + .and(DateOperators.dateOf("date").toStringWithDefaultFormat().onNullReturnValueOf("fallback-field")).as("time") + .toDocument(Aggregation.DEFAULT_CONTEXT); + + assertThat(agg).isEqualTo(Document + .parse("{ $project: { time: { $dateToString: { date: \"$date\", \"onNull\" : \"$fallback-field\" } } } }")); + } + @Test // DATAMONGO-1536 public void shouldRenderSumAggregationExpression() { @@ -2037,6 +2056,16 @@ public class ProjectionOperationUnitTests { "{ $project : { newDate: { $dateFromString: { dateString : \"2017-02-08T12:10:40.787\", timezone : \"America/Chicago\" } } } }")); } + @Test // DATAMONGO-2047 + public void shouldRenderDateFromStringWithFormat() { + + Document agg = project().and(DateOperators.dateFromString("2017-02-08T12:10:40.787").withFormat("dd/mm/yyyy")) + .as("newDate").toDocument(Aggregation.DEFAULT_CONTEXT); + + assertThat(agg).isEqualTo(Document.parse( + "{ $project : { newDate: { $dateFromString: { dateString : \"2017-02-08T12:10:40.787\", format : \"dd/mm/yyyy\" } } } }")); + } + private static Document exctractOperation(String field, Document fromProjectClause) { return (Document) fromProjectClause.get(field); }