Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/changelog/128750.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pr: 128750
summary: Fix conversion of a Lucene wildcard pattern to a regexp
area: ES|QL
type: bug
issues:
- 128677
- 128676
6 changes: 0 additions & 6 deletions muted-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -489,12 +489,6 @@ tests:
- class: org.elasticsearch.packaging.test.DockerTests
method: test085EnvironmentVariablesAreRespectedUnderDockerExec
issue: https://github.com/elastic/elasticsearch/issues/128115
- class: org.elasticsearch.xpack.esql.expression.function.scalar.string.WildcardLikeTests
method: testEvaluateInManyThreads {TestCase=100 random code points matches self case insensitive with keyword}
issue: https://github.com/elastic/elasticsearch/issues/128676
- class: org.elasticsearch.xpack.esql.expression.function.scalar.string.WildcardLikeTests
method: testEvaluateInManyThreads {TestCase=100 random code points matches self case insensitive with text}
issue: https://github.com/elastic/elasticsearch/issues/128677
- class: org.elasticsearch.xpack.esql.expression.function.scalar.string.RLikeTests
method: testEvaluateInManyThreads {TestCase=100 random code points matches self case insensitive with text}
issue: https://github.com/elastic/elasticsearch/issues/128705
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ public static String wildcardToJavaPattern(String pattern, char escape) {

/**
* Translates a Lucene wildcard pattern to a Lucene RegExp one.
* Note: all RegExp "optional" characters are escaped too (allowing the use of the {@code RegExp.ALL} flag).
* @param wildcard Lucene wildcard pattern
* @return Lucene RegExp pattern
*/
Expand Down Expand Up @@ -209,7 +210,10 @@ public static String luceneWildcardToRegExp(String wildcard) {
regex.append("\\\\");
}
}
case '$', '(', ')', '+', '.', '[', ']', '^', '{', '|', '}' -> regex.append("\\").append(c);
// reserved RegExp characters
case '"', '$', '(', ')', '+', '.', '[', ']', '^', '{', '|', '}' -> regex.append("\\").append(c);
// reserved optional RegExp characters
case '#', '&', '<', '>' -> regex.append("\\").append(c);
default -> regex.append(c);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ public void testLuceneWildcardToRegExp() {
assertThat(luceneWildcardToRegExp("foo\\*bar"), is("foo\\*bar"));
assertThat(luceneWildcardToRegExp("foo\\?bar\\?"), is("foo\\?bar\\?"));
assertThat(luceneWildcardToRegExp("foo\\?bar\\"), is("foo\\?bar\\\\"));
assertThat(luceneWildcardToRegExp("[](){}^$.|+"), is("\\[\\]\\(\\)\\{\\}\\^\\$\\.\\|\\+"));
// reserved characters
assertThat(luceneWildcardToRegExp("\"[](){}^$.|+"), is("\\\"\\[\\]\\(\\)\\{\\}\\^\\$\\.\\|\\+"));
// reserved "optional" characters
assertThat(luceneWildcardToRegExp("#&<>"), is("\\#\\&\\<\\>"));
assertThat(luceneWildcardToRegExp("foo\\\uD83D\uDC14bar"), is("foo\uD83D\uDC14bar"));
assertThat(luceneWildcardToRegExp("foo\uD83D\uDC14bar"), is("foo\uD83D\uDC14bar"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1408,7 +1408,36 @@ public void testAsyncGetWithoutContentType() throws IOException {
.item(matchesMap().entry("name", "integer").entry("type", "integer")),
values
);
}

public void testReplaceStringCasingWithInsensitiveWildcardMatch() throws IOException {
createIndex(testIndexName(), Settings.EMPTY, """
{
"properties": {
"reserved": {
"type": "keyword"
},
"optional": {
"type": "keyword"
}
}
}
""");
Request doc = new Request("POST", testIndexName() + "/_doc?refresh=true");
doc.setJsonEntity("""
{
"reserved": "_\\"_$_(_)_+_._[_]_^_{_|_}___",
"optional": "_#_&_<_>___"
}
""");
client().performRequest(doc);
var query = "FROM " + testIndexName() + """
| WHERE TO_LOWER(reserved) LIKE "_\\"_$_(_)_+_._[_]_^_{_|_}*"
| WHERE TO_LOWER(optional) LIKE "_#_&_<_>*"
| KEEP reserved, optional
""";
var answer = runEsql(requestObjectBuilder().query(query));
assertThat(answer.get("values"), equalTo(List.of(List.of("_\"_$_(_)_+_._[_]_^_{_|_}___", "_#_&_<_>___"))));
}

protected static Request prepareRequestWithOptions(RequestObjectBuilder requestObject, Mode mode) throws IOException {
Expand Down
Loading