From 9764ce0147bddb83e5ff6b1e2b17eafb3e771cb1 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 9 Aug 2018 15:13:03 +0200 Subject: [PATCH] DATAMONGO-2049 - Add support for $ltrim, $rtrim, and $trim. Original pull request: #594. --- .../core/aggregation/StringOperators.java | 382 +++++++++++++++++- .../aggregation/StringOperatorsUnitTests.java | 146 +++++++ src/main/asciidoc/reference/mongodb.adoc | 2 +- 3 files changed, 527 insertions(+), 3 deletions(-) create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java index 17349ec29..6e431671a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java @@ -350,8 +350,7 @@ public class StringOperators { * @return */ public StrLenBytes length() { - return usesFieldRef() ? StrLenBytes.stringLengthOf(fieldReference) - : StrLenBytes.stringLengthOf(expression); + return usesFieldRef() ? StrLenBytes.stringLengthOf(fieldReference) : StrLenBytes.stringLengthOf(expression); } /** @@ -391,6 +390,132 @@ public class StringOperators { return usesFieldRef() ? SubstrCP.valueOf(fieldReference) : SubstrCP.valueOf(expression); } + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims whitespaces + * from the beginning and end.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @return new instance of {@link Trim}. + * @since 2.1 + */ + public Trim trim() { + return createTrim(); + } + + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims the given + * character sequence from the beginning and end.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param chars must not be {@literal null}. + * @return new instance of {@link Trim}. + * @since 2.1 + */ + public Trim trim(String chars) { + return trim().chars(chars); + } + + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims the character + * sequence resulting from the given {@link AggregationExpression} from the beginning and end.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link Trim}. + * @since 2.1 + */ + public Trim trim(AggregationExpression expression) { + return trim().charsOf(expression); + } + + private Trim createTrim() { + return usesFieldRef() ? Trim.valueOf(fieldReference) : Trim.valueOf(expression); + } + + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims whitespaces + * from the beginning.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @return new instance of {@link LTrim}. + * @since 2.1 + */ + public LTrim ltrim() { + return createLTrim(); + } + + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims the given + * character sequence from the beginning.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param chars must not be {@literal null}. + * @return new instance of {@link LTrim}. + * @since 2.1 + */ + public LTrim ltrim(String chars) { + return ltrim().chars(chars); + } + + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims the character + * sequence resulting from the given {@link AggregationExpression} from the beginning.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link LTrim}. + * @since 2.1 + */ + public LTrim ltrim(AggregationExpression expression) { + return ltrim().charsOf(expression); + } + + private LTrim createLTrim() { + return usesFieldRef() ? LTrim.valueOf(fieldReference) : LTrim.valueOf(expression); + } + + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims whitespaces + * from the end.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @return new instance of {@link RTrim}. + * @since 2.1 + */ + public RTrim rtrim() { + return createRTrim(); + } + + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims the given + * character sequence from the end.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param chars must not be {@literal null}. + * @return new instance of {@link RTrim}. + * @since 2.1 + */ + public RTrim rtrim(String chars) { + return rtrim().chars(chars); + } + + /** + * Creates new {@link AggregationExpression} that takes the associated string representation and trims the character + * sequence resulting from the given {@link AggregationExpression} from the end.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link RTrim}. + * @since 2.1 + */ + public RTrim rtrim(AggregationExpression expression) { + return rtrim().charsOf(expression); + } + + private RTrim createRTrim() { + return usesFieldRef() ? RTrim.valueOf(fieldReference) : RTrim.valueOf(expression); + } + private boolean usesFieldRef() { return fieldReference != null; } @@ -1072,4 +1197,257 @@ public class StringOperators { return new SubstrCP(append(Arrays.asList(start, nrOfChars))); } } + + /** + * {@link AggregationExpression} for {@code $trim} which removes whitespace or the specified characters from the + * beginning and end of a string.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @author Christoph Strobl + * @since 2.1 + */ + public static class Trim extends AbstractAggregationExpression { + + private Trim(Object value) { + super(value); + } + + /** + * Creates new {@link Trim} using the value of the provided {@link Field fieldReference} as {@literal input} value. + * + * @param fieldReference must not be {@literal null}. + * @return new instance of {@link LTrim}. + */ + public static Trim valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Trim(Collections.singletonMap("input", Fields.field(fieldReference))); + } + + /** + * Creates new {@link Trim} using the result of the provided {@link AggregationExpression} as {@literal input} + * value. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link Trim}. + */ + public static Trim valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Trim(Collections.singletonMap("input", expression)); + } + + /** + * Optional specify the character(s) to trim from the beginning. + * + * @param chars must not be {@literal null}. + * @return new instance of {@link Trim}. + */ + public Trim chars(String chars) { + + Assert.notNull(chars, "Chars must not be null!"); + return new Trim(append("chars", chars)); + } + + /** + * Optional specify the reference to the {@link Field field} holding the character values to trim from the + * beginning. + * + * @param fieldReference must not be {@literal null}. + * @return new instance of {@link Trim}. + */ + public Trim charsOf(String fieldReference) { + return new Trim(append("chars", Fields.field(fieldReference))); + } + + /** + * Optional specify the {@link AggregationExpression} evaluating to the character sequence to trim from the + * beginning. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link Trim}. + */ + public Trim charsOf(AggregationExpression expression) { + return new Trim(append("chars", expression)); + } + + /** + * Remove whitespace or the specified characters from the beginning of a string.
+ * + * @return new instance of {@link LTrim}. + */ + public LTrim left() { + return new LTrim(argumentMap()); + } + + /** + * Remove whitespace or the specified characters from the end of a string.
+ * + * @return new instance of {@link RTrim}. + */ + public RTrim right() { + return new RTrim(argumentMap()); + } + + @Override + protected String getMongoMethod() { + return "$trim"; + } + } + + /** + * {@link AggregationExpression} for {@code $ltrim} which removes whitespace or the specified characters from the + * beginning of a string.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @author Christoph Strobl + * @since 2.1 + */ + public static class LTrim extends AbstractAggregationExpression { + + private LTrim(Object value) { + super(value); + } + + /** + * Creates new {@link LTrim} using the value of the provided {@link Field fieldReference} as {@literal input} value. + * + * @param fieldReference must not be {@literal null}. + * @return new instance of {@link LTrim}. + */ + public static LTrim valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new LTrim(Collections.singletonMap("input", Fields.field(fieldReference))); + } + + /** + * Creates new {@link LTrim} using the result of the provided {@link AggregationExpression} as {@literal input} + * value. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link LTrim}. + */ + public static LTrim valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new LTrim(Collections.singletonMap("input", expression)); + } + + /** + * Optional specify the character(s) to trim from the beginning. + * + * @param chars must not be {@literal null}. + * @return new instance of {@link LTrim}. + */ + public LTrim chars(String chars) { + + Assert.notNull(chars, "Chars must not be null!"); + return new LTrim(append("chars", chars)); + } + + /** + * Optional specify the reference to the {@link Field field} holding the character values to trim from the + * beginning. + * + * @param fieldReference must not be {@literal null}. + * @return new instance of {@link LTrim}. + */ + public LTrim charsOf(String fieldReference) { + return new LTrim(append("chars", Fields.field(fieldReference))); + } + + /** + * Optional specify the {@link AggregationExpression} evaluating to the character sequence to trim from the + * beginning. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link LTrim}. + */ + public LTrim charsOf(AggregationExpression expression) { + return new LTrim(append("chars", expression)); + } + + @Override + protected String getMongoMethod() { + return "$ltrim"; + } + } + + /** + * {@link AggregationExpression} for {@code $rtrim} which removes whitespace or the specified characters from the end + * of a string.
+ * NOTE: Requires MongoDB 4.0 or later. + * + * @author Christoph Strobl + * @since 2.1 + */ + public static class RTrim extends AbstractAggregationExpression { + + private RTrim(Object value) { + super(value); + } + + /** + * Creates new {@link RTrim} using the value of the provided {@link Field fieldReference} as {@literal input} value. + * + * @param fieldReference must not be {@literal null}. + * @return new instance of {@link RTrim}. + */ + public static RTrim valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new RTrim(Collections.singletonMap("input", Fields.field(fieldReference))); + } + + /** + * Creates new {@link RTrim} using the result of the provided {@link AggregationExpression} as {@literal input} + * value. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link RTrim}. + */ + public static RTrim valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new RTrim(Collections.singletonMap("input", expression)); + } + + /** + * Optional specify the character(s) to trim from the end. + * + * @param chars must not be {@literal null}. + * @return new instance of {@link RTrim}. + */ + public RTrim chars(String chars) { + + Assert.notNull(chars, "Chars must not be null!"); + return new RTrim(append("chars", chars)); + } + + /** + * Optional specify the reference to the {@link Field field} holding the character values to trim from the end. + * + * @param fieldReference must not be {@literal null}. + * @return new instance of {@link RTrim}. + */ + public RTrim charsOf(String fieldReference) { + return new RTrim(append("chars", Fields.field(fieldReference))); + } + + /** + * Optional specify the {@link AggregationExpression} evaluating to the character sequence to trim from the end. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link RTrim}. + */ + public RTrim charsOf(AggregationExpression expression) { + return new RTrim(append("chars", expression)); + } + + @Override + protected String getMongoMethod() { + return "$rtrim"; + } + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java new file mode 100644 index 000000000..9bf7ff722 --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java @@ -0,0 +1,146 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import org.assertj.core.api.Assertions; +import org.bson.Document; +import org.junit.Test; + +/** + * Unit test for {@link StringOperators}. + * + * @author Christoph Strobl + * @currentRead Royal Assassin - Robin Hobb + */ +public class StringOperatorsUnitTests { + + static final String EXPRESSION_STRING = "{ \"$fitz\" : \"chivalry\" }"; + static final Document EXPRESSION_DOC = Document.parse(EXPRESSION_STRING); + static final AggregationExpression EXPRESSION = context -> EXPRESSION_DOC; + + @Test // DATAMONGO-2049 + public void shouldRenderTrim() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").trim().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderTrimForExpression() { + + Assertions.assertThat(StringOperators.valueOf(EXPRESSION).trim().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $trim: { \"input\" : " + EXPRESSION_STRING + " } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderTrimWithChars() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").trim("sh").toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderTrimWithCharsExpression() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").trim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderTrimLeft() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").trim().left().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderTrimLeftWithChars() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").trim("sh").left().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderTrimRight() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").trim().right().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderTrimRightWithChars() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").trim("sh").right().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderLTrim() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").ltrim().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderLTrimForExpression() { + + Assertions.assertThat(StringOperators.valueOf(EXPRESSION).ltrim().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $ltrim: { \"input\" : " + EXPRESSION_STRING + " } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderLTrimWithChars() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").ltrim("sh").toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderLTrimWithCharsExpression() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").ltrim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderRTrim() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").rtrim().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderRTrimForExpression() { + + Assertions.assertThat(StringOperators.valueOf(EXPRESSION).rtrim().toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $rtrim: { \"input\" : " + EXPRESSION_STRING + " } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderRTrimWithChars() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").rtrim("sh").toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ")); + } + + @Test // DATAMONGO-2049 + public void shouldRenderRTrimWithCharsExpression() { + + Assertions.assertThat(StringOperators.valueOf("shrewd").rtrim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ")); + } + +} diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index 33b20c9e8..6af4f5e44 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -2119,7 +2119,7 @@ At the time of this writing, we provide support for the following Aggregation Op | `abs`, `add` (*via `plus`), `ceil`, `divide`, `exp`, `floor`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `sqrt`, `subtract` (*via `minus`), `trunc` | String Aggregation Operators -| `concat`, `substr`, `toLower`, `toUpper`, `stcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP` +| `concat`, `substr`, `toLower`, `toUpper`, `stcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim` | Comparison Aggregation Operators | `eq` (*via: `is`), `gt`, `gte`, `lt`, `lte`, `ne`