Skip to content

Commit 36c2e40

Browse files
committed
Add testing and capability
1 parent 6b94c48 commit 36c2e40

File tree

6 files changed

+128
-1
lines changed

6 files changed

+128
-1
lines changed

x-pack/plugin/esql/qa/testFixtures/src/main/resources/kql-function.csv-spec

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,21 @@ r:double | author: text
268268
4.670000076293945 | Walter Scheps
269269
4.559999942779541 | J.R.R. Tolkien
270270
;
271+
272+
testKqlInStatsWithGroupingBy
273+
required_capability: lucene_query_evaluator_query_rewrite
274+
FROM airports
275+
| STATS c = COUNT(*) where kql("country: United States") BY scalerank
276+
| SORT scalerank desc
277+
;
278+
279+
c: long | scalerank: long
280+
0 | 9
281+
44 | 8
282+
10 | 7
283+
28 | 6
284+
10 | 5
285+
12 | 4
286+
10 | 3
287+
15 | 2
288+
;

x-pack/plugin/esql/qa/testFixtures/src/main/resources/match-function.csv-spec

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,3 +841,21 @@ r:double | author: text
841841
4.670000076293945 | Walter Scheps
842842
4.559999942779541 | J.R.R. Tolkien
843843
;
844+
845+
testMatchInStatsWithGroupingBy
846+
required_capability: full_text_functions_in_stats_where
847+
FROM airports
848+
| STATS c = COUNT(*) where match(country, "United States") BY scalerank
849+
| SORT scalerank desc
850+
;
851+
852+
c: long | scalerank: long
853+
0 | 9
854+
44 | 8
855+
10 | 7
856+
28 | 6
857+
10 | 5
858+
12 | 4
859+
10 | 3
860+
15 | 2
861+
;

x-pack/plugin/esql/qa/testFixtures/src/main/resources/qstr-function.csv-spec

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,3 +299,21 @@ r:double | author: text
299299
4.670000076293945 | Walter Scheps
300300
4.559999942779541 | J.R.R. Tolkien
301301
;
302+
303+
testQstrInStatsWithGroupingBy
304+
required_capability: full_text_functions_in_stats_where
305+
FROM airports
306+
| STATS c = COUNT(*) where qstr("country: \"United States\"") BY scalerank
307+
| SORT scalerank desc
308+
;
309+
310+
c: long | scalerank: long
311+
0 | 9
312+
44 | 8
313+
10 | 7
314+
28 | 6
315+
10 | 5
316+
12 | 4
317+
10 | 3
318+
15 | 2
319+
;

x-pack/plugin/esql/src/internalClusterTest/java/org/elasticsearch/xpack/esql/plugin/KqlFunctionIT.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package org.elasticsearch.xpack.esql.plugin;
99

10+
import org.elasticsearch.ElasticsearchException;
1011
import org.elasticsearch.action.index.IndexRequest;
1112
import org.elasticsearch.action.support.WriteRequest;
1213
import org.elasticsearch.common.settings.Settings;
@@ -16,12 +17,14 @@
1617
import org.elasticsearch.xpack.esql.VerificationException;
1718
import org.elasticsearch.xpack.esql.action.AbstractEsqlIntegTestCase;
1819
import org.elasticsearch.xpack.kql.KqlPlugin;
20+
import org.hamcrest.Matchers;
1921
import org.junit.Before;
2022

2123
import java.util.Collection;
2224
import java.util.List;
2325

2426
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
27+
import static org.elasticsearch.xpack.esql.EsqlTestUtils.getValuesList;
2528
import static org.hamcrest.CoreMatchers.containsString;
2629

2730
public class KqlFunctionIT extends AbstractEsqlIntegTestCase {
@@ -91,6 +94,42 @@ public void testInvalidKqlQueryLexicalError() {
9194
assertThat(error.getRootCause().getMessage(), containsString("line 1:1: extraneous input ':' "));
9295
}
9396

97+
public void testKqlhWithStats() {
98+
var errorQuery = """
99+
FROM test
100+
| STATS c = count(*) BY kql("content: fox")
101+
""";
102+
103+
var error = expectThrows(ElasticsearchException.class, () -> run(errorQuery));
104+
assertThat(error.getMessage(), containsString("[KQL] function is only supported in WHERE and STATS commands"));
105+
106+
var query = """
107+
FROM test
108+
| STATS c = count(*) WHERE kql("content: fox"), d = count(*) WHERE kql("content: dog")
109+
""";
110+
111+
try (var resp = run(query)) {
112+
assertColumnNames(resp.columns(), List.of("c", "d"));
113+
assertColumnTypes(resp.columns(), List.of("long", "long"));
114+
assertValues(resp.values(), List.of(List.of(4L, 4L)));
115+
}
116+
117+
query = """
118+
FROM test METADATA _score
119+
| WHERE kql("content: fox")
120+
| STATS m = max(_score), n = min(_score)
121+
""";
122+
123+
try (var resp = run(query)) {
124+
assertColumnNames(resp.columns(), List.of("m", "n"));
125+
assertColumnTypes(resp.columns(), List.of("double", "double"));
126+
List<List<Object>> valuesList = getValuesList(resp.values());
127+
assertEquals(1, valuesList.size());
128+
assertThat((double) valuesList.get(0).get(0), Matchers.lessThan(1.0));
129+
assertThat((double) valuesList.get(0).get(1), Matchers.greaterThan(0.0));
130+
}
131+
}
132+
94133
private void createAndPopulateIndex() {
95134
var indexName = "test";
96135
var client = client().admin().indices();

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,13 @@ public enum Cap {
11021102
* Avid GROK and DISSECT attributes being removed when resolving fields.
11031103
* see <a href="https://github.com/elastic/elasticsearch/issues/127468"> ES|QL: Grok only supports KEYWORD or TEXT values, found expression [type] type [INTEGER] #127468 </a>
11041104
*/
1105-
KEEP_REGEX_EXTRACT_ATTRIBUTES;
1105+
KEEP_REGEX_EXTRACT_ATTRIBUTES,
1106+
1107+
/**
1108+
* {@link org.elasticsearch.compute.lucene.LuceneQueryEvaluator} rewrites the query before executing it in Lucene. This
1109+
* provides support for KQL in a STATS ... BY command that uses a KQL query for filter, for example.
1110+
*/
1111+
LUCENE_QUERY_EVALUATOR_QUERY_REWRITE;
11061112

11071113
private final boolean enabled;
11081114

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
1111

1212
import org.apache.lucene.search.IndexSearcher;
13+
import org.apache.lucene.util.BytesRef;
1314
import org.elasticsearch.Build;
1415
import org.elasticsearch.common.network.NetworkAddress;
1516
import org.elasticsearch.common.settings.Settings;
@@ -2037,6 +2038,33 @@ public void testMatchFunctionStatisWithNonPushableCondition() {
20372038
assertNull(esQuery.query());
20382039
}
20392040

2041+
public void testMatchFunctionWithStatsBy() {
2042+
String query = """
2043+
from test
2044+
| stats count(*) where match(job_positions, "Data Scientist") by gender
2045+
""";
2046+
var analyzer = makeAnalyzer("mapping-default.json");
2047+
var plannerOptimizer = new TestPlannerOptimizer(config, analyzer);
2048+
var plan = plannerOptimizer.plan(query);
2049+
2050+
var limit = as(plan, LimitExec.class);
2051+
var agg = as(limit.child(), AggregateExec.class);
2052+
var grouping = as(agg.groupings().get(0), FieldAttribute.class);
2053+
assertEquals("gender", grouping.name());
2054+
var aggregateAlias = as(agg.aggregates().get(0), Alias.class);
2055+
assertEquals("count(*) where match(job_positions, \"Data Scientist\")", aggregateAlias.name());
2056+
var count = as(aggregateAlias.child(), Count.class);
2057+
var countFilter = as(count.filter(), Match.class);
2058+
assertEquals("Data Scientist", ((BytesRef) ((Literal) countFilter.query()).value()).utf8ToString());
2059+
var aggregateFieldAttr = as(agg.aggregates().get(1), FieldAttribute.class);
2060+
assertEquals("gender", aggregateFieldAttr.name());
2061+
var exchange = as(agg.child(), ExchangeExec.class);
2062+
var aggExec = as(exchange.child(), AggregateExec.class);
2063+
var fieldExtract = as(aggExec.child(), FieldExtractExec.class);
2064+
var esQuery = as(fieldExtract.child(), EsQueryExec.class);
2065+
assertNull(esQuery.query());
2066+
}
2067+
20402068
private QueryBuilder wrapWithSingleQuery(String query, QueryBuilder inner, String fieldName, Source source) {
20412069
return FilterTests.singleValueQuery(query, inner, fieldName, source);
20422070
}

0 commit comments

Comments
 (0)