Add support for $replaceOne & $replaceAll aggregation operators.

Closes: #3695
Original Pull Request: #3861
This commit is contained in:
Divya Srivastava
2021-10-17 14:11:11 +05:30
committed by Christoph Strobl
parent 030f12023c
commit 494c22b192
4 changed files with 321 additions and 0 deletions

View File

@@ -686,10 +686,28 @@ public class StringOperators {
private RegexMatch createRegexMatch() {
return usesFieldRef() ? RegexMatch.valueOf(fieldReference) : RegexMatch.valueOf(expression);
}
public ReplaceOne replaceOne(String find,String replacement) {
return createReplaceOne().find(find).replacement(replacement);
}
private ReplaceOne createReplaceOne() {
return usesFieldRef() ? ReplaceOne.valueOf(fieldReference) : ReplaceOne.valueOf(expression);
}
public ReplaceAll replaceAll(String find,String replacement) {
return createReplaceAll().find(find).replacement(replacement);
}
private ReplaceAll createReplaceAll() {
return usesFieldRef() ? ReplaceAll.valueOf(fieldReference) : ReplaceAll.valueOf(expression);
}
private boolean usesFieldRef() {
return fieldReference != null;
}
}
/**
@@ -2078,4 +2096,260 @@ public class StringOperators {
return "$regexMatch";
}
}
/**
* {@link AggregationExpression} for {@code $replaceOne} which replaces the first instance of a search string in an
* input string with a replacement string. <br />
* <strong>NOTE:</strong> Requires MongoDB 4.4 or later.
*/
public static class ReplaceOne extends AbstractAggregationExpression {
protected ReplaceOne(Object value) {
super(value);
}
/**
* Creates new {@link ReplaceOne} 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 ReplaceOne}.
*/
public static ReplaceOne valueOf(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new ReplaceOne(Collections.singletonMap("input", Fields.field(fieldReference)));
}
/**
* Creates new {@link ReplaceOne} using the result of the provided {@link AggregationExpression} as {@literal input}
* value.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link ReplaceOne}.
*/
public static ReplaceOne valueOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new ReplaceOne(Collections.singletonMap("input", expression));
}
/**
* The string to use to replace the first matched instance of {@code find} in input.
*
* @param replacement must not be {@literal null}.
* @return new instance of {@link ReplaceOne}.
*/
public ReplaceOne replacement(String replacement) {
Assert.notNull(replacement, "Replacement must not be null!");
return new ReplaceOne(append("replacement", replacement));
}
/**
* Specifies the reference to the {@link Field field} holding the string to use to replace the first matched
* instance of {@code find} in input.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link ReplaceOne}.
*/
public ReplaceOne replacementOf(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new ReplaceOne(append("replacement", Fields.field(fieldReference)));
}
/**
* Specifies the {@link AggregationExpression} evaluating to the string to use to replace the first matched instance
* of {@code find} in {@code input}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link ReplaceOne}.
*/
public ReplaceOne replacementOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new ReplaceOne(append("replacement", expression));
}
/**
* The string to search for within the given input field.
*
* @param find must not be {@literal null}.
* @return new instance of {@link ReplaceOne}.
*/
public ReplaceOne find(String searchStr) {
Assert.notNull(searchStr, "Search string must not be null!");
Map<String, Object> search = append("find", searchStr);
return new ReplaceOne(search);
}
/**
* Specify the reference to the {@link Field field} holding the string to search for within the given input field.
*
* @param find must not be {@literal null}.
* @return new instance of {@link ReplaceOne}.
*/
public ReplaceOne findOf(String fieldReference) {
Assert.notNull(fieldReference, "fieldReference must not be null!");
return new ReplaceOne(append("find", fieldReference));
}
/**
* Specify the {@link AggregationExpression} evaluating to the the string to search for within the given input
* field.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link ReplaceOne}.
*/
public ReplaceOne findOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new ReplaceOne(append("find", expression));
}
@Override
protected String getMongoMethod() {
return "$replaceOne";
}
}
/**
* {@link AggregationExpression} for {@code $replaceAll} which replaces all instances of a search string in an input
* string with a replacement string. <br />
* <strong>NOTE:</strong> Requires MongoDB 4.4 or later.
*/
public static class ReplaceAll extends AbstractAggregationExpression {
protected ReplaceAll(Object value) {
super(value);
}
/**
* Creates new {@link ReplaceAll} 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 ReplaceAll}.
*/
public static ReplaceAll valueOf(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new ReplaceAll(Collections.singletonMap("input", Fields.field(fieldReference)));
}
/**
* Creates new {@link ReplaceAll} using the result of the provided {@link AggregationExpression} as {@literal input}
* value.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link ReplaceAll}.
*/
public static ReplaceAll valueOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new ReplaceAll(Collections.singletonMap("input", expression));
}
/**
* The string to use to replace the first matched instance of {@code find} in input.
*
* @param replacement must not be {@literal null}.
* @return new instance of {@link ReplaceAll}.
*/
public ReplaceAll replacement(String replacement) {
Assert.notNull(replacement, "Replacement must not be null!");
return new ReplaceAll(append("replacement", replacement));
}
/**
* Specifies the reference to the {@link Field field} holding the string to use to replace the first matched
* instance of {@code find} in input.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link ReplaceAll}.
*/
public ReplaceAll replacementOf(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new ReplaceAll(append("replacement", Fields.field(fieldReference)));
}
/**
* Specifies the {@link AggregationExpression} evaluating to the string to use to replace the first matched instance
* of {@code find} in input.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link ReplaceAll}.
*/
public ReplaceAll replacementOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new ReplaceAll(append("replacement", expression));
}
/**
* The string to search for within the given input field.
*
* @param find must not be {@literal null}.
* @return new instance of {@link ReplaceAll}.
*/
public ReplaceAll find(String searchStr) {
Assert.notNull(searchStr, "Search string must not be null!");
Map<String, Object> search = append("find", searchStr);
return new ReplaceAll(search);
}
/**
* Specify the reference to the {@link Field field} holding the string to search for within the given input field.
*
* @param find must not be {@literal null}.
* @return new instance of {@link ReplaceAll}.
*/
public ReplaceAll findOf(String fieldReference) {
Assert.notNull(fieldReference, "fieldReference must not be null!");
return new ReplaceAll(append("find", fieldReference));
}
/**
* Specify the {@link AggregationExpression} evaluating to the string to search for within the given input field.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link ReplaceAll}.
*/
public ReplaceAll findOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new ReplaceAll(append("find", expression));
}
@Override
protected String getMongoMethod() {
return "$replaceAll";
}
}
}

View File

@@ -126,6 +126,8 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("regexFind", mapArgRef().forOperator("$regexFind").mappingParametersTo("input", "regex" , "options"));
map.put("regexFindAll", mapArgRef().forOperator("$regexFindAll").mappingParametersTo("input", "regex" , "options"));
map.put("regexMatch", mapArgRef().forOperator("$regexMatch").mappingParametersTo("input", "regex" , "options"));
map.put("replaceOne", mapArgRef().forOperator("$replaceOne").mappingParametersTo("input", "find" , "replacement"));
map.put("replaceAll", mapArgRef().forOperator("$replaceAll").mappingParametersTo("input", "find" , "replacement"));
// TEXT SEARCH OPERATORS
map.put("meta", singleArgRef().forOperator("$meta"));

View File

@@ -864,6 +864,20 @@ public class SpelExpressionTransformerUnitTests {
assertThat(transform("regexMatch(field1,'e',field2)"))
.isEqualTo("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}");
}
@Test
void shouldRenderReplaceOne() {
assertThat(transform("replaceOne(field,'bar','baz')"))
.isEqualTo("{ \"$replaceOne\" : {\"input\" : \"$field\" , \"find\" : \"bar\" , \"replacement\" : \"baz\"}}");
}
@Test
void shouldRenderReplaceAll() {
assertThat(transform("replaceAll(field,'bar','baz')"))
.isEqualTo("{ \"$replaceAll\" : {\"input\" : \"$field\" , \"find\" : \"bar\" , \"replacement\" : \"baz\"}}");
}
@Test // DATAMONGO-2077

View File

@@ -281,4 +281,35 @@ class StringOperatorsUnitTests {
.isEqualTo("{ $regexFind: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING
+ " } } ");
}
@Test
void shouldRenderReplaceOne() {
assertThat(StringOperators.valueOf("bar").replaceOne("foobar","baz").toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $replaceOne : {\"find\" : \"foobar\", \"input\" : \"$bar\", \"replacement\" : \"baz\"}}");
}
@Test
void shouldRenderReplaceOneForExpression() {
assertThat(StringOperators.valueOf(EXPRESSION).replaceOne("a","s").toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $replaceOne : {\"find\" : \"a\", \"input\" : " + EXPRESSION_STRING + ", \"replacement\" : \"s\"}}");
}
@Test
void shouldRenderReplaceAll() {
assertThat(StringOperators.valueOf("bar").replaceAll("foobar","baz").toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $replaceAll : {\"find\" : \"foobar\", \"input\" : \"$bar\", \"replacement\" : \"baz\"}}");
}
@Test
void shouldRenderReplaceAllForExpression() {
assertThat(StringOperators.valueOf(EXPRESSION).replaceAll("a","s").toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $replaceAll : {\"find\" : \"a\", \"input\" : " + EXPRESSION_STRING + ", \"replacement\" : \"s\"}}");
}
}