Skip to content

Commit 3811ddf

Browse files
christophstroblmp911de
authored andcommitted
DATAMONGO-2077 - Enhance SpEL aggregation support.
Added aggregation method detection for: - trim, ltrim, trim - arrayToObject, objectToArray, indexOfArray - dateFromString, dateFromParts, isoDateFromParts, dateToParts - mergeObjects - convert, toBool, toDate, toDecimal, toDouble, toInt, toLong, toObjectId, toString - range Original pull request: #639.
1 parent 9cc7fc2 commit 3811ddf

File tree

2 files changed

+245
-0
lines changed

2 files changed

+245
-0
lines changed

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ public class MethodReferenceNode extends ExpressionNode {
9898
map.put("strLenBytes", singleArgumentAggregationMethodReference().forOperator("$strLenBytes"));
9999
map.put("strLenCP", singleArgumentAggregationMethodReference().forOperator("$strLenCP"));
100100
map.put("substrCP", arrayArgumentAggregationMethodReference().forOperator("$substrCP"));
101+
map.put("trim", mapArgumentAggregationMethodReference().forOperator("$trim").mappingParametersTo("input", "chars"));
102+
map.put("ltrim",
103+
mapArgumentAggregationMethodReference().forOperator("$ltrim").mappingParametersTo("input", "chars"));
104+
map.put("rtrim",
105+
mapArgumentAggregationMethodReference().forOperator("$rtrim").mappingParametersTo("input", "chars"));
101106

102107
// TEXT SEARCH OPERATORS
103108
map.put("meta", singleArgumentAggregationMethodReference().forOperator("$meta"));
@@ -116,6 +121,9 @@ public class MethodReferenceNode extends ExpressionNode {
116121
map.put("zip", mapArgumentAggregationMethodReference().forOperator("$zip").mappingParametersTo("inputs",
117122
"useLongestLength", "defaults"));
118123
map.put("in", arrayArgumentAggregationMethodReference().forOperator("$in"));
124+
map.put("arrayToObject", singleArgumentAggregationMethodReference().forOperator("$arrayToObject"));
125+
map.put("indexOfArray", arrayArgumentAggregationMethodReference().forOperator("$indexOfArray"));
126+
map.put("range", arrayArgumentAggregationMethodReference().forOperator("$range"));
119127

120128
// VARIABLE OPERATORS
121129
map.put("map", mapArgumentAggregationMethodReference().forOperator("$map") //
@@ -138,6 +146,15 @@ public class MethodReferenceNode extends ExpressionNode {
138146
map.put("millisecond", singleArgumentAggregationMethodReference().forOperator("$millisecond"));
139147
map.put("dateToString", mapArgumentAggregationMethodReference().forOperator("$dateToString") //
140148
.mappingParametersTo("format", "date"));
149+
map.put("dateFromString", mapArgumentAggregationMethodReference().forOperator("$dateFromString") //
150+
.mappingParametersTo("dateString", "format", "timezone", "onError", "onNull"));
151+
map.put("dateFromParts", mapArgumentAggregationMethodReference().forOperator("$dateFromParts")
152+
.mappingParametersTo("year", "month", "day", "hour", "minute", "second", "milliseconds", "timezone"));
153+
map.put("isoDateFromParts",
154+
mapArgumentAggregationMethodReference().forOperator("$dateFromParts").mappingParametersTo("isoWeekYear",
155+
"isoWeek", "isoDayOfWeek", "hour", "minute", "second", "milliseconds", "timezone"));
156+
map.put("dateToParts", mapArgumentAggregationMethodReference().forOperator("$dateToParts") //
157+
.mappingParametersTo("date", "timezone", "iso8601"));
141158
map.put("isoDayOfWeek", singleArgumentAggregationMethodReference().forOperator("$isoDayOfWeek"));
142159
map.put("isoWeek", singleArgumentAggregationMethodReference().forOperator("$isoWeek"));
143160
map.put("isoWeekYear", singleArgumentAggregationMethodReference().forOperator("$isoWeekYear"));
@@ -162,6 +179,24 @@ public class MethodReferenceNode extends ExpressionNode {
162179
// TYPE OPERATORS
163180
map.put("type", singleArgumentAggregationMethodReference().forOperator("$type"));
164181

182+
// OBJECT OPERATORS
183+
map.put("objectToArray", singleArgumentAggregationMethodReference().forOperator("$objectToArray"));
184+
map.put("mergeObjects", arrayArgumentAggregationMethodReference().forOperator("$mergeObjects"));
185+
186+
// CONVERT OPERATORS
187+
map.put("convert", mapArgumentAggregationMethodReference().forOperator("$convert") //
188+
.mappingParametersTo("input", "to", "onError", "onNull"));
189+
map.put("toBool", singleArgumentAggregationMethodReference().forOperator("$toBool"));
190+
map.put("toDate", singleArgumentAggregationMethodReference().forOperator("$toDate"));
191+
map.put("toDecimal", singleArgumentAggregationMethodReference().forOperator("$toDecimal"));
192+
map.put("toDouble", singleArgumentAggregationMethodReference().forOperator("$toDouble"));
193+
map.put("toInt", singleArgumentAggregationMethodReference().forOperator("$toInt"));
194+
map.put("toLong", singleArgumentAggregationMethodReference().forOperator("$toLong"));
195+
map.put("toObjectId", singleArgumentAggregationMethodReference().forOperator("$toObjectId"));
196+
map.put("toString", singleArgumentAggregationMethodReference().forOperator("$toString"));
197+
198+
199+
165200
FUNCTIONS = Collections.unmodifiableMap(map);
166201
}
167202

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

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,216 @@ public void shouldRenderMethodRefereneType() {
719719
assertThat(transform("type(a)"), is(Document.parse("{ \"$type\" : \"$a\"}")));
720720
}
721721

722+
@Test // DATAMONGO-2077
723+
public void shouldRenderArrayToObjectWithFieldReference() {
724+
assertThat(transform("arrayToObject(field)"), is(Document.parse("{ \"$arrayToObject\" : \"$field\"}")));
725+
}
726+
727+
@Test // DATAMONGO-2077
728+
public void shouldRenderArrayToObjectWithArray() {
729+
730+
assertThat(transform("arrayToObject(new String[]{'key', 'value'})"),
731+
is(Document.parse("{ \"$arrayToObject\" : [\"key\", \"value\"]}")));
732+
}
733+
734+
@Test // DATAMONGO-2077
735+
public void shouldRenderObjectToArrayWithFieldReference() {
736+
assertThat(transform("objectToArray(field)"), is(Document.parse("{ \"$objectToArray\" : \"$field\"}")));
737+
}
738+
739+
@Test // DATAMONGO-2077
740+
public void shouldRenderMergeObjects() {
741+
742+
assertThat(transform("mergeObjects(field1, $$ROOT)"),
743+
is(Document.parse("{ \"$mergeObjects\" : [\"$field1\", \"$$ROOT\"]}")));
744+
}
745+
746+
@Test // DATAMONGO-2077
747+
public void shouldRenderTrimWithoutChars() {
748+
assertThat(transform("trim(field)"), is(Document.parse("{ \"$trim\" : {\"input\" : \"$field\"}}")));
749+
}
750+
751+
@Test // DATAMONGO-2077
752+
public void shouldRenderTrimWithChars() {
753+
754+
assertThat(transform("trim(field, 'ie')"),
755+
is(Document.parse("{ \"$trim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}")));
756+
}
757+
758+
@Test // DATAMONGO-2077
759+
public void shouldRenderTrimWithCharsFromFieldReference() {
760+
761+
assertThat(transform("trim(field1, field2)"),
762+
is(Document.parse("{ \"$trim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}")));
763+
}
764+
765+
@Test // DATAMONGO-2077
766+
public void shouldRenderLtrimWithoutChars() {
767+
assertThat(transform("ltrim(field)"), is(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field\"}}")));
768+
}
769+
770+
@Test // DATAMONGO-2077
771+
public void shouldRenderLtrimWithChars() {
772+
773+
assertThat(transform("ltrim(field, 'ie')"),
774+
is(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}")));
775+
}
776+
777+
@Test // DATAMONGO-2077
778+
public void shouldRenderLtrimWithCharsFromFieldReference() {
779+
780+
assertThat(transform("ltrim(field1, field2)"),
781+
is(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}")));
782+
}
783+
784+
@Test // DATAMONGO-2077
785+
public void shouldRenderRtrimWithoutChars() {
786+
assertThat(transform("rtrim(field)"), is(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field\"}}")));
787+
}
788+
789+
@Test // DATAMONGO-2077
790+
public void shouldRenderRtrimWithChars() {
791+
792+
assertThat(transform("rtrim(field, 'ie')"),
793+
is(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}")));
794+
}
795+
796+
@Test // DATAMONGO-2077
797+
public void shouldRenderRtrimWithCharsFromFieldReference() {
798+
799+
assertThat(transform("rtrim(field1, field2)"),
800+
is(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}")));
801+
}
802+
803+
@Test // DATAMONGO-2077
804+
public void shouldRenderConvertWithoutOptionalParameters() {
805+
806+
assertThat(transform("convert(field, 'string')"),
807+
is(Document.parse("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"string\" }}")));
808+
}
809+
810+
@Test // DATAMONGO-2077
811+
public void shouldRenderConvertWithOnError() {
812+
813+
assertThat(transform("convert(field, 'int', 'Not an integer.')"), is(Document
814+
.parse("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\" }}")));
815+
}
816+
817+
@Test // DATAMONGO-2077
818+
public void shouldRenderConvertWithOnErrorOnNull() {
819+
820+
assertThat(transform("convert(field, 'int', 'Not an integer.', -1)"), is(Document.parse(
821+
"{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\", \"onNull\" : -1 }}")));
822+
}
823+
824+
@Test // DATAMONGO-2077
825+
public void shouldRenderToBool() {
826+
assertThat(transform("toBool(field)"), is(Document.parse("{ \"$toBool\" : \"$field\"}")));
827+
}
828+
829+
@Test // DATAMONGO-2077
830+
public void shouldRenderToDate() {
831+
assertThat(transform("toDate(field)"), is(Document.parse("{ \"$toDate\" : \"$field\"}")));
832+
}
833+
834+
@Test // DATAMONGO-2077
835+
public void shouldRenderToDecimal() {
836+
assertThat(transform("toDecimal(field)"), is(Document.parse("{ \"$toDecimal\" : \"$field\"}")));
837+
}
838+
839+
@Test // DATAMONGO-2077
840+
public void shouldRenderToDouble() {
841+
assertThat(transform("toDouble(field)"), is(Document.parse("{ \"$toDouble\" : \"$field\"}")));
842+
}
843+
844+
@Test // DATAMONGO-2077
845+
public void shouldRenderToInt() {
846+
assertThat(transform("toInt(field)"), is(Document.parse("{ \"$toInt\" : \"$field\"}")));
847+
}
848+
849+
@Test // DATAMONGO-2077
850+
public void shouldRenderToLong() {
851+
assertThat(transform("toLong(field)"), is(Document.parse("{ \"$toLong\" : \"$field\"}")));
852+
}
853+
854+
@Test // DATAMONGO-2077
855+
public void shouldRenderToObjectId() {
856+
assertThat(transform("toObjectId(field)"), is(Document.parse("{ \"$toObjectId\" : \"$field\"}")));
857+
}
858+
859+
@Test // DATAMONGO-2077
860+
public void shouldRenderToString() {
861+
assertThat(transform("toString(field)"), is(Document.parse("{ \"$toString\" : \"$field\"}")));
862+
}
863+
864+
@Test // DATAMONGO-2077
865+
public void shouldRenderDateFromStringWithoutOptionalParameters() {
866+
867+
assertThat(transform("dateFromString(field)"),
868+
is(Document.parse("{ \"$dateFromString\" : {\"dateString\" : \"$field\" }}")));
869+
}
870+
871+
@Test // DATAMONGO-2077
872+
public void shouldRenderDateFromStringWithFormat() {
873+
874+
assertThat(transform("dateFromString(field, 'DD-MM-YYYY')"),
875+
is(Document.parse("{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\" }}")));
876+
}
877+
878+
@Test // DATAMONGO-2077
879+
public void shouldRenderDateFromStringWithFormatAndTimezone() {
880+
881+
assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC')"), is(Document.parse(
882+
"{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\" }}")));
883+
}
884+
885+
@Test // DATAMONGO-2077
886+
public void shouldRenderDateFromStringWithFormatTimezoneAndOnError() {
887+
888+
assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1)"), is(Document.parse(
889+
"{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1 }}")));
890+
}
891+
892+
@Test // DATAMONGO-2077
893+
public void shouldRenderDateFromStringWithFormatTimezoneOnErrorAndOnNull() {
894+
895+
assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1, -2)"), is(Document.parse(
896+
"{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1, \"onNull\" : -2}}")));
897+
}
898+
899+
@Test // DATAMONGO-2077
900+
public void shouldRenderDateFromParts() {
901+
902+
assertThat(transform("dateFromParts(y, m, d, h, mm, s, ms, 'UTC')"), is(Document.parse(
903+
"{ \"$dateFromParts\" : {\"year\" : \"$y\", \"month\" : \"$m\", \"day\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"milliseconds\" : \"$ms\", \"timezone\" : \"UTC\"}}")));
904+
}
905+
906+
@Test // DATAMONGO-2077
907+
public void shouldRenderIsoDateFromParts() {
908+
909+
assertThat(transform("isoDateFromParts(y, m, d, h, mm, s, ms, 'UTC')"), is(Document.parse(
910+
"{ \"$dateFromParts\" : {\"isoWeekYear\" : \"$y\", \"isoWeek\" : \"$m\", \"isoDayOfWeek\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"milliseconds\" : \"$ms\", \"timezone\" : \"UTC\"}}")));
911+
}
912+
913+
@Test // DATAMONGO-2077
914+
public void shouldRenderDateToParts() {
915+
916+
assertThat(transform("dateToParts(field, 'UTC', false)"), is(
917+
Document.parse("{ \"$dateToParts\" : {\"date\" : \"$field\", \"timezone\" : \"UTC\", \"iso8601\" : false}}")));
918+
}
919+
920+
@Test // DATAMONGO-2077
921+
public void shouldRenderIndexOfArray() {
922+
923+
assertThat(transform("indexOfArray(field, 2)"), is(Document.parse("{ \"$indexOfArray\" : [\"$field\", 2 ]}")));
924+
}
925+
926+
@Test // DATAMONGO-2077
927+
public void shouldRenderRange() {
928+
929+
assertThat(transform("range(0, 10, 2)"), is(Document.parse("{ \"$range\" : [0, 10, 2 ]}")));
930+
}
931+
722932
private Object transform(String expression, Object... params) {
723933
Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
724934
return result == null ? null : (!(result instanceof org.bson.Document) ? result.toString() : result);

0 commit comments

Comments
 (0)