Skip to content

Commit c0c6bad

Browse files
Integrate with latest from main, add UTs
1 parent 5417628 commit c0c6bad

File tree

12 files changed

+702
-599
lines changed

12 files changed

+702
-599
lines changed

docs/reference/query-languages/esql/_snippets/operators/detailedDescription/rlike.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,24 @@ ROW message = "foo ( bar"
1010
```
1111

1212

13+
To reduce the overhead of escaping, we suggest using triple quotes strings `"""`
14+
1315
```esql
14-
ROW message = "foobar"
15-
| WHERE message RLIKE ("foo.*", "bar.")
16+
ROW message = "foo ( bar"
17+
| WHERE message RLIKE """foo \( bar"""
1618
```
1719

20+
```{applies_to}
21+
stack: ga 9.1
22+
serverless: ga
23+
```
1824

19-
To reduce the overhead of escaping, we suggest using triple quotes strings `"""`
25+
Both a single pattern or a list of patterns are supported. If a list of patterns is provided,
26+
the expression will return true if any of the patterns match.
2027

2128
```esql
22-
ROW message = "foo ( bar"
23-
| WHERE message RLIKE """foo \( bar"""
29+
ROW message = "foobar"
30+
| WHERE message RLIKE ("foo.*", "bar.")
2431
```
2532

2633

docs/reference/query-languages/esql/kibana/definition/operators/not rlike.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/query-languages/esql/kibana/definition/operators/rlike.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/query-languages/esql/kibana/docs/operators/not rlike.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/reference/query-languages/esql/kibana/docs/operators/rlike.md

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClustersIT.java

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -205,25 +205,24 @@ private Map<String, Object> runEsql(RestEsqlTestCase.RequestObjectBuilder reques
205205
}
206206
}
207207

208-
private <C, V> void assertResultMapForLike(
208+
private <C, V> void assertResultMapWithCapabilities(
209209
boolean includeCCSMetadata,
210210
Map<String, Object> result,
211211
C columns,
212212
V values,
213213
boolean remoteOnly,
214-
boolean requireLikeListCapability
214+
List<String> fullResultCapabilities
215215
) throws IOException {
216-
List<String> requiredCapabilities = new ArrayList<>(List.of("like_on_index_fields"));
217-
if (requireLikeListCapability) {
218-
requiredCapabilities.add("like_list_on_index_fields");
219-
}
220216
// the feature is completely supported if both local and remote clusters support it
221-
boolean isSupported = capabilitiesSupportedNewAndOld(requiredCapabilities);
222-
217+
// otherwise we expect a partial result, and will not check the data
218+
boolean isSupported = capabilitiesSupportedNewAndOld(fullResultCapabilities);
223219
if (isSupported) {
224220
assertResultMap(includeCCSMetadata, result, columns, values, remoteOnly);
225221
} else {
226-
logger.info("--> skipping data check for like index test, cluster does not support like index feature");
222+
logger.info(
223+
"--> skipping data check for a test, cluster does not support all of [{}] capabilities",
224+
String.join(",", fullResultCapabilities)
225+
);
227226
// just verify that we did not get a partial result
228227
var clusters = result.get("_clusters");
229228
var reason = "unexpected partial results" + (clusters != null ? ": _clusters=" + clusters : "");
@@ -526,7 +525,7 @@ public void testLikeIndex() throws Exception {
526525
""", includeCCSMetadata);
527526
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
528527
var values = List.of(List.of(remoteDocs.size(), REMOTE_CLUSTER_NAME + ":" + remoteIndex));
529-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, false);
528+
assertResultMapWithCapabilities(includeCCSMetadata, result, columns, values, false, List.of("like_on_index_fields"));
530529
}
531530

532531
public void testLikeIndexLegacySettingNoResults() throws Exception {
@@ -548,7 +547,7 @@ public void testLikeIndexLegacySettingNoResults() throws Exception {
548547
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
549548
// we expect empty result, since the setting is false
550549
var values = List.of();
551-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, false);
550+
assertResultMapWithCapabilities(includeCCSMetadata, result, columns, values, false, List.of("like_on_index_fields"));
552551
}
553552
}
554553

@@ -572,7 +571,7 @@ public void testLikeIndexLegacySettingResults() throws Exception {
572571
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
573572
// we expect results, since the setting is false, but there is : in the LIKE query
574573
var values = List.of(List.of(remoteDocs.size(), REMOTE_CLUSTER_NAME + ":" + remoteIndex));
575-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, false);
574+
assertResultMapWithCapabilities(includeCCSMetadata, result, columns, values, false, List.of("like_on_index_fields"));
576575
}
577576
}
578577

@@ -586,7 +585,7 @@ public void testNotLikeIndex() throws Exception {
586585
""", includeCCSMetadata);
587586
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
588587
var values = List.of(List.of(localDocs.size(), localIndex));
589-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, false);
588+
assertResultMapWithCapabilities(includeCCSMetadata, result, columns, values, false, List.of("like_on_index_fields"));
590589
}
591590

592591
public void testLikeListIndex() throws Exception {
@@ -601,7 +600,14 @@ public void testLikeListIndex() throws Exception {
601600
""", includeCCSMetadata);
602601
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
603602
var values = List.of(List.of(remoteDocs.size(), REMOTE_CLUSTER_NAME + ":" + remoteIndex));
604-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, true);
603+
assertResultMapWithCapabilities(
604+
includeCCSMetadata,
605+
result,
606+
columns,
607+
values,
608+
false,
609+
List.of("like_on_index_fields", "like_list_on_index_fields")
610+
);
605611
}
606612

607613
public void testNotLikeListIndex() throws Exception {
@@ -615,7 +621,14 @@ public void testNotLikeListIndex() throws Exception {
615621
""", includeCCSMetadata);
616622
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
617623
var values = List.of(List.of(localDocs.size(), localIndex));
618-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, true);
624+
assertResultMapWithCapabilities(
625+
includeCCSMetadata,
626+
result,
627+
columns,
628+
values,
629+
false,
630+
List.of("like_on_index_fields", "like_list_on_index_fields")
631+
);
619632
}
620633

621634
public void testNotLikeListKeyword() throws Exception {
@@ -639,7 +652,14 @@ public void testNotLikeListKeyword() throws Exception {
639652
if (localCount > 0) {
640653
values.add(List.of(localCount, localIndex));
641654
}
642-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, true);
655+
assertResultMapWithCapabilities(
656+
includeCCSMetadata,
657+
result,
658+
columns,
659+
values,
660+
false,
661+
List.of("like_on_index_fields", "like_list_on_index_fields")
662+
);
643663
}
644664

645665
public void testRLikeIndex() throws Exception {
@@ -652,7 +672,7 @@ public void testRLikeIndex() throws Exception {
652672
""", includeCCSMetadata);
653673
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
654674
var values = List.of(List.of(remoteDocs.size(), REMOTE_CLUSTER_NAME + ":" + remoteIndex));
655-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, false);
675+
assertResultMapWithCapabilities(includeCCSMetadata, result, columns, values, false, List.of("like_on_index_fields"));
656676
}
657677

658678
public void testNotRLikeIndex() throws Exception {
@@ -665,7 +685,37 @@ public void testNotRLikeIndex() throws Exception {
665685
""", includeCCSMetadata);
666686
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
667687
var values = List.of(List.of(localDocs.size(), localIndex));
668-
assertResultMapForLike(includeCCSMetadata, result, columns, values, false, false);
688+
assertResultMapWithCapabilities(includeCCSMetadata, result, columns, values, false, List.of("like_on_index_fields"));
689+
}
690+
691+
public void testRLikeListIndex() throws Exception {
692+
assumeTrue("not supported", capabilitiesSupportedNewAndOld(List.of("rlike_with_list_of_patterns")));
693+
boolean includeCCSMetadata = includeCCSMetadata();
694+
Map<String, Object> result = run("""
695+
FROM test-local-index,*:test-remote-index METADATA _index
696+
| WHERE _index RLIKE (".*remote.*", ".*not-exist.*")
697+
| STATS c = COUNT(*) BY _index
698+
| SORT _index ASC
699+
""", includeCCSMetadata);
700+
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
701+
var values = List.of(List.of(remoteDocs.size(), REMOTE_CLUSTER_NAME + ":" + remoteIndex));
702+
// we depend on the code in like_on_index_fields to serialize an ExpressionQueryBuilder
703+
assertResultMapWithCapabilities(includeCCSMetadata, result, columns, values, false, List.of("like_on_index_fields"));
704+
}
705+
706+
public void testNotRLikeListIndex() throws Exception {
707+
assumeTrue("not supported", capabilitiesSupportedNewAndOld(List.of("rlike_with_list_of_patterns")));
708+
boolean includeCCSMetadata = includeCCSMetadata();
709+
Map<String, Object> result = run("""
710+
FROM test-local-index,*:test-remote-index METADATA _index
711+
| WHERE _index NOT RLIKE (".*remote.*", ".*not-exist.*")
712+
| STATS c = COUNT(*) BY _index
713+
| SORT _index ASC
714+
""", includeCCSMetadata);
715+
var columns = List.of(Map.of("name", "c", "type", "long"), Map.of("name", "_index", "type", "keyword"));
716+
var values = List.of(List.of(localDocs.size(), localIndex));
717+
// we depend on the code in like_on_index_fields to serialize an ExpressionQueryBuilder
718+
assertResultMapWithCapabilities(includeCCSMetadata, result, columns, values, false, List.of("like_on_index_fields"));
669719
}
670720

671721
private RestClient remoteClusterClient() throws IOException {

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1237,7 +1237,7 @@ public enum Cap {
12371237
/**
12381238
* Support for the RLIKE operator with a list of regexes.
12391239
*/
1240-
RLIKE_WITH_LIST_OF_PATTERNS;
1240+
RLIKE_WITH_LIST_OF_PATTERNS,
12411241

12421242
/**
12431243
* FUSE command

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/regex/RLike.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,25 @@ public class RLike extends RegexMatch<RLikePattern> {
3333
Use `RLIKE` to filter data based on string patterns using using
3434
<<regexp-syntax,regular expressions>>. `RLIKE` usually acts on a field placed on
3535
the left-hand side of the operator, but it can also act on a constant (literal)
36-
expression. The right-hand side of the operator represents the pattern or a list of patterns.""", detailedDescription = """
36+
expression. The right-hand side of the operator represents the pattern.""", detailedDescription = """
3737
Matching special characters (eg. `.`, `*`, `(`...) will require escaping.
3838
The escape character is backslash `\\`. Since also backslash is a special character in string literals,
3939
it will require further escaping.
4040
4141
<<load-esql-example, file=string tag=rlikeEscapingSingleQuotes>>
4242
43-
<<load-esql-example, file=where-like tag=rlikeListDocExample>>
44-
4543
To reduce the overhead of escaping, we suggest using triple quotes strings `\"\"\"`
4644
4745
<<load-esql-example, file=string tag=rlikeEscapingTripleQuotes>>
46+
```{applies_to}
47+
stack: ga 9.1
48+
serverless: ga
49+
```
50+
51+
Both a single pattern or a list of patterns are supported. If a list of patterns is provided,
52+
the expression will return true if any of the patterns match.
53+
54+
<<load-esql-example, file=where-like tag=rlikeListDocExample>>
4855
""", operator = NAME, examples = @Example(file = "docs", tag = "rlike"))
4956
public RLike(
5057
Source source,

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/regex/RLikeList.java

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,29 @@
77

88
package org.elasticsearch.xpack.esql.expression.function.scalar.string.regex;
99

10+
import org.apache.lucene.search.MultiTermQuery;
11+
import org.apache.lucene.util.automaton.Automaton;
12+
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
1013
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
1114
import org.elasticsearch.common.io.stream.StreamInput;
1215
import org.elasticsearch.common.io.stream.StreamOutput;
16+
import org.elasticsearch.index.mapper.MappedFieldType;
17+
import org.elasticsearch.index.query.SearchExecutionContext;
1318
import org.elasticsearch.xpack.esql.core.expression.Expression;
1419
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
1520
import org.elasticsearch.xpack.esql.core.expression.predicate.regex.RLikePattern;
1621
import org.elasticsearch.xpack.esql.core.expression.predicate.regex.RLikePatternList;
17-
import org.elasticsearch.xpack.esql.core.querydsl.query.EsqlAutomatonQuery;
1822
import org.elasticsearch.xpack.esql.core.querydsl.query.Query;
1923
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
2024
import org.elasticsearch.xpack.esql.core.tree.Source;
2125
import org.elasticsearch.xpack.esql.expression.function.Param;
26+
import org.elasticsearch.xpack.esql.io.stream.ExpressionQuery;
2227
import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
2328
import org.elasticsearch.xpack.esql.optimizer.rules.physical.local.LucenePushdownPredicates;
2429
import org.elasticsearch.xpack.esql.planner.TranslatorHandler;
2530

2631
import java.io.IOException;
32+
import java.util.function.Supplier;
2733
import java.util.stream.Collectors;
2834

2935
public class RLikeList extends RegexMatch<RLikePatternList> {
@@ -33,6 +39,30 @@ public class RLikeList extends RegexMatch<RLikePatternList> {
3339
RLikeList::new
3440
);
3541

42+
Supplier<Automaton> automatonSupplier = new Supplier<>() {
43+
Automaton cached;
44+
45+
@Override
46+
public Automaton get() {
47+
if (cached == null) {
48+
cached = pattern().createAutomaton(caseInsensitive());
49+
}
50+
return cached;
51+
}
52+
};
53+
54+
Supplier<CharacterRunAutomaton> characterRunAutomatonSupplier = new Supplier<>() {
55+
CharacterRunAutomaton cached;
56+
57+
@Override
58+
public CharacterRunAutomaton get() {
59+
if (cached == null) {
60+
cached = new CharacterRunAutomaton(automatonSupplier.get());
61+
}
62+
return cached;
63+
}
64+
};
65+
3666
/**
3767
* The documentation for this function is in RLike, and shown to the users as `RLIKE` in the docs.
3868
*/
@@ -97,15 +127,30 @@ public Query asQuery(LucenePushdownPredicates pushdownPredicates, TranslatorHand
97127
}
98128

99129
private Query translateField(String targetFieldName) {
100-
return new EsqlAutomatonQuery(source(), targetFieldName, pattern().createAutomaton(caseInsensitive()), getAutomatonDescription());
130+
return new ExpressionQuery(source(), targetFieldName, this);
131+
}
132+
133+
@Override
134+
public org.apache.lucene.search.Query asLuceneQuery(
135+
MappedFieldType fieldType,
136+
MultiTermQuery.RewriteMethod constantScoreRewrite,
137+
SearchExecutionContext context
138+
) {
139+
return fieldType.automatonQuery(
140+
automatonSupplier,
141+
characterRunAutomatonSupplier,
142+
constantScoreRewrite,
143+
context,
144+
getLuceneQueryDescription()
145+
);
101146
}
102147

103148
@Override
104149
protected NodeInfo<? extends Expression> info() {
105150
return NodeInfo.create(this, RLikeList::new, field(), pattern(), caseInsensitive());
106151
}
107152

108-
private String getAutomatonDescription() {
153+
private String getLuceneQueryDescription() {
109154
// we use the information used to create the automaton to describe the query here
110155
String patternDesc = pattern().patternList().stream().map(RLikePattern::pattern).collect(Collectors.joining("\", \""));
111156
return "RLIKE(\"" + patternDesc + "\"), caseInsensitive=" + caseInsensitive();

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/regex/WildcardLikeList.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ public Query asQuery(LucenePushdownPredicates pushdownPredicates, TranslatorHand
147147

148148
private boolean supportsPushdown(TransportVersion version) {
149149
return version == null || expressionTransportSupported(version);
150-
return new EsqlAutomatonQuery(source(), targetFieldName, pattern().createAutomaton(caseInsensitive()), getAutomatonDescription());
151150
}
152151

153152
@Override

0 commit comments

Comments
 (0)