Skip to content

Commit 696a650

Browse files
committed
Adding tests.
1 parent f2e11ae commit 696a650

File tree

2 files changed

+210
-13
lines changed

2 files changed

+210
-13
lines changed

x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlAstBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public QueryBuilder visitNestedQuery(KqlBaseParser.NestedQueryContext ctx) {
124124
return subQuery;
125125
}
126126

127-
return QueryBuilders.nestedQuery(nestedFieldName, subQuery, ScoreMode.None);
127+
return wrapWithNestedQuery(nestedFieldName, QueryBuilders.nestedQuery(nestedFieldName, subQuery, ScoreMode.None));
128128
}
129129

130130
@Override

x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlNestedFieldQueryTests.java

Lines changed: 209 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77

88
package org.elasticsearch.xpack.kql.parser;
99

10+
import org.elasticsearch.index.query.BoolQueryBuilder;
11+
import org.elasticsearch.index.query.MatchQueryBuilder;
1012
import org.elasticsearch.index.query.NestedQueryBuilder;
13+
import org.elasticsearch.index.query.QueryBuilder;
14+
import org.elasticsearch.index.query.RangeQueryBuilder;
15+
import org.elasticsearch.index.query.TermQueryBuilder;
1116
import org.elasticsearch.test.ESTestCase;
1217
import org.hamcrest.Matchers;
1318

@@ -16,7 +21,9 @@
1621
import java.util.stream.Stream;
1722

1823
import static org.elasticsearch.common.Strings.format;
24+
import static org.hamcrest.Matchers.empty;
1925
import static org.hamcrest.Matchers.equalTo;
26+
import static org.hamcrest.Matchers.hasSize;
2027

2128
public class KqlNestedFieldQueryTests extends AbstractKqlParserTestCase {
2229
public void testInvalidNestedFieldName() {
@@ -35,9 +42,7 @@ public void testInlineNestedFieldMatchTextQuery() {
3542
{
3643
// Querying a nested text subfield.
3744
String nestedFieldName = format("%s.%s", NESTED_FIELD_NAME, fieldName);
38-
String searchTerms = Stream.generate(ESTestCase::randomIdentifier)
39-
.limit(randomIntBetween(1, 10))
40-
.collect(Collectors.joining(" "));
45+
String searchTerms = randomSearchTerms();
4146
String kqlQueryString = format("%s: %s", nestedFieldName, searchTerms);
4247

4348
NestedQueryBuilder nestedQuery = asInstanceOf(NestedQueryBuilder.class, parseKqlQuery(kqlQueryString));
@@ -49,9 +54,7 @@ public void testInlineNestedFieldMatchTextQuery() {
4954
{
5055
// Several levels of nested fields.
5156
String nestedFieldName = format("%s.%s.%s", NESTED_FIELD_NAME, NESTED_FIELD_NAME, fieldName);
52-
String searchTerms = Stream.generate(ESTestCase::randomIdentifier)
53-
.limit(randomIntBetween(1, 10))
54-
.collect(Collectors.joining(" "));
57+
String searchTerms = randomSearchTerms();
5558
String kqlQueryString = format("%s: %s", nestedFieldName, searchTerms);
5659

5760
NestedQueryBuilder nestedQuery = asInstanceOf(NestedQueryBuilder.class, parseKqlQuery(kqlQueryString));
@@ -69,9 +72,7 @@ public void testInlineNestedFieldMatchKeywordFieldQuery() {
6972
{
7073
// Querying a nested text subfield.
7174
String nestedFieldName = format("%s.%s", NESTED_FIELD_NAME, KEYWORD_FIELD_NAME);
72-
String searchTerms = Stream.generate(ESTestCase::randomIdentifier)
73-
.limit(randomIntBetween(1, 10))
74-
.collect(Collectors.joining(" "));
75+
String searchTerms = randomSearchTerms();
7576
String kqlQueryString = format("%s: %s", nestedFieldName, searchTerms);
7677

7778
NestedQueryBuilder nestedQuery = asInstanceOf(NestedQueryBuilder.class, parseKqlQuery(kqlQueryString));
@@ -83,9 +84,7 @@ public void testInlineNestedFieldMatchKeywordFieldQuery() {
8384
{
8485
// Several levels of nested fields.
8586
String nestedFieldName = format("%s.%s.%s", NESTED_FIELD_NAME, NESTED_FIELD_NAME, KEYWORD_FIELD_NAME);
86-
String searchTerms = Stream.generate(ESTestCase::randomIdentifier)
87-
.limit(randomIntBetween(1, 10))
88-
.collect(Collectors.joining(" "));
87+
String searchTerms = randomSearchTerms();
8988
String kqlQueryString = format("%s: %s", nestedFieldName, searchTerms);
9089

9190
NestedQueryBuilder nestedQuery = asInstanceOf(NestedQueryBuilder.class, parseKqlQuery(kqlQueryString));
@@ -97,4 +96,202 @@ public void testInlineNestedFieldMatchKeywordFieldQuery() {
9796
assertTermQueryBuilder(nestedSubQuery.query(), nestedFieldName, searchTerms);
9897
}
9998
}
99+
100+
public void testInlineNestedFieldRangeQuery() {
101+
{
102+
// Querying a nested text subfield.
103+
String nestedFieldName = format("%s.%s", NESTED_FIELD_NAME, INT_FIELD_NAME);
104+
String operator = randomFrom(">", ">=", "<", "<=");
105+
String kqlQueryString = format("%s %s %s", nestedFieldName, operator, randomDouble());
106+
107+
NestedQueryBuilder nestedQuery = asInstanceOf(NestedQueryBuilder.class, parseKqlQuery(kqlQueryString));
108+
109+
assertThat(nestedQuery.path(), equalTo(NESTED_FIELD_NAME));
110+
assertRangeQueryBuilder(nestedQuery.query(), nestedFieldName, rangeQueryBuilder -> {});
111+
}
112+
113+
{
114+
// Several levels of nested fields.
115+
String nestedFieldName = format("%s.%s.%s", NESTED_FIELD_NAME, NESTED_FIELD_NAME, INT_FIELD_NAME);
116+
String operator = randomFrom(">", ">=", "<", "<=");
117+
String kqlQueryString = format("%s %s %s", nestedFieldName, operator, randomDouble());
118+
119+
NestedQueryBuilder nestedQuery = asInstanceOf(NestedQueryBuilder.class, parseKqlQuery(kqlQueryString));
120+
assertThat(nestedQuery.path(), equalTo(NESTED_FIELD_NAME));
121+
122+
NestedQueryBuilder nestedSubQuery = asInstanceOf(NestedQueryBuilder.class, nestedQuery.query());
123+
assertThat(nestedSubQuery.path(), equalTo(format("%s.%s", NESTED_FIELD_NAME, NESTED_FIELD_NAME)));
124+
125+
assertRangeQueryBuilder(nestedSubQuery.query(), nestedFieldName, rangeQueryBuilder -> {});
126+
}
127+
}
128+
129+
public void testNestedQuerySyntax() {
130+
// Single word - Keyword & text field
131+
List.of(KEYWORD_FIELD_NAME, TEXT_FIELD_NAME)
132+
.forEach(
133+
fieldName -> assertThat(
134+
parseKqlQuery(format("%s : { %s : %s }", NESTED_FIELD_NAME, fieldName, "foo")),
135+
equalTo(parseKqlQuery(format("%s.%s : %s", NESTED_FIELD_NAME, fieldName, "foo")))
136+
)
137+
);
138+
139+
// Multiple words - Keyword & text field
140+
List.of(KEYWORD_FIELD_NAME, TEXT_FIELD_NAME)
141+
.forEach(
142+
fieldName -> assertThat(
143+
parseKqlQuery(format("%s : { %s : %s }", NESTED_FIELD_NAME, fieldName, "foo bar")),
144+
equalTo(parseKqlQuery(format("%s.%s : %s", NESTED_FIELD_NAME, fieldName, "foo bar")))
145+
)
146+
);
147+
148+
// Range syntax
149+
{
150+
String operator = randomFrom("<", "<=", ">", ">=");
151+
double rangeValue = randomDouble();
152+
assertThat(
153+
parseKqlQuery(format("%s : { %s %s %s }", NESTED_FIELD_NAME, INT_FIELD_NAME, operator, rangeValue)),
154+
equalTo(parseKqlQuery(format("%s.%s %s %s", NESTED_FIELD_NAME, INT_FIELD_NAME, operator, rangeValue)))
155+
);
156+
}
157+
158+
// Several level of nesting
159+
{
160+
QueryBuilder inlineQuery = parseKqlQuery(
161+
format("%s.%s.%s : %s", NESTED_FIELD_NAME, NESTED_FIELD_NAME, TEXT_FIELD_NAME, "foo bar")
162+
);
163+
164+
assertThat(
165+
parseKqlQuery(format("%s : { %s : { %s : %s } }", NESTED_FIELD_NAME, NESTED_FIELD_NAME, TEXT_FIELD_NAME, "foo bar")),
166+
equalTo(inlineQuery)
167+
);
168+
169+
assertThat(
170+
parseKqlQuery(format("%s.%s : { %s : %s }", NESTED_FIELD_NAME, NESTED_FIELD_NAME, TEXT_FIELD_NAME, "foo bar")),
171+
equalTo(inlineQuery)
172+
);
173+
174+
assertThat(
175+
parseKqlQuery(format("%s : { %s.%s : %s }", NESTED_FIELD_NAME, NESTED_FIELD_NAME, TEXT_FIELD_NAME, "foo bar")),
176+
equalTo(inlineQuery)
177+
);
178+
}
179+
}
180+
181+
public void testBooleanAndNestedQuerySyntax() {
182+
NestedQueryBuilder nestedQuery = asInstanceOf(
183+
NestedQueryBuilder.class,
184+
parseKqlQuery(
185+
format("%s: { %s : foo AND %s: bar AND %s > 3}", NESTED_FIELD_NAME, TEXT_FIELD_NAME, KEYWORD_FIELD_NAME, INT_FIELD_NAME)
186+
)
187+
);
188+
assertThat(nestedQuery.path(), equalTo(NESTED_FIELD_NAME));
189+
190+
BoolQueryBuilder subQuery = asInstanceOf(BoolQueryBuilder.class, nestedQuery.query());
191+
assertThat(subQuery.should(), empty());
192+
assertThat(subQuery.filter(), empty());
193+
assertThat(subQuery.mustNot(), empty());
194+
assertThat(subQuery.must(), hasSize(3));
195+
assertMatchQueryBuilder(
196+
subQuery.must().stream().filter(q -> q instanceof MatchQueryBuilder).findFirst().get(),
197+
format("%s.%s", NESTED_FIELD_NAME, TEXT_FIELD_NAME),
198+
"foo"
199+
);
200+
assertTermQueryBuilder(
201+
subQuery.must().stream().filter(q -> q instanceof TermQueryBuilder).findFirst().get(),
202+
format("%s.%s", NESTED_FIELD_NAME, KEYWORD_FIELD_NAME),
203+
"bar"
204+
);
205+
assertRangeQueryBuilder(
206+
subQuery.must().stream().filter(q -> q instanceof RangeQueryBuilder).findAny().get(),
207+
format("%s.%s", NESTED_FIELD_NAME, INT_FIELD_NAME),
208+
q -> {}
209+
);
210+
}
211+
212+
public void testBooleanOrNestedQuerySyntax() {
213+
NestedQueryBuilder nestedQuery = asInstanceOf(
214+
NestedQueryBuilder.class,
215+
parseKqlQuery(
216+
format("%s: { %s : foo OR %s: bar OR %s > 3 }", NESTED_FIELD_NAME, TEXT_FIELD_NAME, KEYWORD_FIELD_NAME, INT_FIELD_NAME)
217+
)
218+
);
219+
220+
assertThat(nestedQuery.path(), equalTo(NESTED_FIELD_NAME));
221+
222+
BoolQueryBuilder subQuery = asInstanceOf(BoolQueryBuilder.class, nestedQuery.query());
223+
assertThat(subQuery.must(), empty());
224+
assertThat(subQuery.filter(), empty());
225+
assertThat(subQuery.mustNot(), empty());
226+
assertThat(subQuery.should(), hasSize(3));
227+
assertMatchQueryBuilder(
228+
subQuery.should().stream().filter(q -> q instanceof MatchQueryBuilder).findFirst().get(),
229+
format("%s.%s", NESTED_FIELD_NAME, TEXT_FIELD_NAME),
230+
"foo"
231+
);
232+
assertTermQueryBuilder(
233+
subQuery.should().stream().filter(q -> q instanceof TermQueryBuilder).findFirst().get(),
234+
format("%s.%s", NESTED_FIELD_NAME, KEYWORD_FIELD_NAME),
235+
"bar"
236+
);
237+
assertRangeQueryBuilder(
238+
subQuery.should().stream().filter(q -> q instanceof RangeQueryBuilder).findAny().get(),
239+
format("%s.%s", NESTED_FIELD_NAME, INT_FIELD_NAME),
240+
q -> {}
241+
);
242+
}
243+
244+
public void testBooleanNotNestedQuerySyntax() {
245+
{
246+
NestedQueryBuilder nestedQuery = asInstanceOf(
247+
NestedQueryBuilder.class,
248+
parseKqlQuery(format("%s: { NOT %s : foo }", NESTED_FIELD_NAME, TEXT_FIELD_NAME))
249+
);
250+
251+
assertThat(nestedQuery.path(), equalTo(NESTED_FIELD_NAME));
252+
253+
BoolQueryBuilder subQuery = asInstanceOf(BoolQueryBuilder.class, nestedQuery.query());
254+
assertThat(subQuery.must(), empty());
255+
assertThat(subQuery.filter(), empty());
256+
assertThat(subQuery.should(), empty());
257+
assertThat(subQuery.mustNot(), hasSize(1));
258+
assertMatchQueryBuilder(subQuery.mustNot().get(0), format("%s.%s", NESTED_FIELD_NAME, TEXT_FIELD_NAME), "foo");
259+
}
260+
261+
{
262+
NestedQueryBuilder nestedQuery = asInstanceOf(
263+
NestedQueryBuilder.class,
264+
parseKqlQuery(format("%s: { NOT %s : foo }", NESTED_FIELD_NAME, KEYWORD_FIELD_NAME))
265+
);
266+
267+
assertThat(nestedQuery.path(), equalTo(NESTED_FIELD_NAME));
268+
269+
BoolQueryBuilder subQuery = asInstanceOf(BoolQueryBuilder.class, nestedQuery.query());
270+
assertThat(subQuery.must(), empty());
271+
assertThat(subQuery.filter(), empty());
272+
assertThat(subQuery.should(), empty());
273+
assertThat(subQuery.mustNot(), hasSize(1));
274+
assertTermQueryBuilder(subQuery.mustNot().get(0), format("%s.%s", NESTED_FIELD_NAME, KEYWORD_FIELD_NAME), "foo");
275+
}
276+
277+
{
278+
NestedQueryBuilder nestedQuery = asInstanceOf(
279+
NestedQueryBuilder.class,
280+
parseKqlQuery(format("%s: { NOT %s < 3 }", NESTED_FIELD_NAME, INT_FIELD_NAME))
281+
);
282+
283+
assertThat(nestedQuery.path(), equalTo(NESTED_FIELD_NAME));
284+
285+
BoolQueryBuilder subQuery = asInstanceOf(BoolQueryBuilder.class, nestedQuery.query());
286+
assertThat(subQuery.must(), empty());
287+
assertThat(subQuery.filter(), empty());
288+
assertThat(subQuery.should(), empty());
289+
assertThat(subQuery.mustNot(), hasSize(1));
290+
assertRangeQueryBuilder(subQuery.mustNot().get(0), format("%s.%s", NESTED_FIELD_NAME, INT_FIELD_NAME), q -> {});
291+
}
292+
}
293+
294+
private static String randomSearchTerms() {
295+
return Stream.generate(ESTestCase::randomIdentifier).limit(randomIntBetween(1, 10)).collect(Collectors.joining(" "));
296+
}
100297
}

0 commit comments

Comments
 (0)