Skip to content

Commit ee5a09e

Browse files
authored
QL: case sensitive support in EQL (elastic#56404)
* * StartsWith is case sensitive aware * Added case sensitivity to EQL configuration * case_sensitive parameter can be specified when running queries (default is case insensitive) * Added STARTS_WITH function to SQL as well * Add case sensitive aware queryfolder tests * Address reviews * Address review #2
1 parent 12ccf8d commit ee5a09e

File tree

61 files changed

+1021
-306
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1021
-306
lines changed

docs/reference/sql/functions/string.asciidoc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,36 @@ SPACE(count) <1>
422422
include-tagged::{sql-specs}/docs/docs.csv-spec[stringSpace]
423423
--------------------------------------------------
424424

425+
[[sql-functions-string-startswith]]
426+
==== `STARTS_WITH`
427+
428+
.Synopsis:
429+
[source, sql]
430+
--------------------------------------------------
431+
STARTS_WITH(
432+
source, <1>
433+
pattern) <2>
434+
--------------------------------------------------
435+
*Input*:
436+
437+
<1> string expression
438+
<2> string expression
439+
440+
*Output*: boolean value
441+
442+
*Description*: Returns `true` if the source expression starts with the specified pattern, `false` otherwise. The matching is case sensitive.
443+
If either parameters is `null`, the function returns `null`.
444+
445+
[source, sql]
446+
--------------------------------------------------
447+
include-tagged::{sql-specs}/docs/docs.csv-spec[stringStartsWithTrue]
448+
--------------------------------------------------
449+
450+
[source, sql]
451+
--------------------------------------------------
452+
include-tagged::{sql-specs}/docs/docs.csv-spec[stringStartsWithFalse]
453+
--------------------------------------------------
454+
425455
[[sql-functions-string-substring]]
426456
==== `SUBSTRING`
427457

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/action/EqlSearchRequest.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class EqlSearchRequest extends ActionRequest implements IndicesRequest.Re
4848
private int fetchSize = FETCH_SIZE;
4949
private SearchAfterBuilder searchAfterBuilder;
5050
private String query;
51+
private boolean isCaseSensitive = false;
5152

5253
static final String KEY_FILTER = "filter";
5354
static final String KEY_TIMESTAMP_FIELD = "timestamp_field";
@@ -56,6 +57,7 @@ public class EqlSearchRequest extends ActionRequest implements IndicesRequest.Re
5657
static final String KEY_SIZE = "size";
5758
static final String KEY_SEARCH_AFTER = "search_after";
5859
static final String KEY_QUERY = "query";
60+
static final String KEY_CASE_SENSITIVE = "case_sensitive";
5961

6062
static final ParseField FILTER = new ParseField(KEY_FILTER);
6163
static final ParseField TIMESTAMP_FIELD = new ParseField(KEY_TIMESTAMP_FIELD);
@@ -64,6 +66,7 @@ public class EqlSearchRequest extends ActionRequest implements IndicesRequest.Re
6466
static final ParseField SIZE = new ParseField(KEY_SIZE);
6567
static final ParseField SEARCH_AFTER = new ParseField(KEY_SEARCH_AFTER);
6668
static final ParseField QUERY = new ParseField(KEY_QUERY);
69+
static final ParseField CASE_SENSITIVE = new ParseField(KEY_CASE_SENSITIVE);
6770

6871
private static final ObjectParser<EqlSearchRequest, Void> PARSER = objectParser(EqlSearchRequest::new);
6972

@@ -82,6 +85,7 @@ public EqlSearchRequest(StreamInput in) throws IOException {
8285
fetchSize = in.readVInt();
8386
searchAfterBuilder = in.readOptionalWriteable(SearchAfterBuilder::new);
8487
query = in.readString();
88+
isCaseSensitive = in.readBoolean();
8589
}
8690

8791
@Override
@@ -143,6 +147,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
143147
}
144148

145149
builder.field(KEY_QUERY, query);
150+
builder.field(KEY_CASE_SENSITIVE, isCaseSensitive);
146151

147152
return builder;
148153
}
@@ -162,6 +167,7 @@ protected static <R extends EqlSearchRequest> ObjectParser<R, Void> objectParser
162167
parser.declareField(EqlSearchRequest::setSearchAfter, SearchAfterBuilder::fromXContent, SEARCH_AFTER,
163168
ObjectParser.ValueType.OBJECT_ARRAY);
164169
parser.declareString(EqlSearchRequest::query, QUERY);
170+
parser.declareBoolean(EqlSearchRequest::isCaseSensitive, CASE_SENSITIVE);
165171
return parser;
166172
}
167173

@@ -230,6 +236,13 @@ public EqlSearchRequest query(String query) {
230236
return this;
231237
}
232238

239+
public boolean isCaseSensitive() { return this.isCaseSensitive; }
240+
241+
public EqlSearchRequest isCaseSensitive(boolean isCaseSensitive) {
242+
this.isCaseSensitive = isCaseSensitive;
243+
return this;
244+
}
245+
233246
@Override
234247
public void writeTo(StreamOutput out) throws IOException {
235248
super.writeTo(out);
@@ -242,6 +255,7 @@ public void writeTo(StreamOutput out) throws IOException {
242255
out.writeVInt(fetchSize);
243256
out.writeOptionalWriteable(searchAfterBuilder);
244257
out.writeString(query);
258+
out.writeBoolean(isCaseSensitive);
245259
}
246260

247261
@Override
@@ -261,7 +275,8 @@ public boolean equals(Object o) {
261275
Objects.equals(eventCategoryField, that.eventCategoryField) &&
262276
Objects.equals(implicitJoinKeyField, that.implicitJoinKeyField) &&
263277
Objects.equals(searchAfterBuilder, that.searchAfterBuilder) &&
264-
Objects.equals(query, that.query);
278+
Objects.equals(query, that.query) &&
279+
Objects.equals(isCaseSensitive, that.isCaseSensitive);
265280
}
266281

267282
@Override
@@ -274,7 +289,8 @@ public int hashCode() {
274289
timestampField, eventCategoryField,
275290
implicitJoinKeyField,
276291
searchAfterBuilder,
277-
query);
292+
query,
293+
isCaseSensitive);
278294
}
279295

280296
@Override

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/action/EqlSearchRequestBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,8 @@ public EqlSearchRequestBuilder query(String query) {
5555
return this;
5656
}
5757

58+
public EqlSearchRequestBuilder query(boolean isCaseSensitive) {
59+
request.isCaseSensitive(isCaseSensitive);
60+
return this;
61+
}
5862
}

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/analysis/Analyzer.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.xpack.ql.expression.function.UnresolvedFunction;
1717
import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan;
1818
import org.elasticsearch.xpack.ql.rule.RuleExecutor;
19+
import org.elasticsearch.xpack.ql.session.Configuration;
1920

2021
import java.util.ArrayList;
2122
import java.util.Collection;
@@ -26,10 +27,12 @@
2627

2728
public class Analyzer extends RuleExecutor<LogicalPlan> {
2829

30+
private final Configuration configuration;
2931
private final FunctionRegistry functionRegistry;
3032
private final Verifier verifier;
3133

32-
public Analyzer(FunctionRegistry functionRegistry, Verifier verifier) {
34+
public Analyzer(Configuration configuration, FunctionRegistry functionRegistry, Verifier verifier) {
35+
this.configuration = configuration;
3336
this.functionRegistry = functionRegistry;
3437
this.verifier = verifier;
3538
}
@@ -113,7 +116,7 @@ protected LogicalPlan rule(LogicalPlan plan) {
113116
return uf.missing(functionName, functionRegistry.listFunctions());
114117
}
115118
FunctionDefinition def = functionRegistry.resolveFunction(functionName);
116-
Function f = uf.buildResolved(null, def);
119+
Function f = uf.buildResolved(configuration, def);
117120
return f;
118121
}
119122
return e;

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/execution/PlanExecutor.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99
import org.elasticsearch.action.ActionListener;
1010
import org.elasticsearch.client.Client;
1111
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
12-
import org.elasticsearch.xpack.eql.analysis.Analyzer;
1312
import org.elasticsearch.xpack.eql.analysis.PreAnalyzer;
1413
import org.elasticsearch.xpack.eql.analysis.Verifier;
1514
import org.elasticsearch.xpack.eql.expression.function.EqlFunctionRegistry;
1615
import org.elasticsearch.xpack.eql.optimizer.Optimizer;
1716
import org.elasticsearch.xpack.eql.parser.ParserParams;
1817
import org.elasticsearch.xpack.eql.planner.Planner;
19-
import org.elasticsearch.xpack.eql.session.Configuration;
18+
import org.elasticsearch.xpack.eql.session.EqlConfiguration;
2019
import org.elasticsearch.xpack.eql.session.EqlSession;
2120
import org.elasticsearch.xpack.eql.session.Results;
2221
import org.elasticsearch.xpack.eql.stats.Metrics;
@@ -33,7 +32,7 @@ public class PlanExecutor {
3332
private final FunctionRegistry functionRegistry;
3433

3534
private final PreAnalyzer preAnalyzer;
36-
private final Analyzer analyzer;
35+
private final Verifier verifier;
3736
private final Optimizer optimizer;
3837
private final Planner planner;
3938

@@ -50,16 +49,16 @@ public PlanExecutor(Client client, IndexResolver indexResolver, NamedWriteableRe
5049
this.metrics = new Metrics();
5150

5251
this.preAnalyzer = new PreAnalyzer();
53-
this.analyzer = new Analyzer(functionRegistry, new Verifier());
52+
this.verifier = new Verifier();
5453
this.optimizer = new Optimizer();
5554
this.planner = new Planner();
5655
}
5756

58-
private EqlSession newSession(Configuration cfg) {
59-
return new EqlSession(client, cfg, indexResolver, preAnalyzer, analyzer, optimizer, planner, this);
57+
private EqlSession newSession(EqlConfiguration cfg) {
58+
return new EqlSession(client, cfg, indexResolver, preAnalyzer, functionRegistry, verifier, optimizer, planner, this);
6059
}
6160

62-
public void eql(Configuration cfg, String eql, ParserParams parserParams, ActionListener<Results> listener) {
61+
public void eql(EqlConfiguration cfg, String eql, ParserParams parserParams, ActionListener<Results> listener) {
6362
newSession(cfg).eql(eql, parserParams, wrap(listener::onResponse, listener::onFailure));
6463
}
6564

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/execution/search/Querier.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import org.elasticsearch.search.builder.SearchSourceBuilder;
2020
import org.elasticsearch.tasks.TaskCancelledException;
2121
import org.elasticsearch.xpack.eql.querydsl.container.QueryContainer;
22-
import org.elasticsearch.xpack.eql.session.Configuration;
22+
import org.elasticsearch.xpack.eql.session.EqlConfiguration;
2323
import org.elasticsearch.xpack.eql.session.EqlSession;
2424
import org.elasticsearch.xpack.eql.session.Results;
2525
import org.elasticsearch.xpack.ql.expression.Attribute;
@@ -33,7 +33,7 @@ public class Querier {
3333

3434
private static final Logger log = LogManager.getLogger(Querier.class);
3535

36-
private final Configuration cfg;
36+
private final EqlConfiguration cfg;
3737
private final Client client;
3838
private final TimeValue keepAlive;
3939
private final QueryBuilder filter;

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/execution/search/SearchAfterListener.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import org.elasticsearch.xpack.eql.querydsl.container.ComputedRef;
2121
import org.elasticsearch.xpack.eql.querydsl.container.QueryContainer;
2222
import org.elasticsearch.xpack.eql.querydsl.container.SearchHitFieldRef;
23-
import org.elasticsearch.xpack.eql.session.Configuration;
23+
import org.elasticsearch.xpack.eql.session.EqlConfiguration;
2424
import org.elasticsearch.xpack.eql.session.Results;
2525
import org.elasticsearch.xpack.ql.execution.search.FieldExtraction;
2626
import org.elasticsearch.xpack.ql.execution.search.extractor.ComputingExtractor;
@@ -43,12 +43,12 @@ class SearchAfterListener implements ActionListener<SearchResponse> {
4343
private final ActionListener<Results> listener;
4444

4545
private final Client client;
46-
private final Configuration cfg;
46+
private final EqlConfiguration cfg;
4747
private final List<Attribute> output;
4848
private final QueryContainer container;
4949
private final SearchRequest request;
5050

51-
SearchAfterListener(ActionListener<Results> listener, Client client, Configuration cfg, List<Attribute> output,
51+
SearchAfterListener(ActionListener<Results> listener, Client client, EqlConfiguration cfg, List<Attribute> output,
5252
QueryContainer container, SearchRequest request) {
5353

5454
this.listener = listener;

x-pack/plugin/eql/src/main/java/org/elasticsearch/xpack/eql/expression/function/EqlFunctionRegistry.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
package org.elasticsearch.xpack.eql.expression.function;
88

9-
import org.elasticsearch.xpack.eql.expression.function.scalar.string.CIDRMatch;
109
import org.elasticsearch.xpack.eql.expression.function.scalar.string.Between;
10+
import org.elasticsearch.xpack.eql.expression.function.scalar.string.CIDRMatch;
1111
import org.elasticsearch.xpack.eql.expression.function.scalar.string.Concat;
1212
import org.elasticsearch.xpack.eql.expression.function.scalar.string.EndsWith;
1313
import org.elasticsearch.xpack.eql.expression.function.scalar.string.IndexOf;
@@ -50,15 +50,15 @@ private static FunctionDefinition[][] functions() {
5050
def(ToString.class, ToString::new, "string"),
5151
def(StringContains.class, StringContains::new, "stringcontains"),
5252
def(Substring.class, Substring::new, "substring"),
53-
def(Wildcard.class, Wildcard::new, "wildcard"),
53+
def(Wildcard.class, Wildcard::new, "wildcard")
5454
},
5555
// Arithmetic
5656
new FunctionDefinition[] {
57-
def(Add.class, Add::new, "add"),
58-
def(Div.class, Div::new, "divide"),
59-
def(Mod.class, Mod::new, "modulo"),
60-
def(Mul.class, Mul::new, "multiply"),
61-
def(Sub.class, Sub::new, "subtract"),
57+
def(Add.class, Add::new, "add"),
58+
def(Div.class, Div::new, "divide"),
59+
def(Mod.class, Mod::new, "modulo"),
60+
def(Mul.class, Mul::new, "multiply"),
61+
def(Sub.class, Sub::new, "subtract")
6262
}
6363
};
6464
}

0 commit comments

Comments
 (0)