mirror of
https://github.com/apache/nifi.git
synced 2025-03-04 08:29:55 +00:00
NIFI-6782: Added repeat() String EL function
NIFI-6782: Fixed intermittent unit test failure This closes #3825 Signed-off-by: Mike Thomsen <mthomsen@apache.org>
This commit is contained in:
parent
c72a5618c0
commit
f1be730c94
@ -182,6 +182,7 @@ JOIN : 'join';
|
|||||||
TO_LITERAL : 'literal';
|
TO_LITERAL : 'literal';
|
||||||
JSON_PATH : 'jsonPath';
|
JSON_PATH : 'jsonPath';
|
||||||
JSON_PATH_DELETE : 'jsonPathDelete';
|
JSON_PATH_DELETE : 'jsonPathDelete';
|
||||||
|
REPEAT : 'repeat';
|
||||||
|
|
||||||
// 2 arg functions
|
// 2 arg functions
|
||||||
SUBSTRING : 'substring';
|
SUBSTRING : 'substring';
|
||||||
|
@ -79,7 +79,7 @@ oneArgString : ((SUBSTRING_BEFORE | SUBSTRING_BEFORE_LAST | SUBSTRING_AFTER | SU
|
|||||||
PREPEND | APPEND | STARTS_WITH | ENDS_WITH | CONTAINS | JOIN | JSON_PATH | JSON_PATH_DELETE | FROM_RADIX) LPAREN! anyArg RPAREN!) |
|
PREPEND | APPEND | STARTS_WITH | ENDS_WITH | CONTAINS | JOIN | JSON_PATH | JSON_PATH_DELETE | FROM_RADIX) LPAREN! anyArg RPAREN!) |
|
||||||
(TO_RADIX LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
|
(TO_RADIX LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
|
||||||
twoArgString : ((REPLACE | REPLACE_FIRST | REPLACE_ALL | IF_ELSE | JSON_PATH_SET | JSON_PATH_ADD) LPAREN! anyArg COMMA! anyArg RPAREN!) |
|
twoArgString : ((REPLACE | REPLACE_FIRST | REPLACE_ALL | IF_ELSE | JSON_PATH_SET | JSON_PATH_ADD) LPAREN! anyArg COMMA! anyArg RPAREN!) |
|
||||||
((SUBSTRING | FORMAT | PAD_LEFT | PAD_RIGHT) LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
|
((SUBSTRING | FORMAT | PAD_LEFT | PAD_RIGHT | REPEAT) LPAREN! anyArg (COMMA! anyArg)? RPAREN!);
|
||||||
threeArgString: ((JSON_PATH_PUT) LPAREN! anyArg COMMA! anyArg COMMA! anyArg RPAREN!);
|
threeArgString: ((JSON_PATH_PUT) LPAREN! anyArg COMMA! anyArg COMMA! anyArg RPAREN!);
|
||||||
fiveArgString : GET_DELIMITED_FIELD LPAREN! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg)?)?)?)? RPAREN!;
|
fiveArgString : GET_DELIMITED_FIELD LPAREN! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg (COMMA! anyArg)?)?)?)? RPAREN!;
|
||||||
|
|
||||||
|
@ -89,6 +89,7 @@ import org.apache.nifi.attribute.expression.language.evaluation.functions.PadRig
|
|||||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.PlusEvaluator;
|
import org.apache.nifi.attribute.expression.language.evaluation.functions.PlusEvaluator;
|
||||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.PrependEvaluator;
|
import org.apache.nifi.attribute.expression.language.evaluation.functions.PrependEvaluator;
|
||||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.RandomNumberGeneratorEvaluator;
|
import org.apache.nifi.attribute.expression.language.evaluation.functions.RandomNumberGeneratorEvaluator;
|
||||||
|
import org.apache.nifi.attribute.expression.language.evaluation.functions.RepeatEvaluator;
|
||||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceAllEvaluator;
|
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceAllEvaluator;
|
||||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEmptyEvaluator;
|
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEmptyEvaluator;
|
||||||
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEvaluator;
|
import org.apache.nifi.attribute.expression.language.evaluation.functions.ReplaceEvaluator;
|
||||||
@ -208,6 +209,7 @@ import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpre
|
|||||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PLUS;
|
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PLUS;
|
||||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PREPEND;
|
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.PREPEND;
|
||||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.RANDOM;
|
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.RANDOM;
|
||||||
|
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPEAT;
|
||||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE;
|
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE;
|
||||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_ALL;
|
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_ALL;
|
||||||
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_EMPTY;
|
import static org.apache.nifi.attribute.expression.language.antlr.AttributeExpressionParser.REPLACE_EMPTY;
|
||||||
@ -721,6 +723,19 @@ public class ExpressionCompiler {
|
|||||||
throw new AttributeExpressionLanguageParsingException("substring() function can take either 1 or 2 arguments but cannot take " + numArgs + " arguments");
|
throw new AttributeExpressionLanguageParsingException("substring() function can take either 1 or 2 arguments but cannot take " + numArgs + " arguments");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case REPEAT: {
|
||||||
|
final int numArgs = argEvaluators.size();
|
||||||
|
if (numArgs == 1) {
|
||||||
|
return addToken(new RepeatEvaluator(toStringEvaluator(subjectEvaluator),
|
||||||
|
toWholeNumberEvaluator(argEvaluators.get(0), "first argument to repeat")), "repeat");
|
||||||
|
} else if (numArgs == 2) {
|
||||||
|
return addToken(new RepeatEvaluator(toStringEvaluator(subjectEvaluator),
|
||||||
|
toWholeNumberEvaluator(argEvaluators.get(0), "first argument to repeat"),
|
||||||
|
toWholeNumberEvaluator(argEvaluators.get(1), "second argument to repeat")), "repeat");
|
||||||
|
} else {
|
||||||
|
throw new AttributeExpressionLanguageParsingException("repeat() function can take either 1 or 2 arguments but cannot take " + numArgs + " arguments");
|
||||||
|
}
|
||||||
|
}
|
||||||
case JOIN: {
|
case JOIN: {
|
||||||
verifyArgCount(argEvaluators, 1, "join");
|
verifyArgCount(argEvaluators, 1, "join");
|
||||||
return addToken(new JoinEvaluator(toStringEvaluator(subjectEvaluator), toStringEvaluator(argEvaluators.get(0))), "join");
|
return addToken(new JoinEvaluator(toStringEvaluator(subjectEvaluator), toStringEvaluator(argEvaluators.get(0))), "join");
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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.apache.nifi.attribute.expression.language.evaluation.functions;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.nifi.attribute.expression.language.EvaluationContext;
|
||||||
|
import org.apache.nifi.attribute.expression.language.evaluation.Evaluator;
|
||||||
|
import org.apache.nifi.attribute.expression.language.evaluation.QueryResult;
|
||||||
|
import org.apache.nifi.attribute.expression.language.evaluation.StringEvaluator;
|
||||||
|
import org.apache.nifi.attribute.expression.language.evaluation.StringQueryResult;
|
||||||
|
import org.apache.nifi.attribute.expression.language.exception.AttributeExpressionLanguageException;
|
||||||
|
|
||||||
|
public class RepeatEvaluator extends StringEvaluator {
|
||||||
|
|
||||||
|
private final Evaluator<String> subject;
|
||||||
|
private final Evaluator<Long> minRepeats;
|
||||||
|
private final Evaluator<Long> maxRepeats;
|
||||||
|
|
||||||
|
public RepeatEvaluator(final Evaluator<String> subject, final Evaluator<Long> minRepeats, final Evaluator<Long> maxRepeats) {
|
||||||
|
this.subject = subject;
|
||||||
|
this.minRepeats = minRepeats;
|
||||||
|
this.maxRepeats = maxRepeats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RepeatEvaluator(final Evaluator<String> subject, final Evaluator<Long> minRepeats) {
|
||||||
|
this.subject = subject;
|
||||||
|
this.minRepeats = minRepeats;
|
||||||
|
this.maxRepeats = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryResult<String> evaluate(final EvaluationContext evaluationContext) {
|
||||||
|
final String subjectValue = subject.evaluate(evaluationContext).getValue();
|
||||||
|
if (subjectValue == null) {
|
||||||
|
return new StringQueryResult("");
|
||||||
|
}
|
||||||
|
final int firstRepeatValue = minRepeats.evaluate(evaluationContext).getValue().intValue();
|
||||||
|
if (maxRepeats == null) {
|
||||||
|
if (firstRepeatValue <= 0) {
|
||||||
|
throw new AttributeExpressionLanguageException("Number of repeats must be > 0");
|
||||||
|
}
|
||||||
|
return new StringQueryResult(StringUtils.repeat(subjectValue, firstRepeatValue));
|
||||||
|
} else {
|
||||||
|
if (firstRepeatValue <= 0) {
|
||||||
|
throw new AttributeExpressionLanguageException("Minimum number of repeats must be > 0");
|
||||||
|
}
|
||||||
|
final int maxRepeatCount = maxRepeats.evaluate(evaluationContext).getValue().intValue();
|
||||||
|
if (firstRepeatValue > maxRepeatCount) {
|
||||||
|
throw new AttributeExpressionLanguageException("Min repeats must not be greater than max repeats");
|
||||||
|
}
|
||||||
|
final int randomRepeatCount = ((int) (Math.random() * (maxRepeatCount - firstRepeatValue + 1))) + firstRepeatValue;
|
||||||
|
return new StringQueryResult(StringUtils.repeat(subjectValue, randomRepeatCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Evaluator<?> getSubjectEvaluator() {
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
}
|
@ -2098,6 +2098,45 @@ public class TestQuery {
|
|||||||
verifyEmpty("${nullString:padRight(10, \"@\")}", attributes);
|
verifyEmpty("${nullString:padRight(10, \"@\")}", attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRepeat() {
|
||||||
|
final Map<String, String> attributes = new HashMap<>();
|
||||||
|
attributes.put("str", "abc");
|
||||||
|
|
||||||
|
verifyEquals("${not_exist:repeat(1, 2)}", attributes, "");
|
||||||
|
verifyEquals("${str:repeat(1, 1)}", attributes, "abc");
|
||||||
|
|
||||||
|
// Custom verify because the result could be one of multiple options
|
||||||
|
String multipleResultExpression = "${str:repeat(1, 3)}";
|
||||||
|
String multipleResultExpectedResult1 = "abc";
|
||||||
|
String multipleResultExpectedResult2 = "abcabc";
|
||||||
|
String multipleResultExpectedResult3 = "abcabcabc";
|
||||||
|
List<String> multipleResultExpectedResults = Arrays.asList(multipleResultExpectedResult1, multipleResultExpectedResult2, multipleResultExpectedResult3);
|
||||||
|
Query.validateExpression(multipleResultExpression, false);
|
||||||
|
final String actualResult = Query.evaluateExpressions(multipleResultExpression, attributes, null, null, ParameterLookup.EMPTY);
|
||||||
|
assertTrue(multipleResultExpectedResults.contains(actualResult));
|
||||||
|
|
||||||
|
verifyEquals("${str:repeat(4)}", attributes, "abcabcabcabc");
|
||||||
|
try {
|
||||||
|
verifyEquals("${str:repeat(-1)}", attributes, "");
|
||||||
|
fail("Should have failed on numRepeats < 0");
|
||||||
|
} catch(AttributeExpressionLanguageException aele) {
|
||||||
|
// Do nothing, it is expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
verifyEquals("${str:repeat(0)}", attributes, "");
|
||||||
|
fail("Should have failed on numRepeats = 0");
|
||||||
|
} catch(AttributeExpressionLanguageException aele) {
|
||||||
|
// Do nothing, it is expected
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
verifyEquals("${str:repeat(2,1)}", attributes, "");
|
||||||
|
fail("Should have failed on minRepeats > maxRepeats");
|
||||||
|
} catch(AttributeExpressionLanguageException aele) {
|
||||||
|
// Do nothing, it is expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void verifyEquals(final String expression, final Map<String, String> attributes, final Object expectedResult) {
|
private void verifyEquals(final String expression, final Map<String, String> attributes, final Object expectedResult) {
|
||||||
verifyEquals(expression,attributes, null, ParameterLookup.EMPTY, expectedResult);
|
verifyEquals(expression,attributes, null, ParameterLookup.EMPTY, expectedResult);
|
||||||
}
|
}
|
||||||
|
@ -1123,6 +1123,48 @@ Expressions will provide the following results:
|
|||||||
then the Expression `${query:evaluateELString()}` will return SELECT * FROM TABLE WHERE ID = 20
|
then the Expression `${query:evaluateELString()}` will return SELECT * FROM TABLE WHERE ID = 20
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[.function]
|
||||||
|
=== repeat
|
||||||
|
|
||||||
|
*Description*:
|
||||||
|
[.description]#Returns a string that is the Subject repeated a random number of times between _min repeats_ and
|
||||||
|
_max repeats_. If _max repeats_ is not supplied, it will return the Subject repeated exactly _min repeats_ times.#
|
||||||
|
|
||||||
|
[.description]#The _min repeats_ and _max repeats_ must be positive numbers, with _max repeats_ greater than or equal
|
||||||
|
to _min repeats_#
|
||||||
|
|
||||||
|
[.description]#If either _min repeats_ or _max repeats_ is not a number, this function call will result
|
||||||
|
in an error.#
|
||||||
|
|
||||||
|
|
||||||
|
*Subject Type*: [.subject]#String#
|
||||||
|
|
||||||
|
*Arguments*:
|
||||||
|
|
||||||
|
- [.argName]#_min repeats_# : [.argDesc]#The minimum number (inclusive) of times to repeat the subject, or the exact number
|
||||||
|
of times to repeat the subject if _max repeats_ is not supplied.#
|
||||||
|
- [.argName]#_max repeats_# : [.argDesc]#The maximum number (inclusive) of times to repeat the subject.#
|
||||||
|
|
||||||
|
*Return Type*: [.returnType]#String#
|
||||||
|
|
||||||
|
*Examples*:
|
||||||
|
|
||||||
|
If we have an attribute named "str" with the value "abc",
|
||||||
|
then the following Expressions will result in the following values:
|
||||||
|
|
||||||
|
.Repeat Examples
|
||||||
|
|================================================================
|
||||||
|
| Expression | Value
|
||||||
|
| `${str:repeat(1)}` | `abc`
|
||||||
|
| `${str:repeat(2)}` | `abcabc`
|
||||||
|
| `${str:repeat(1,2)}` | `abc` or `abcabc` (at random)
|
||||||
|
| `${str:repeat( ${str:length()} )}` | `abc` or `abcabc` or `abcabcabc` (at random)
|
||||||
|
|================================================================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[encode]]
|
[[encode]]
|
||||||
== Encode/Decode Functions
|
== Encode/Decode Functions
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user