* EQL: implement case sensitivity for indexOf and endsWith string functions
This commit is contained in:
parent
85f5c4192b
commit
ec60335496
|
@ -6,14 +6,16 @@
|
|||
|
||||
package org.elasticsearch.xpack.eql.expression.function.scalar.string;
|
||||
|
||||
import org.elasticsearch.xpack.eql.session.EqlConfiguration;
|
||||
import org.elasticsearch.xpack.ql.expression.Expression;
|
||||
import org.elasticsearch.xpack.ql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.ql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.ql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction;
|
||||
import org.elasticsearch.xpack.ql.expression.function.scalar.string.CaseSensitiveScalarFunction;
|
||||
import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.ql.expression.gen.script.Scripts;
|
||||
import org.elasticsearch.xpack.ql.session.Configuration;
|
||||
import org.elasticsearch.xpack.ql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.ql.tree.Source;
|
||||
import org.elasticsearch.xpack.ql.type.DataType;
|
||||
|
@ -32,17 +34,22 @@ import static org.elasticsearch.xpack.ql.expression.gen.script.ParamsBuilder.par
|
|||
* Function that checks if first parameter ends with the second parameter. Both parameters should be strings
|
||||
* and the function returns a boolean value. The function is case insensitive.
|
||||
*/
|
||||
public class EndsWith extends ScalarFunction {
|
||||
public class EndsWith extends CaseSensitiveScalarFunction {
|
||||
|
||||
private final Expression source;
|
||||
private final Expression pattern;
|
||||
|
||||
public EndsWith(Source source, Expression src, Expression pattern) {
|
||||
super(source, Arrays.asList(src, pattern));
|
||||
public EndsWith(Source source, Expression src, Expression pattern, Configuration configuration) {
|
||||
super(source, Arrays.asList(src, pattern), configuration);
|
||||
this.source = src;
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaseSensitive() {
|
||||
return ((EqlConfiguration) configuration()).isCaseSensitive();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (!childrenResolved()) {
|
||||
|
@ -59,7 +66,7 @@ public class EndsWith extends ScalarFunction {
|
|||
|
||||
@Override
|
||||
protected Pipe makePipe() {
|
||||
return new EndsWithFunctionPipe(source(), this, Expressions.pipe(source), Expressions.pipe(pattern));
|
||||
return new EndsWithFunctionPipe(source(), this, Expressions.pipe(source), Expressions.pipe(pattern), isCaseSensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,12 +76,12 @@ public class EndsWith extends ScalarFunction {
|
|||
|
||||
@Override
|
||||
public Object fold() {
|
||||
return doProcess(source.fold(), pattern.fold());
|
||||
return doProcess(source.fold(), pattern.fold(), isCaseSensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<? extends Expression> info() {
|
||||
return NodeInfo.create(this, EndsWith::new, source, pattern);
|
||||
return NodeInfo.create(this, EndsWith::new, source, pattern, configuration());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,14 +93,16 @@ public class EndsWith extends ScalarFunction {
|
|||
}
|
||||
|
||||
protected ScriptTemplate asScriptFrom(ScriptTemplate sourceScript, ScriptTemplate patternScript) {
|
||||
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{eql}.%s(%s,%s)"),
|
||||
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{eql}.%s(%s,%s,%s)"),
|
||||
"endsWith",
|
||||
sourceScript.template(),
|
||||
patternScript.template()),
|
||||
patternScript.template(),
|
||||
"{}"),
|
||||
paramsBuilder()
|
||||
.script(sourceScript.params())
|
||||
.script(patternScript.params())
|
||||
.build(), dataType());
|
||||
.script(sourceScript.params())
|
||||
.script(patternScript.params())
|
||||
.variable(isCaseSensitive())
|
||||
.build(), dataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -114,7 +123,7 @@ public class EndsWith extends ScalarFunction {
|
|||
throw new IllegalArgumentException("expected [2] children but received [" + newChildren.size() + "]");
|
||||
}
|
||||
|
||||
return new EndsWith(source(), newChildren.get(0), newChildren.get(1));
|
||||
return new EndsWith(source(), newChildren.get(0), newChildren.get(1), configuration());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,11 +19,13 @@ public class EndsWithFunctionPipe extends Pipe {
|
|||
|
||||
private final Pipe source;
|
||||
private final Pipe pattern;
|
||||
private final boolean isCaseSensitive;
|
||||
|
||||
public EndsWithFunctionPipe(Source source, Expression expression, Pipe src, Pipe pattern) {
|
||||
public EndsWithFunctionPipe(Source source, Expression expression, Pipe src, Pipe pattern, boolean isCaseSensitive) {
|
||||
super(source, expression, Arrays.asList(src, pattern));
|
||||
this.source = src;
|
||||
this.pattern = pattern;
|
||||
this.isCaseSensitive = isCaseSensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,7 +57,7 @@ public class EndsWithFunctionPipe extends Pipe {
|
|||
}
|
||||
|
||||
protected Pipe replaceChildren(Pipe newSource, Pipe newPattern) {
|
||||
return new EndsWithFunctionPipe(source(), expression(), newSource, newPattern);
|
||||
return new EndsWithFunctionPipe(source(), expression(), newSource, newPattern, isCaseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,12 +68,12 @@ public class EndsWithFunctionPipe extends Pipe {
|
|||
|
||||
@Override
|
||||
protected NodeInfo<EndsWithFunctionPipe> info() {
|
||||
return NodeInfo.create(this, EndsWithFunctionPipe::new, expression(), source, pattern);
|
||||
return NodeInfo.create(this, EndsWithFunctionPipe::new, expression(), source, pattern, isCaseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndsWithFunctionProcessor asProcessor() {
|
||||
return new EndsWithFunctionProcessor(source.asProcessor(), pattern.asProcessor());
|
||||
return new EndsWithFunctionProcessor(source.asProcessor(), pattern.asProcessor(), isCaseSensitive);
|
||||
}
|
||||
|
||||
public Pipe src() {
|
||||
|
@ -84,7 +86,7 @@ public class EndsWithFunctionPipe extends Pipe {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(source, pattern);
|
||||
return Objects.hash(source, pattern, isCaseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -99,6 +101,7 @@ public class EndsWithFunctionPipe extends Pipe {
|
|||
|
||||
EndsWithFunctionPipe other = (EndsWithFunctionPipe) obj;
|
||||
return Objects.equals(source, other.source)
|
||||
&& Objects.equals(pattern, other.pattern);
|
||||
&& Objects.equals(pattern, other.pattern)
|
||||
&& Objects.equals(isCaseSensitive, other.isCaseSensitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,29 +20,33 @@ public class EndsWithFunctionProcessor implements Processor {
|
|||
|
||||
private final Processor source;
|
||||
private final Processor pattern;
|
||||
private final boolean isCaseSensitive;
|
||||
|
||||
public EndsWithFunctionProcessor(Processor source, Processor pattern) {
|
||||
public EndsWithFunctionProcessor(Processor source, Processor pattern, boolean isCaseSensitive) {
|
||||
this.source = source;
|
||||
this.pattern = pattern;
|
||||
this.isCaseSensitive = isCaseSensitive;
|
||||
}
|
||||
|
||||
public EndsWithFunctionProcessor(StreamInput in) throws IOException {
|
||||
source = in.readNamedWriteable(Processor.class);
|
||||
pattern = in.readNamedWriteable(Processor.class);
|
||||
isCaseSensitive = in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeNamedWriteable(source);
|
||||
out.writeNamedWriteable(pattern);
|
||||
out.writeBoolean(isCaseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(Object input) {
|
||||
return doProcess(source.process(input), pattern.process(input));
|
||||
return doProcess(source.process(input), pattern.process(input), isCaseSensitive());
|
||||
}
|
||||
|
||||
public static Object doProcess(Object source, Object pattern) {
|
||||
public static Object doProcess(Object source, Object pattern, boolean isCaseSensitive) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -56,7 +60,11 @@ public class EndsWithFunctionProcessor implements Processor {
|
|||
throw new EqlIllegalArgumentException("A string/char is required; received [{}]", pattern);
|
||||
}
|
||||
|
||||
return source.toString().toLowerCase(Locale.ROOT).endsWith(pattern.toString().toLowerCase(Locale.ROOT));
|
||||
if (isCaseSensitive) {
|
||||
return source.toString().endsWith(pattern.toString());
|
||||
} else {
|
||||
return source.toString().toLowerCase(Locale.ROOT).endsWith(pattern.toString().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
}
|
||||
|
||||
protected Processor source() {
|
||||
|
@ -66,7 +74,11 @@ public class EndsWithFunctionProcessor implements Processor {
|
|||
protected Processor pattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
|
||||
protected boolean isCaseSensitive() {
|
||||
return isCaseSensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
|
@ -79,12 +91,13 @@ public class EndsWithFunctionProcessor implements Processor {
|
|||
|
||||
EndsWithFunctionProcessor other = (EndsWithFunctionProcessor) obj;
|
||||
return Objects.equals(source(), other.source())
|
||||
&& Objects.equals(pattern(), other.pattern());
|
||||
&& Objects.equals(pattern(), other.pattern())
|
||||
&& Objects.equals(isCaseSensitive(), other.isCaseSensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(source(), pattern());
|
||||
return Objects.hash(source(), pattern(), isCaseSensitive());
|
||||
}
|
||||
|
||||
|
||||
|
@ -92,4 +105,4 @@ public class EndsWithFunctionProcessor implements Processor {
|
|||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,18 @@
|
|||
|
||||
package org.elasticsearch.xpack.eql.expression.function.scalar.string;
|
||||
|
||||
import org.elasticsearch.xpack.eql.session.EqlConfiguration;
|
||||
import org.elasticsearch.xpack.ql.expression.Expression;
|
||||
import org.elasticsearch.xpack.ql.expression.Expressions;
|
||||
import org.elasticsearch.xpack.ql.expression.Expressions.ParamOrdinal;
|
||||
import org.elasticsearch.xpack.ql.expression.FieldAttribute;
|
||||
import org.elasticsearch.xpack.ql.expression.Literal;
|
||||
import org.elasticsearch.xpack.ql.expression.function.OptionalArgument;
|
||||
import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction;
|
||||
import org.elasticsearch.xpack.ql.expression.function.scalar.string.CaseSensitiveScalarFunction;
|
||||
import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe;
|
||||
import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate;
|
||||
import org.elasticsearch.xpack.ql.expression.gen.script.Scripts;
|
||||
import org.elasticsearch.xpack.ql.session.Configuration;
|
||||
import org.elasticsearch.xpack.ql.tree.NodeInfo;
|
||||
import org.elasticsearch.xpack.ql.tree.Source;
|
||||
import org.elasticsearch.xpack.ql.type.DataType;
|
||||
|
@ -35,17 +37,22 @@ import static org.elasticsearch.xpack.ql.expression.gen.script.ParamsBuilder.par
|
|||
* Find the first position (zero-indexed) of a string where a substring is found.
|
||||
* If the optional parameter start is provided, then this will find the first occurrence at or after the start position.
|
||||
*/
|
||||
public class IndexOf extends ScalarFunction implements OptionalArgument {
|
||||
public class IndexOf extends CaseSensitiveScalarFunction implements OptionalArgument {
|
||||
|
||||
private final Expression source, substring, start;
|
||||
|
||||
public IndexOf(Source source, Expression src, Expression substring, Expression start) {
|
||||
super(source, Arrays.asList(src, substring, start != null ? start : new Literal(source, null, DataTypes.NULL)));
|
||||
public IndexOf(Source source, Expression src, Expression substring, Expression start, Configuration configuration) {
|
||||
super(source, Arrays.asList(src, substring, start != null ? start : new Literal(source, null, DataTypes.NULL)), configuration);
|
||||
this.source = src;
|
||||
this.substring = substring;
|
||||
this.start = arguments().get(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaseSensitive() {
|
||||
return ((EqlConfiguration) configuration()).isCaseSensitive();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeResolution resolveType() {
|
||||
if (!childrenResolved()) {
|
||||
|
@ -61,13 +68,14 @@ public class IndexOf extends ScalarFunction implements OptionalArgument {
|
|||
if (resolution.unresolved()) {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
|
||||
return isInteger(start, sourceText(), ParamOrdinal.THIRD);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pipe makePipe() {
|
||||
return new IndexOfFunctionPipe(source(), this, Expressions.pipe(source), Expressions.pipe(substring), Expressions.pipe(start));
|
||||
return new IndexOfFunctionPipe(source(), this, Expressions.pipe(source), Expressions.pipe(substring),
|
||||
Expressions.pipe(start), isCaseSensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,12 +85,12 @@ public class IndexOf extends ScalarFunction implements OptionalArgument {
|
|||
|
||||
@Override
|
||||
public Object fold() {
|
||||
return doProcess(source.fold(), substring.fold(), start.fold());
|
||||
return doProcess(source.fold(), substring.fold(), start.fold(), isCaseSensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeInfo<? extends Expression> info() {
|
||||
return NodeInfo.create(this, IndexOf::new, source, substring, start);
|
||||
return NodeInfo.create(this, IndexOf::new, source, substring, start, configuration());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -93,18 +101,20 @@ public class IndexOf extends ScalarFunction implements OptionalArgument {
|
|||
|
||||
return asScriptFrom(sourceScript, substringScript, startScript);
|
||||
}
|
||||
|
||||
|
||||
protected ScriptTemplate asScriptFrom(ScriptTemplate sourceScript, ScriptTemplate substringScript, ScriptTemplate startScript) {
|
||||
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{eql}.%s(%s,%s,%s)"),
|
||||
return new ScriptTemplate(format(Locale.ROOT, formatTemplate("{eql}.%s(%s,%s,%s,%s)"),
|
||||
"indexOf",
|
||||
sourceScript.template(),
|
||||
substringScript.template(),
|
||||
startScript.template()),
|
||||
startScript.template(),
|
||||
"{}"),
|
||||
paramsBuilder()
|
||||
.script(sourceScript.params())
|
||||
.script(substringScript.params())
|
||||
.script(startScript.params())
|
||||
.build(), dataType());
|
||||
.script(sourceScript.params())
|
||||
.script(substringScript.params())
|
||||
.script(startScript.params())
|
||||
.variable(isCaseSensitive())
|
||||
.build(), dataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -125,7 +135,7 @@ public class IndexOf extends ScalarFunction implements OptionalArgument {
|
|||
throw new IllegalArgumentException("expected [3] children but received [" + newChildren.size() + "]");
|
||||
}
|
||||
|
||||
return new IndexOf(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2));
|
||||
return new IndexOf(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2), configuration());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,14 @@ import java.util.Objects;
|
|||
public class IndexOfFunctionPipe extends Pipe {
|
||||
|
||||
private final Pipe source, substring, start;
|
||||
private final boolean isCaseSensitive;
|
||||
|
||||
public IndexOfFunctionPipe(Source source, Expression expression, Pipe src, Pipe substring, Pipe start) {
|
||||
public IndexOfFunctionPipe(Source source, Expression expression, Pipe src, Pipe substring, Pipe start, boolean isCaseSensitive) {
|
||||
super(source, expression, Arrays.asList(src, substring, start));
|
||||
this.source = src;
|
||||
this.substring = substring;
|
||||
this.start = start;
|
||||
this.isCaseSensitive = isCaseSensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -56,7 +58,7 @@ public class IndexOfFunctionPipe extends Pipe {
|
|||
}
|
||||
|
||||
protected Pipe replaceChildren(Pipe newSource, Pipe newSubstring, Pipe newStart) {
|
||||
return new IndexOfFunctionPipe(source(), expression(), newSource, newSubstring, newStart);
|
||||
return new IndexOfFunctionPipe(source(), expression(), newSource, newSubstring, newStart, isCaseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,12 +70,12 @@ public class IndexOfFunctionPipe extends Pipe {
|
|||
|
||||
@Override
|
||||
protected NodeInfo<IndexOfFunctionPipe> info() {
|
||||
return NodeInfo.create(this, IndexOfFunctionPipe::new, expression(), source, substring, start);
|
||||
return NodeInfo.create(this, IndexOfFunctionPipe::new, expression(), source, substring, start, isCaseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexOfFunctionProcessor asProcessor() {
|
||||
return new IndexOfFunctionProcessor(source.asProcessor(), substring.asProcessor(), start.asProcessor());
|
||||
return new IndexOfFunctionProcessor(source.asProcessor(), substring.asProcessor(), start.asProcessor(), isCaseSensitive);
|
||||
}
|
||||
|
||||
public Pipe src() {
|
||||
|
@ -90,7 +92,7 @@ public class IndexOfFunctionPipe extends Pipe {
|
|||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(source, substring, start);
|
||||
return Objects.hash(source, substring, start, isCaseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -106,6 +108,7 @@ public class IndexOfFunctionPipe extends Pipe {
|
|||
IndexOfFunctionPipe other = (IndexOfFunctionPipe) obj;
|
||||
return Objects.equals(source, other.source)
|
||||
&& Objects.equals(substring, other.substring)
|
||||
&& Objects.equals(start, other.start);
|
||||
&& Objects.equals(start, other.start)
|
||||
&& Objects.equals(isCaseSensitive, other.isCaseSensitive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,17 +21,20 @@ public class IndexOfFunctionProcessor implements Processor {
|
|||
private final Processor source;
|
||||
private final Processor substring;
|
||||
private final Processor start;
|
||||
private final boolean isCaseSensitive;
|
||||
|
||||
public IndexOfFunctionProcessor(Processor source, Processor substring, Processor start) {
|
||||
public IndexOfFunctionProcessor(Processor source, Processor substring, Processor start, boolean isCaseSensitive) {
|
||||
this.source = source;
|
||||
this.substring = substring;
|
||||
this.start = start;
|
||||
this.isCaseSensitive = isCaseSensitive;
|
||||
}
|
||||
|
||||
public IndexOfFunctionProcessor(StreamInput in) throws IOException {
|
||||
source = in.readNamedWriteable(Processor.class);
|
||||
substring = in.readNamedWriteable(Processor.class);
|
||||
start = in.readNamedWriteable(Processor.class);
|
||||
isCaseSensitive = in.readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,14 +42,15 @@ public class IndexOfFunctionProcessor implements Processor {
|
|||
out.writeNamedWriteable(source);
|
||||
out.writeNamedWriteable(substring);
|
||||
out.writeNamedWriteable(start);
|
||||
out.writeBoolean(isCaseSensitive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object process(Object input) {
|
||||
return doProcess(source.process(input), substring.process(input), start.process(input));
|
||||
return doProcess(source.process(input), substring.process(input), start.process(input), isCaseSensitive());
|
||||
}
|
||||
|
||||
public static Object doProcess(Object source, Object substring, Object start) {
|
||||
public static Object doProcess(Object source, Object substring, Object start, boolean isCaseSensitive) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -64,7 +68,14 @@ public class IndexOfFunctionProcessor implements Processor {
|
|||
throw new EqlIllegalArgumentException("A number is required; received [{}]", start);
|
||||
}
|
||||
int startIndex = start == null ? 0 : ((Number) start).intValue();
|
||||
int result = source.toString().toLowerCase(Locale.ROOT).indexOf(substring.toString().toLowerCase(Locale.ROOT), startIndex);
|
||||
|
||||
int result;
|
||||
|
||||
if (isCaseSensitive) {
|
||||
result = source.toString().indexOf(substring.toString(), startIndex);
|
||||
} else {
|
||||
result = source.toString().toLowerCase(Locale.ROOT).indexOf(substring.toString().toLowerCase(Locale.ROOT), startIndex);
|
||||
}
|
||||
|
||||
return result < 0 ? null : result;
|
||||
}
|
||||
|
@ -80,7 +91,11 @@ public class IndexOfFunctionProcessor implements Processor {
|
|||
protected Processor start() {
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
protected boolean isCaseSensitive() {
|
||||
return isCaseSensitive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
|
@ -94,12 +109,13 @@ public class IndexOfFunctionProcessor implements Processor {
|
|||
IndexOfFunctionProcessor other = (IndexOfFunctionProcessor) obj;
|
||||
return Objects.equals(source(), other.source())
|
||||
&& Objects.equals(substring(), other.substring())
|
||||
&& Objects.equals(start(), other.start());
|
||||
&& Objects.equals(start(), other.start())
|
||||
&& Objects.equals(isCaseSensitive(), other.isCaseSensitive());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(source(), substring(), start());
|
||||
return Objects.hash(source(), substring(), start(), isCaseSensitive());
|
||||
}
|
||||
|
||||
|
||||
|
@ -107,4 +123,4 @@ public class IndexOfFunctionProcessor implements Processor {
|
|||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,12 +41,12 @@ public class InternalEqlScriptUtils extends InternalQlScriptUtils {
|
|||
return (String) ConcatFunctionProcessor.doProcess(values);
|
||||
}
|
||||
|
||||
public static Boolean endsWith(String s, String pattern) {
|
||||
return (Boolean) EndsWithFunctionProcessor.doProcess(s, pattern);
|
||||
public static Boolean endsWith(String s, String pattern, Boolean isCaseSensitive) {
|
||||
return (Boolean) EndsWithFunctionProcessor.doProcess(s, pattern, isCaseSensitive);
|
||||
}
|
||||
|
||||
public static Integer indexOf(String s, String substring, Number start) {
|
||||
return (Integer) IndexOfFunctionProcessor.doProcess(s, substring, start);
|
||||
public static Integer indexOf(String s, String substring, Number start, Boolean isCaseSensitive) {
|
||||
return (Integer) IndexOfFunctionProcessor.doProcess(s, substring, start, isCaseSensitive);
|
||||
}
|
||||
|
||||
public static Integer length(String s) {
|
||||
|
|
|
@ -68,8 +68,8 @@ class org.elasticsearch.xpack.eql.expression.function.scalar.whitelist.InternalE
|
|||
String between(String, String, String, Boolean, Boolean)
|
||||
Boolean cidrMatch(String, java.util.List)
|
||||
String concat(java.util.List)
|
||||
Boolean endsWith(String, String)
|
||||
Integer indexOf(String, String, Number)
|
||||
Boolean endsWith(String, String, Boolean)
|
||||
Integer indexOf(String, String, Number, Boolean)
|
||||
Integer length(String)
|
||||
Number number(String, Number)
|
||||
String string(Object)
|
||||
|
|
|
@ -10,7 +10,9 @@ import org.elasticsearch.test.ESTestCase;
|
|||
import org.elasticsearch.xpack.ql.QlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.ql.expression.Literal;
|
||||
import org.elasticsearch.xpack.ql.expression.LiteralTests;
|
||||
import org.elasticsearch.xpack.ql.session.Configuration;
|
||||
|
||||
import static org.elasticsearch.xpack.eql.EqlTestUtils.randomConfigurationWithCaseSensitive;
|
||||
import static org.elasticsearch.xpack.ql.expression.function.scalar.FunctionTestUtils.l;
|
||||
import static org.elasticsearch.xpack.ql.tree.Source.EMPTY;
|
||||
import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD;
|
||||
|
@ -18,37 +20,60 @@ import static org.hamcrest.Matchers.startsWith;
|
|||
|
||||
public class EndsWithProcessorTests extends ESTestCase {
|
||||
|
||||
public void testStartsWithFunctionWithValidInput() {
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foobarbar"), l("r")).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foobar"), l("foo")).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foo"), l("foobar")).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foobar"), l("")).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foo"), l("foo")).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foo"), l("oO")).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foo"), l("FOo")).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l('f'), l('f')).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l(""), l("bar")).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l(null), l("bar")).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l("foo"), l(null)).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l(null), l(null)).makePipe().asProcessor().process(null));
|
||||
final Configuration caseInsensitive = randomConfigurationWithCaseSensitive(false);
|
||||
|
||||
public void testEndsWithFunctionWithValidInputCaseSensitive() {
|
||||
final Configuration caseSensitive = randomConfigurationWithCaseSensitive(true);
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foobarbar"), l("r"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foobarbar"), l("R"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foobarbar"), l("bar"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foobarBar"), l("bar"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foobar"), l("foo"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foo"), l("foobar"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foobar"), l(""), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foo"), l("foo"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foo"), l("oO"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foo"), l("FOo"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l('f'), l('f'), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l(""), l("bar"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l(null), l("bar"), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l("foo"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l(null), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
}
|
||||
|
||||
public void testStartsWithFunctionInputsValidation() {
|
||||
|
||||
public void testEndsWithFunctionWithValidInputCaseInsensitive() {
|
||||
final Configuration config = randomConfigurationWithCaseSensitive(false);
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foobarbar"), l("r"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foobarbar"), l("R"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foobar"), l("foo"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l("foo"), l("foobar"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foobar"), l(""), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foo"), l("foo"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foo"), l("oO"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l("foo"), l("FOo"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(true, new EndsWith(EMPTY, l('f'), l('f'), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(false, new EndsWith(EMPTY, l(""), l("bar"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l(null), l("bar"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l("foo"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new EndsWith(EMPTY, l(null), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
}
|
||||
|
||||
public void testEndsWithFunctionInputsValidation() {
|
||||
QlIllegalArgumentException siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new EndsWith(EMPTY, l(5), l("foo")).makePipe().asProcessor().process(null));
|
||||
() -> new EndsWith(EMPTY, l(5), l("foo"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals("A string/char is required; received [5]", siae.getMessage());
|
||||
siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new EndsWith(EMPTY, l("bar"), l(false)).makePipe().asProcessor().process(null));
|
||||
() -> new EndsWith(EMPTY, l("bar"), l(false), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals("A string/char is required; received [false]", siae.getMessage());
|
||||
}
|
||||
|
||||
public void testStartsWithFunctionWithRandomInvalidDataType() {
|
||||
public void testEndsWithFunctionWithRandomInvalidDataType() {
|
||||
Literal literal = randomValueOtherThanMany(v -> v.dataType() == KEYWORD, () -> LiteralTests.randomLiteral());
|
||||
QlIllegalArgumentException siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new EndsWith(EMPTY, literal, l("foo")).makePipe().asProcessor().process(null));
|
||||
() -> new EndsWith(EMPTY, literal, l("foo"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertThat(siae.getMessage(), startsWith("A string/char is required; received"));
|
||||
siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new EndsWith(EMPTY, l("foo"), literal).makePipe().asProcessor().process(null));
|
||||
() -> new EndsWith(EMPTY, l("foo"), literal, caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertThat(siae.getMessage(), startsWith("A string/char is required; received"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ import org.elasticsearch.test.ESTestCase;
|
|||
import org.elasticsearch.xpack.ql.QlIllegalArgumentException;
|
||||
import org.elasticsearch.xpack.ql.expression.Literal;
|
||||
import org.elasticsearch.xpack.ql.expression.LiteralTests;
|
||||
import org.elasticsearch.xpack.ql.session.Configuration;
|
||||
|
||||
import static org.elasticsearch.xpack.eql.EqlTestUtils.randomConfigurationWithCaseSensitive;
|
||||
import static org.elasticsearch.xpack.ql.expression.function.scalar.FunctionTestUtils.l;
|
||||
import static org.elasticsearch.xpack.ql.tree.Source.EMPTY;
|
||||
import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD;
|
||||
|
@ -18,51 +20,77 @@ import static org.hamcrest.Matchers.startsWith;
|
|||
|
||||
public class IndexOfProcessorTests extends ESTestCase {
|
||||
|
||||
public void testStartsWithFunctionWithValidInput() {
|
||||
assertEquals(5, new IndexOf(EMPTY, l("foobarbar"), l("r"), l(null)).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("foobar"), l("foo"), l(null)).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foo"), l("foobar"), l(null)).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("foo"), l("foo"), l(null)).makePipe().asProcessor().process(null));
|
||||
assertEquals(1, new IndexOf(EMPTY, l("foo"), l("oO"), l(null)).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("foo"), l("FOo"), l(null)).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l('f'), l('f'), l(null)).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(""), l("bar"), l(1)).makePipe().asProcessor().process(null));
|
||||
assertEquals(5, new IndexOf(EMPTY, l("foobarbar"), l("R"), l(5)).makePipe().asProcessor().process(null));
|
||||
assertEquals(2, new IndexOf(EMPTY, l("foobar"), l("O"), l(2)).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foobar"), l("O"), l(3)).makePipe().asProcessor().process(null));
|
||||
assertEquals(6, new IndexOf(EMPTY, l("foobarbaz"), l("ba"), l(4)).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l("bar"), l(2)).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l("bar"), l(2)).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foo"), l(null), l(3)).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l(null), l(4)).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("bar"), l("bar"), l(null)).makePipe().asProcessor().process(null));
|
||||
final Configuration caseInsensitive = randomConfigurationWithCaseSensitive(false);
|
||||
|
||||
public void testIndexOfFunctionWithValidInputInsensitive() {
|
||||
assertEquals(5, new IndexOf(EMPTY, l("foobarbar"), l("r"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(5, new IndexOf(EMPTY, l("foobaRbar"), l("r"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("foobar"), l("Foo"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foo"), l("foobar"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("foo"), l("foo"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(1, new IndexOf(EMPTY, l("foo"), l("oO"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("foo"), l("FOo"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l('f'), l('f'), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(""), l("bar"), l(1), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(5, new IndexOf(EMPTY, l("foobarbar"), l("R"), l(5), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(2, new IndexOf(EMPTY, l("foobar"), l("O"), l(2), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foobar"), l("O"), l(3), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(6, new IndexOf(EMPTY, l("foobarbaz"), l("ba"), l(4), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l("bar"), l(2), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l("bar"), l(2), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foo"), l(null), l(3), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l(null), l(4), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("bar"), l("bar"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
}
|
||||
|
||||
public void testStartsWithFunctionInputsValidation() {
|
||||
|
||||
public void testIndexOfFunctionWithValidInputSensitive() {
|
||||
final Configuration caseSensitive = randomConfigurationWithCaseSensitive(true);
|
||||
assertEquals(5, new IndexOf(EMPTY, l("foobarbar"), l("r"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(8, new IndexOf(EMPTY, l("foobaRbar"), l("r"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(4, new IndexOf(EMPTY, l("foobARbar"), l("AR"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("foobar"), l("foo"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foo"), l("foobar"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("foo"), l("foo"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foo"), l("oO"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foo"), l("FOo"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l('f'), l('f'), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(""), l("bar"), l(1), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foobarbar"), l("R"), l(5), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foobar"), l("O"), l(2), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foobar"), l("O"), l(3), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(6, new IndexOf(EMPTY, l("foobarbaz"), l("ba"), l(4), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l("bar"), l(2), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l("bar"), l(2), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l("foo"), l(null), l(3), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(null, new IndexOf(EMPTY, l(null), l(null), l(4), caseSensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals(0, new IndexOf(EMPTY, l("bar"), l("bar"), l(null), caseSensitive).makePipe().asProcessor().process(null));
|
||||
}
|
||||
|
||||
public void testIndexOfFunctionInputsValidation() {
|
||||
QlIllegalArgumentException siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new IndexOf(EMPTY, l(5), l("foo"), l(null)).makePipe().asProcessor().process(null));
|
||||
() -> new IndexOf(EMPTY, l(5), l("foo"), l(null), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals("A string/char is required; received [5]", siae.getMessage());
|
||||
siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new IndexOf(EMPTY, l("bar"), l(false), l(2)).makePipe().asProcessor().process(null));
|
||||
() -> new IndexOf(EMPTY, l("bar"), l(false), l(2), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals("A string/char is required; received [false]", siae.getMessage());
|
||||
siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new IndexOf(EMPTY, l("bar"), l("a"), l("1")).makePipe().asProcessor().process(null));
|
||||
() -> new IndexOf(EMPTY, l("bar"), l("a"), l("1"), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertEquals("A number is required; received [1]", siae.getMessage());
|
||||
}
|
||||
|
||||
public void testStartsWithFunctionWithRandomInvalidDataType() {
|
||||
public void testIndexOfFunctionWithRandomInvalidDataType() {
|
||||
Literal stringLiteral = randomValueOtherThanMany(v -> v.dataType() == KEYWORD, () -> LiteralTests.randomLiteral());
|
||||
QlIllegalArgumentException siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new IndexOf(EMPTY, stringLiteral, l("foo"), l(1)).makePipe().asProcessor().process(null));
|
||||
() -> new IndexOf(EMPTY, stringLiteral, l("foo"), l(1), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertThat(siae.getMessage(), startsWith("A string/char is required; received"));
|
||||
|
||||
siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new IndexOf(EMPTY, l("foo"), stringLiteral, l(2)).makePipe().asProcessor().process(null));
|
||||
() -> new IndexOf(EMPTY, l("foo"), stringLiteral, l(2), caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertThat(siae.getMessage(), startsWith("A string/char is required; received"));
|
||||
|
||||
Literal numericLiteral = randomValueOtherThanMany(v -> v.dataType().isNumeric(), () -> LiteralTests.randomLiteral());
|
||||
siae = expectThrows(QlIllegalArgumentException.class,
|
||||
() -> new IndexOf(EMPTY, l("foo"), l("o"), numericLiteral).makePipe().asProcessor().process(null));
|
||||
() -> new IndexOf(EMPTY, l("foo"), l("o"), numericLiteral, caseInsensitive).makePipe().asProcessor().process(null));
|
||||
assertThat(siae.getMessage(), startsWith("A number is required; received"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,24 +102,44 @@ process where cidrMatch(source_address, "10.0.0.0/8") != false
|
|||
{"terms":{"source_address":["10.0.0.0/8"]
|
||||
;
|
||||
|
||||
twoFunctionsEqualsBooleanLiterals
|
||||
twoFunctionsEqualsBooleanLiterals-caseSensitive
|
||||
process where endsWith(process_path, 'x') == true and endsWith(process_path, 'yx') == false
|
||||
;
|
||||
{"bool":{"must":[{"term":{"event.category":{"value":"process",
|
||||
{"bool":{"must":[{"script":{"script":{"source":"InternalQlScriptUtils.nullSafeFilter(
|
||||
InternalEqlScriptUtils.endsWith(InternalQlScriptUtils.docValue(doc,params.v0),params.v1))","lang":"painless",
|
||||
"params":{"v0":"process_path","v1":"x"}}
|
||||
{"script":{"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.not(
|
||||
InternalEqlScriptUtils.endsWith(InternalQlScriptUtils.docValue(doc,params.v0),params.v1)))","lang":"painless",
|
||||
"params":{"v0":"process_path","v1":"yx"}}
|
||||
"bool":{"must":[{"term":{"event.category":{"value":"process"
|
||||
"must":[{"script":{"script":{"source":"InternalQlScriptUtils.nullSafeFilter(
|
||||
InternalEqlScriptUtils.endsWith(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2))","lang":"painless",
|
||||
"params":{"v0":"process_path","v1":"x","v2":true}}
|
||||
"script":{"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.not(
|
||||
InternalEqlScriptUtils.endsWith(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2)))","lang":"painless",
|
||||
"params":{"v0":"process_path","v1":"yx","v2":true}}
|
||||
;
|
||||
|
||||
endsWithFunction
|
||||
twoFunctionsEqualsBooleanLiterals-caseInsensitive
|
||||
process where endsWith(process_path, 'x') == true and endsWith(process_path, 'yx') == false
|
||||
;
|
||||
"bool":{"must":[{"term":{"event.category":{"value":"process"
|
||||
"must":[{"script":{"script":{"source":"InternalQlScriptUtils.nullSafeFilter(
|
||||
InternalEqlScriptUtils.endsWith(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2))","lang":"painless",
|
||||
"params":{"v0":"process_path","v1":"x","v2":false}}
|
||||
"script":{"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.not(
|
||||
InternalEqlScriptUtils.endsWith(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2)))","lang":"painless",
|
||||
"params":{"v0":"process_path","v1":"yx","v2":false}}
|
||||
;
|
||||
|
||||
endsWithFunction-caseSensitive
|
||||
process where endsWith(user_name, 'c')
|
||||
;
|
||||
"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalEqlScriptUtils.endsWith(
|
||||
InternalQlScriptUtils.docValue(doc,params.v0),params.v1))",
|
||||
"params":{"v0":"user_name","v1":"c"}
|
||||
InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2))",
|
||||
"params":{"v0":"user_name","v1":"c","v2":true}}
|
||||
;
|
||||
|
||||
endsWithFunction-caseInsensitive
|
||||
process where endsWith(user_name, 'c')
|
||||
;
|
||||
"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalEqlScriptUtils.endsWith(
|
||||
InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2))",
|
||||
"params":{"v0":"user_name","v1":"c","v2":false}}
|
||||
;
|
||||
|
||||
lengthFunctionWithExactSubField
|
||||
|
@ -146,7 +166,7 @@ InternalEqlScriptUtils.length(InternalQlScriptUtils.docValue(doc,params.v0)),par
|
|||
"params":{"v0":"constant_keyword","v1":5}
|
||||
;
|
||||
|
||||
startsWithFunction-caseInSensitive
|
||||
startsWithFunction-caseInsensitive
|
||||
process where startsWith(user_name, 'A')
|
||||
;
|
||||
"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.startsWith(
|
||||
|
@ -185,14 +205,21 @@ InternalEqlScriptUtils.string(InternalQlScriptUtils.docValue(doc,params.v0)),par
|
|||
"params":{"v0":"pid","v1":"123"}
|
||||
;
|
||||
|
||||
indexOfFunction
|
||||
indexOfFunction-caseSensitive
|
||||
process where indexOf(user_name, 'A', 2) > 0
|
||||
;
|
||||
"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.gt(
|
||||
InternalEqlScriptUtils.indexOf(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2),params.v3))",
|
||||
"params":{"v0":"user_name","v1":"A","v2":2,"v3":0}
|
||||
InternalEqlScriptUtils.indexOf(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2,params.v3),params.v4))",
|
||||
"params":{"v0":"user_name","v1":"A","v2":2,"v3":true,"v4":0}
|
||||
;
|
||||
|
||||
indexOfFunction-caseInsensitive
|
||||
process where indexOf(user_name, 'A', 2) > 0
|
||||
;
|
||||
"script":{"source":"InternalQlScriptUtils.nullSafeFilter(InternalQlScriptUtils.gt(
|
||||
InternalEqlScriptUtils.indexOf(InternalQlScriptUtils.docValue(doc,params.v0),params.v1,params.v2,params.v3),params.v4))",
|
||||
"params":{"v0":"user_name","v1":"A","v2":2,"v3":false,"v4":0}
|
||||
;
|
||||
|
||||
substringFunction
|
||||
process where substring(file_name, -4) == '.exe'
|
||||
|
|
|
@ -394,6 +394,28 @@ public class FunctionRegistry {
|
|||
T build(Source source, Expression src, Expression exp1, Expression exp2);
|
||||
}
|
||||
|
||||
@SuppressWarnings("overloads") // These are ambiguous if you aren't using ctor references but we always do
|
||||
public static <T extends Function> FunctionDefinition def(Class<T> function,
|
||||
ScalarTriFunctionConfigurationAwareBuilder<T> ctorRef, String... names) {
|
||||
FunctionBuilder builder = (source, children, distinct, cfg) -> {
|
||||
boolean hasMinimumTwo = OptionalArgument.class.isAssignableFrom(function);
|
||||
if (hasMinimumTwo && (children.size() > 3 || children.size() < 2)) {
|
||||
throw new QlIllegalArgumentException("expects two or three arguments");
|
||||
} else if (!hasMinimumTwo && children.size() != 3) {
|
||||
throw new QlIllegalArgumentException("expects exactly three arguments");
|
||||
}
|
||||
if (distinct) {
|
||||
throw new QlIllegalArgumentException("does not support DISTINCT yet it was specified");
|
||||
}
|
||||
return ctorRef.build(source, children.get(0), children.get(1), children.size() == 3 ? children.get(2) : null, cfg);
|
||||
};
|
||||
return def(function, builder, false, names);
|
||||
}
|
||||
|
||||
protected interface ScalarTriFunctionConfigurationAwareBuilder<T> {
|
||||
T build(Source source, Expression exp1, Expression exp2, Expression exp3, Configuration configuration);
|
||||
}
|
||||
|
||||
@SuppressWarnings("overloads") // These are ambiguous if you aren't using ctor references but we always do
|
||||
public static <T extends Function> FunctionDefinition def(Class<T> function,
|
||||
FourParametersFunctionBuilder<T> ctorRef, String... names) {
|
||||
|
|
Loading…
Reference in New Issue