Skip to content

Commit 4395bca

Browse files
committed
Adding KqlQuery to esql-core query dsl supported queries.
1 parent 342a4b9 commit 4395bca

File tree

3 files changed

+163
-1
lines changed

3 files changed

+163
-1
lines changed

x-pack/plugin/esql-core/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ esplugin {
66
name 'x-pack-esql-core'
77
description 'Elasticsearch infrastructure plugin for ESQL'
88
classname 'org.elasticsearch.xpack.esql.core.plugin.EsqlCorePlugin'
9-
extendedPlugins = ['x-pack-core']
9+
extendedPlugins = ['x-pack-core', 'x-pack-kql']
1010
}
1111

1212
base {
@@ -17,6 +17,7 @@ dependencies {
1717
api "org.antlr:antlr4-runtime:${versions.antlr4}"
1818
api project(path: xpackModule('mapper-version'))
1919
compileOnly project(path: xpackModule('core'))
20+
compileOnly project(path: xpackModule('kql'))
2021
testApi(project(xpackModule('esql-core:test-fixtures'))) {
2122
exclude group: 'org.elasticsearch.plugin', module: 'esql-core'
2223
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
package org.elasticsearch.xpack.esql.core.querydsl.query;
8+
9+
import org.elasticsearch.core.Booleans;
10+
import org.elasticsearch.index.query.QueryBuilder;
11+
import org.elasticsearch.xpack.esql.core.tree.Source;
12+
import org.elasticsearch.xpack.kql.query.KqlQueryBuilder;
13+
14+
import java.util.Collections;
15+
import java.util.Map;
16+
import java.util.Objects;
17+
import java.util.function.BiConsumer;
18+
19+
import static java.util.Map.entry;
20+
21+
public class KqlQuery extends Query {
22+
23+
private static final Map<String, BiConsumer<KqlQueryBuilder, String>> BUILDER_APPLIERS = Map.ofEntries(
24+
entry(KqlQueryBuilder.TIME_ZONE_FIELD.getPreferredName(), KqlQueryBuilder::timeZone),
25+
entry(KqlQueryBuilder.DEFAULT_FIELD_FIELD.getPreferredName(), KqlQueryBuilder::defaultField),
26+
entry(KqlQueryBuilder.CASE_INSENSITIVE_FIELD.getPreferredName(), (qb, s) -> qb.caseInsensitive(Booleans.parseBoolean(s)))
27+
);
28+
29+
private final String query;
30+
31+
private final Map<String, String> options;
32+
33+
// dedicated constructor for QueryTranslator
34+
public KqlQuery(Source source, String query) {
35+
this(source, query, null);
36+
}
37+
38+
public KqlQuery(Source source, String query, Map<String, String> options) {
39+
super(source);
40+
this.query = query;
41+
this.options = options == null ? Collections.emptyMap() : options;
42+
}
43+
44+
@Override
45+
public QueryBuilder asBuilder() {
46+
final KqlQueryBuilder queryBuilder = new KqlQueryBuilder(query);
47+
options.forEach((k, v) -> {
48+
if (BUILDER_APPLIERS.containsKey(k)) {
49+
BUILDER_APPLIERS.get(k).accept(queryBuilder, v);
50+
} else {
51+
throw new IllegalArgumentException("illegal kql query option [" + k + "]");
52+
}
53+
});
54+
return queryBuilder;
55+
}
56+
57+
public String query() {
58+
return query;
59+
}
60+
61+
public Map<String, String> options() {
62+
return options;
63+
}
64+
65+
@Override
66+
public int hashCode() {
67+
return Objects.hash(query, options);
68+
}
69+
70+
@Override
71+
public boolean equals(Object obj) {
72+
if (false == super.equals(obj)) {
73+
return false;
74+
}
75+
76+
KqlQuery other = (KqlQuery) obj;
77+
return Objects.equals(query, other.query) && Objects.equals(options, other.options);
78+
}
79+
80+
@Override
81+
protected String innerToString() {
82+
return query;
83+
}
84+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
package org.elasticsearch.xpack.esql.core.querydsl.query;
8+
9+
import org.elasticsearch.test.ESTestCase;
10+
import org.elasticsearch.xpack.esql.core.expression.predicate.fulltext.StringQueryPredicate;
11+
import org.elasticsearch.xpack.esql.core.tree.Source;
12+
import org.elasticsearch.xpack.esql.core.tree.SourceTests;
13+
import org.elasticsearch.xpack.esql.core.util.StringUtils;
14+
import org.elasticsearch.xpack.kql.query.KqlQueryBuilder;
15+
16+
import java.time.ZoneId;
17+
import java.time.zone.ZoneRulesException;
18+
import java.util.Arrays;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.function.Function;
22+
23+
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
24+
import static org.hamcrest.Matchers.equalTo;
25+
26+
public class KqlQueryTests extends ESTestCase {
27+
static KqlQuery randomMatchQuery() {
28+
return new KqlQuery(SourceTests.randomSource(), randomAlphaOfLength(5));
29+
// TODO add the options
30+
}
31+
32+
public void testEqualsAndHashCode() {
33+
checkEqualsAndHashCode(randomMatchQuery(), KqlQueryTests::copy, KqlQueryTests::mutate);
34+
}
35+
36+
private static KqlQuery copy(KqlQuery query) {
37+
return new KqlQuery(query.source(), query.query(), query.options());
38+
}
39+
40+
private static KqlQuery mutate(KqlQuery query) {
41+
List<Function<KqlQuery, KqlQuery>> options = Arrays.asList(
42+
q -> new KqlQuery(SourceTests.mutate(q.source()), q.query(), q.options()),
43+
q -> new KqlQuery(q.source(), randomValueOtherThan(q.query(), () -> randomAlphaOfLength(5)), q.options())
44+
);
45+
// TODO mutate the options
46+
return randomFrom(options).apply(query);
47+
}
48+
49+
public void testQueryBuilding() {
50+
KqlQueryBuilder qb = getBuilder("case_insensitive=false");
51+
assertThat(qb.caseInsensitive(), equalTo(false));
52+
53+
qb = getBuilder("case_insensitive=false;time_zone=UTC;default_field=foo");
54+
assertThat(qb.caseInsensitive(), equalTo(false));
55+
assertThat(qb.timeZone(), equalTo(ZoneId.of("UTC")));
56+
assertThat(qb.defaultField(), equalTo("foo"));
57+
58+
Exception e = expectThrows(IllegalArgumentException.class, () -> getBuilder("pizza=yummy"));
59+
assertThat(e.getMessage(), equalTo("illegal kql query option [pizza]"));
60+
61+
e = expectThrows(ZoneRulesException.class, () -> getBuilder("time_zone=aoeu"));
62+
assertThat(e.getMessage(), equalTo("Unknown time-zone ID: aoeu"));
63+
}
64+
65+
private static KqlQueryBuilder getBuilder(String options) {
66+
final Source source = new Source(1, 1, StringUtils.EMPTY);
67+
final StringQueryPredicate predicate = new StringQueryPredicate(source, "eggplant", options);
68+
final KqlQuery kqlQuery = new KqlQuery(source, "eggplant", predicate.optionMap());
69+
return (KqlQueryBuilder) kqlQuery.asBuilder();
70+
}
71+
72+
public void testToString() {
73+
final Source source = new Source(1, 1, StringUtils.EMPTY);
74+
final KqlQuery kqlQuery = new KqlQuery(source, "eggplant", Map.of());
75+
assertEquals("KqlQuery@1:2[eggplant]", kqlQuery.toString());
76+
}
77+
}

0 commit comments

Comments
 (0)