From f2ef6ff656e88ba26e38f1236c600110e01402f4 Mon Sep 17 00:00:00 2001 From: Nik Everett Date: Mon, 19 May 2025 12:09:52 -0400 Subject: [PATCH] ESQL: Fix a test bug When adding support for pushing `==` to `semantic_text` we were incorrectly asserting that all queries to that field used nested documents. That's normal for `semantic_text`, but sometimes we query indices that don't have any nested fields. Closes #128122 --- .../esql/qa/single_node/PushQueriesIT.java | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java index 518b097c6604f..43e37b97cc126 100644 --- a/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java +++ b/x-pack/plugin/esql/qa/server/single-node/src/javaRestTest/java/org/elasticsearch/xpack/esql/qa/single_node/PushQueriesIT.java @@ -13,6 +13,7 @@ import org.elasticsearch.client.Request; import org.elasticsearch.client.Response; import org.elasticsearch.client.ResponseException; +import org.elasticsearch.common.collect.Iterators; import org.elasticsearch.test.ListMatcher; import org.elasticsearch.test.MapMatcher; import org.elasticsearch.test.TestClustersThreadFilter; @@ -21,6 +22,7 @@ import org.elasticsearch.xcontent.XContentType; import org.elasticsearch.xpack.esql.AssertWarnings; import org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase; +import org.hamcrest.Matcher; import org.junit.ClassRule; import java.io.IOException; @@ -39,6 +41,7 @@ import static org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.runEsql; import static org.elasticsearch.xpack.esql.qa.single_node.RestEsqlIT.commonProfile; import static org.elasticsearch.xpack.esql.qa.single_node.RestEsqlIT.fixTypesOnProfile; +import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.startsWith; @@ -79,7 +82,7 @@ public void testEquality() throws IOException { case "match_only_text", "semantic_text" -> true; default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; - testPushQuery(value, esqlQuery, luceneQuery, filterInCompute, true); + testPushQuery(value, esqlQuery, List.of(luceneQuery), filterInCompute, true); } public void testEqualityTooBigToPush() throws IOException { @@ -93,7 +96,7 @@ public void testEqualityTooBigToPush() throws IOException { case "semantic_text" -> "FieldExistsQuery [field=_primary_term]"; default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; - testPushQuery(value, esqlQuery, luceneQuery, true, true); + testPushQuery(value, esqlQuery, List.of(luceneQuery), true, true); } /** @@ -111,7 +114,7 @@ public void testEqualityOrTooBig() throws IOException { case "semantic_text" -> "FieldExistsQuery [field=_primary_term]"; default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; - testPushQuery(value, esqlQuery, luceneQuery, true, true); + testPushQuery(value, esqlQuery, List.of(luceneQuery), true, true); } public void testEqualityOrOther() throws IOException { @@ -131,7 +134,7 @@ public void testEqualityOrOther() throws IOException { case "match_only_text", "semantic_text" -> true; default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; - testPushQuery(value, esqlQuery, luceneQuery, filterInCompute, true); + testPushQuery(value, esqlQuery, List.of(luceneQuery), filterInCompute, true); } public void testEqualityAndOther() throws IOException { @@ -140,15 +143,15 @@ public void testEqualityAndOther() throws IOException { FROM test | WHERE test == "%value" AND foo == 1 """; - String luceneQuery = switch (type) { - case "text", "auto" -> "#test.keyword:%value -_ignored:test.keyword #foo:[1 TO 1]"; - case "match_only_text" -> "foo:[1 TO 1]"; + List luceneQueryOptions = switch (type) { + case "text", "auto" -> List.of("#test.keyword:%value -_ignored:test.keyword #foo:[1 TO 1]"); + case "match_only_text" -> List.of("foo:[1 TO 1]"); case "semantic_text" -> /* * single_value_match is here because there are extra documents hiding in the index * that don't have the `foo` field. */ - "#foo:[1 TO 1] #single_value_match(foo)"; + List.of("#foo:[1 TO 1] #single_value_match(foo)", "foo:[1 TO 1]"); default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; boolean filterInCompute = switch (type) { @@ -156,7 +159,7 @@ public void testEqualityAndOther() throws IOException { case "match_only_text", "semantic_text" -> true; default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; - testPushQuery(value, esqlQuery, luceneQuery, filterInCompute, true); + testPushQuery(value, esqlQuery, luceneQueryOptions, filterInCompute, true); } public void testInequality() throws IOException { @@ -171,7 +174,7 @@ public void testInequality() throws IOException { case "semantic_text" -> "FieldExistsQuery [field=_primary_term]"; default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; - testPushQuery(value, esqlQuery, luceneQuery, true, true); + testPushQuery(value, esqlQuery, List.of(luceneQuery), true, true); } public void testInequalityTooBigToPush() throws IOException { @@ -185,7 +188,7 @@ public void testInequalityTooBigToPush() throws IOException { case "semantic_text" -> "FieldExistsQuery [field=_primary_term]"; default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; - testPushQuery(value, esqlQuery, luceneQuery, true, false); + testPushQuery(value, esqlQuery, List.of(luceneQuery), true, false); } public void testCaseInsensitiveEquality() throws IOException { @@ -199,10 +202,10 @@ public void testCaseInsensitiveEquality() throws IOException { case "semantic_text" -> "FieldExistsQuery [field=_primary_term]"; default -> throw new UnsupportedOperationException("unknown type [" + type + "]"); }; - testPushQuery(value, esqlQuery, luceneQuery, true, true); + testPushQuery(value, esqlQuery, List.of(luceneQuery), true, true); } - private void testPushQuery(String value, String esqlQuery, String luceneQuery, boolean filterInCompute, boolean found) + private void testPushQuery(String value, String esqlQuery, List luceneQueryOptions, boolean filterInCompute, boolean found) throws IOException { indexValue(value); String differentValue = randomValueOtherThan(value, () -> randomAlphaOfLength(value.isEmpty() ? 1 : value.length())); @@ -222,6 +225,12 @@ private void testPushQuery(String value, String esqlQuery, String luceneQuery, b matchesList().item(matchesMap().entry("name", "test").entry("type", "text")), equalTo(found ? List.of(List.of(value)) : List.of()) ); + Matcher luceneQueryMatcher = anyOf( + () -> Iterators.map( + luceneQueryOptions.iterator(), + (String s) -> equalTo(s.replaceAll("%value", value).replaceAll("%different_value", differentValue)) + ) + ); @SuppressWarnings("unchecked") List> profiles = (List>) ((Map) result.get("profile")).get("drivers"); @@ -232,7 +241,7 @@ private void testPushQuery(String value, String esqlQuery, String luceneQuery, b @SuppressWarnings("unchecked") List> operators = (List>) p.get("operators"); for (Map o : operators) { - sig.add(checkOperatorProfile(o, luceneQuery.replaceAll("%value", value).replaceAll("%different_value", differentValue))); + sig.add(checkOperatorProfile(o, luceneQueryMatcher)); } String description = p.get("description").toString(); switch (description) { @@ -317,7 +326,7 @@ private void indexValue(String value) throws IOException { private static final Pattern TO_NAME = Pattern.compile("\\[.+", Pattern.DOTALL); - private static String checkOperatorProfile(Map o, String query) { + private static String checkOperatorProfile(Map o, Matcher query) { String name = (String) o.get("operator"); name = TO_NAME.matcher(name).replaceAll(""); if (name.equals("LuceneSourceOperator")) {