diff --git a/pom.xml b/pom.xml index 657607148e..f213d523f4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2077-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index c2ff37b35c..b35a0b47f4 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2077-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index fd36f227c0..1368c9e8c6 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2077-SNAPSHOT ../pom.xml @@ -50,7 +50,7 @@ org.springframework.data spring-data-mongodb - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2077-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index fc8d28a2b6..146da343ad 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2077-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index f3c85a046a..f8db2870bc 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2077-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java index 4913b04604..c8033be50a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java @@ -98,6 +98,11 @@ public class MethodReferenceNode extends ExpressionNode { map.put("strLenBytes", singleArgumentAggregationMethodReference().forOperator("$strLenBytes")); map.put("strLenCP", singleArgumentAggregationMethodReference().forOperator("$strLenCP")); map.put("substrCP", arrayArgumentAggregationMethodReference().forOperator("$substrCP")); + map.put("trim", mapArgumentAggregationMethodReference().forOperator("$trim").mappingParametersTo("input", "chars")); + map.put("ltrim", + mapArgumentAggregationMethodReference().forOperator("$ltrim").mappingParametersTo("input", "chars")); + map.put("rtrim", + mapArgumentAggregationMethodReference().forOperator("$rtrim").mappingParametersTo("input", "chars")); // TEXT SEARCH OPERATORS map.put("meta", singleArgumentAggregationMethodReference().forOperator("$meta")); @@ -116,6 +121,9 @@ public class MethodReferenceNode extends ExpressionNode { map.put("zip", mapArgumentAggregationMethodReference().forOperator("$zip").mappingParametersTo("inputs", "useLongestLength", "defaults")); map.put("in", arrayArgumentAggregationMethodReference().forOperator("$in")); + map.put("arrayToObject", singleArgumentAggregationMethodReference().forOperator("$arrayToObject")); + map.put("indexOfArray", arrayArgumentAggregationMethodReference().forOperator("$indexOfArray")); + map.put("range", arrayArgumentAggregationMethodReference().forOperator("$range")); // VARIABLE OPERATORS map.put("map", mapArgumentAggregationMethodReference().forOperator("$map") // @@ -138,6 +146,15 @@ public class MethodReferenceNode extends ExpressionNode { map.put("millisecond", singleArgumentAggregationMethodReference().forOperator("$millisecond")); map.put("dateToString", mapArgumentAggregationMethodReference().forOperator("$dateToString") // .mappingParametersTo("format", "date")); + map.put("dateFromString", mapArgumentAggregationMethodReference().forOperator("$dateFromString") // + .mappingParametersTo("dateString", "format", "timezone", "onError", "onNull")); + map.put("dateFromParts", mapArgumentAggregationMethodReference().forOperator("$dateFromParts") + .mappingParametersTo("year", "month", "day", "hour", "minute", "second", "milliseconds", "timezone")); + map.put("isoDateFromParts", + mapArgumentAggregationMethodReference().forOperator("$dateFromParts").mappingParametersTo("isoWeekYear", + "isoWeek", "isoDayOfWeek", "hour", "minute", "second", "milliseconds", "timezone")); + map.put("dateToParts", mapArgumentAggregationMethodReference().forOperator("$dateToParts") // + .mappingParametersTo("date", "timezone", "iso8601")); map.put("isoDayOfWeek", singleArgumentAggregationMethodReference().forOperator("$isoDayOfWeek")); map.put("isoWeek", singleArgumentAggregationMethodReference().forOperator("$isoWeek")); map.put("isoWeekYear", singleArgumentAggregationMethodReference().forOperator("$isoWeekYear")); @@ -162,6 +179,24 @@ public class MethodReferenceNode extends ExpressionNode { // TYPE OPERATORS map.put("type", singleArgumentAggregationMethodReference().forOperator("$type")); + // OBJECT OPERATORS + map.put("objectToArray", singleArgumentAggregationMethodReference().forOperator("$objectToArray")); + map.put("mergeObjects", arrayArgumentAggregationMethodReference().forOperator("$mergeObjects")); + + // CONVERT OPERATORS + map.put("convert", mapArgumentAggregationMethodReference().forOperator("$convert") // + .mappingParametersTo("input", "to", "onError", "onNull")); + map.put("toBool", singleArgumentAggregationMethodReference().forOperator("$toBool")); + map.put("toDate", singleArgumentAggregationMethodReference().forOperator("$toDate")); + map.put("toDecimal", singleArgumentAggregationMethodReference().forOperator("$toDecimal")); + map.put("toDouble", singleArgumentAggregationMethodReference().forOperator("$toDouble")); + map.put("toInt", singleArgumentAggregationMethodReference().forOperator("$toInt")); + map.put("toLong", singleArgumentAggregationMethodReference().forOperator("$toLong")); + map.put("toObjectId", singleArgumentAggregationMethodReference().forOperator("$toObjectId")); + map.put("toString", singleArgumentAggregationMethodReference().forOperator("$toString")); + + + FUNCTIONS = Collections.unmodifiableMap(map); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java index 09974c9836..c3b1f72285 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java @@ -719,6 +719,216 @@ public void shouldRenderMethodRefereneType() { assertThat(transform("type(a)"), is(Document.parse("{ \"$type\" : \"$a\"}"))); } + @Test // DATAMONGO-2077 + public void shouldRenderArrayToObjectWithFieldReference() { + assertThat(transform("arrayToObject(field)"), is(Document.parse("{ \"$arrayToObject\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderArrayToObjectWithArray() { + + assertThat(transform("arrayToObject(new String[]{'key', 'value'})"), + is(Document.parse("{ \"$arrayToObject\" : [\"key\", \"value\"]}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderObjectToArrayWithFieldReference() { + assertThat(transform("objectToArray(field)"), is(Document.parse("{ \"$objectToArray\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderMergeObjects() { + + assertThat(transform("mergeObjects(field1, $$ROOT)"), + is(Document.parse("{ \"$mergeObjects\" : [\"$field1\", \"$$ROOT\"]}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderTrimWithoutChars() { + assertThat(transform("trim(field)"), is(Document.parse("{ \"$trim\" : {\"input\" : \"$field\"}}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderTrimWithChars() { + + assertThat(transform("trim(field, 'ie')"), + is(Document.parse("{ \"$trim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderTrimWithCharsFromFieldReference() { + + assertThat(transform("trim(field1, field2)"), + is(Document.parse("{ \"$trim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderLtrimWithoutChars() { + assertThat(transform("ltrim(field)"), is(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field\"}}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderLtrimWithChars() { + + assertThat(transform("ltrim(field, 'ie')"), + is(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderLtrimWithCharsFromFieldReference() { + + assertThat(transform("ltrim(field1, field2)"), + is(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderRtrimWithoutChars() { + assertThat(transform("rtrim(field)"), is(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field\"}}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderRtrimWithChars() { + + assertThat(transform("rtrim(field, 'ie')"), + is(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderRtrimWithCharsFromFieldReference() { + + assertThat(transform("rtrim(field1, field2)"), + is(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderConvertWithoutOptionalParameters() { + + assertThat(transform("convert(field, 'string')"), + is(Document.parse("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"string\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderConvertWithOnError() { + + assertThat(transform("convert(field, 'int', 'Not an integer.')"), is(Document + .parse("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderConvertWithOnErrorOnNull() { + + assertThat(transform("convert(field, 'int', 'Not an integer.', -1)"), is(Document.parse( + "{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\", \"onNull\" : -1 }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderToBool() { + assertThat(transform("toBool(field)"), is(Document.parse("{ \"$toBool\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderToDate() { + assertThat(transform("toDate(field)"), is(Document.parse("{ \"$toDate\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderToDecimal() { + assertThat(transform("toDecimal(field)"), is(Document.parse("{ \"$toDecimal\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderToDouble() { + assertThat(transform("toDouble(field)"), is(Document.parse("{ \"$toDouble\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderToInt() { + assertThat(transform("toInt(field)"), is(Document.parse("{ \"$toInt\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderToLong() { + assertThat(transform("toLong(field)"), is(Document.parse("{ \"$toLong\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderToObjectId() { + assertThat(transform("toObjectId(field)"), is(Document.parse("{ \"$toObjectId\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderToString() { + assertThat(transform("toString(field)"), is(Document.parse("{ \"$toString\" : \"$field\"}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderDateFromStringWithoutOptionalParameters() { + + assertThat(transform("dateFromString(field)"), + is(Document.parse("{ \"$dateFromString\" : {\"dateString\" : \"$field\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderDateFromStringWithFormat() { + + assertThat(transform("dateFromString(field, 'DD-MM-YYYY')"), + is(Document.parse("{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderDateFromStringWithFormatAndTimezone() { + + assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC')"), is(Document.parse( + "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\" }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderDateFromStringWithFormatTimezoneAndOnError() { + + assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1)"), is(Document.parse( + "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1 }}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderDateFromStringWithFormatTimezoneOnErrorAndOnNull() { + + assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1, -2)"), is(Document.parse( + "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1, \"onNull\" : -2}}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderDateFromParts() { + + assertThat(transform("dateFromParts(y, m, d, h, mm, s, ms, 'UTC')"), is(Document.parse( + "{ \"$dateFromParts\" : {\"year\" : \"$y\", \"month\" : \"$m\", \"day\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"milliseconds\" : \"$ms\", \"timezone\" : \"UTC\"}}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderIsoDateFromParts() { + + assertThat(transform("isoDateFromParts(y, m, d, h, mm, s, ms, 'UTC')"), is(Document.parse( + "{ \"$dateFromParts\" : {\"isoWeekYear\" : \"$y\", \"isoWeek\" : \"$m\", \"isoDayOfWeek\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"milliseconds\" : \"$ms\", \"timezone\" : \"UTC\"}}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderDateToParts() { + + assertThat(transform("dateToParts(field, 'UTC', false)"), is( + Document.parse("{ \"$dateToParts\" : {\"date\" : \"$field\", \"timezone\" : \"UTC\", \"iso8601\" : false}}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderIndexOfArray() { + + assertThat(transform("indexOfArray(field, 2)"), is(Document.parse("{ \"$indexOfArray\" : [\"$field\", 2 ]}"))); + } + + @Test // DATAMONGO-2077 + public void shouldRenderRange() { + + assertThat(transform("range(0, 10, 2)"), is(Document.parse("{ \"$range\" : [0, 10, 2 ]}"))); + } + private Object transform(String expression, Object... params) { Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params); return result == null ? null : (!(result instanceof org.bson.Document) ? result.toString() : result);