|
25 | 25 | import java.util.Set; |
26 | 26 | import java.util.function.BiConsumer; |
27 | 27 | import java.util.function.BiFunction; |
| 28 | +import java.util.function.Consumer; |
28 | 29 |
|
29 | 30 | import static org.elasticsearch.common.logging.LoggerMessageFormat.format; |
30 | 31 | import static org.elasticsearch.xpack.kql.parser.KqlParsingContext.isDateField; |
@@ -207,38 +208,76 @@ public QueryBuilder visitFieldLessQuery(KqlBaseParser.FieldLessQueryContext ctx) |
207 | 208 |
|
208 | 209 | @Override |
209 | 210 | public QueryBuilder visitFieldQuery(KqlBaseParser.FieldQueryContext ctx) { |
| 211 | + return parseFieldQuery(ctx.fieldName(), ctx.fieldQueryValue()); |
| 212 | + } |
210 | 213 |
|
211 | | - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery().minimumShouldMatch(1); |
212 | | - String queryText = extractText(ctx.fieldQueryValue()); |
213 | | - boolean hasWildcard = hasWildcard(ctx.fieldQueryValue()); |
| 214 | + public QueryBuilder parseBooleanFieldQuery( |
| 215 | + KqlBaseParser.FieldNameContext fieldNameCtx, |
| 216 | + KqlBaseParser.BooleanFieldQueryValueContext booleanFieldQueryValueCtx |
| 217 | + ) { |
| 218 | + if (booleanFieldQueryValueCtx.operator != null) { |
| 219 | + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); |
| 220 | + |
| 221 | + Token operator = booleanFieldQueryValueCtx.operator; |
| 222 | + Consumer<QueryBuilder> boolClauseConsumer = operator.getType() == KqlBaseParser.AND |
| 223 | + ? boolQueryBuilder::must |
| 224 | + : boolQueryBuilder::should; |
| 225 | + boolClauseConsumer.accept(parseBooleanFieldQuery(fieldNameCtx, booleanFieldQueryValueCtx.booleanFieldQueryValue())); |
| 226 | + boolClauseConsumer.accept(parseFieldQuery(fieldNameCtx, booleanFieldQueryValueCtx.fieldQueryValue())); |
| 227 | + |
| 228 | + return operator.getType() == KqlBaseParser.AND |
| 229 | + ? rewriteConjunctionQuery(boolQueryBuilder) |
| 230 | + : rewriteDisjunctionQuery(boolQueryBuilder); |
| 231 | + } else if (booleanFieldQueryValueCtx.booleanFieldQueryValue() != null) { |
| 232 | + return parseBooleanFieldQuery(fieldNameCtx, booleanFieldQueryValueCtx.booleanFieldQueryValue()); |
| 233 | + } else { |
| 234 | + assert booleanFieldQueryValueCtx.fieldQueryValue() != null; |
| 235 | + return parseFieldQuery(fieldNameCtx, booleanFieldQueryValueCtx.fieldQueryValue()); |
| 236 | + } |
| 237 | + } |
214 | 238 |
|
215 | | - withFields(ctx.fieldName(), (fieldName, mappedFieldType) -> { |
216 | | - QueryBuilder fieldQuery = null; |
217 | | - |
218 | | - if (hasWildcard && isKeywordField(mappedFieldType)) { |
219 | | - fieldQuery = QueryBuilders.wildcardQuery(fieldName, queryText).caseInsensitive(kqlParsingContext.caseInsensitive()); |
220 | | - } else if (hasWildcard) { |
221 | | - fieldQuery = QueryBuilders.queryStringQuery(escapeLuceneQueryString(queryText, true)).field(fieldName); |
222 | | - } else if (isDateField(mappedFieldType)) { |
223 | | - RangeQueryBuilder rangeFieldQuery = QueryBuilders.rangeQuery(fieldName).gte(queryText).lte(queryText); |
224 | | - if (kqlParsingContext.timeZone() != null) { |
225 | | - rangeFieldQuery.timeZone(kqlParsingContext.timeZone().getId()); |
| 239 | + public QueryBuilder parseFieldQuery( |
| 240 | + KqlBaseParser.FieldNameContext fieldNameCtx, |
| 241 | + KqlBaseParser.FieldQueryValueContext fieldQueryValueCtx |
| 242 | + ) { |
| 243 | + if (fieldQueryValueCtx.operator != null) { |
| 244 | + assert fieldQueryValueCtx.fieldQueryValue() != null; |
| 245 | + return QueryBuilders.boolQuery().mustNot(parseFieldQuery(fieldNameCtx, fieldQueryValueCtx.fieldQueryValue())); |
| 246 | + } else if (fieldQueryValueCtx.booleanFieldQueryValue() != null) { |
| 247 | + return parseBooleanFieldQuery(fieldNameCtx, fieldQueryValueCtx.booleanFieldQueryValue()); |
| 248 | + } else { |
| 249 | + BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); |
| 250 | + String queryText = extractText(fieldQueryValueCtx); |
| 251 | + boolean hasWildcard = hasWildcard(fieldQueryValueCtx); |
| 252 | + |
| 253 | + withFields(fieldNameCtx, (fieldName, mappedFieldType) -> { |
| 254 | + QueryBuilder fieldQuery; |
| 255 | + |
| 256 | + if (hasWildcard && isKeywordField(mappedFieldType)) { |
| 257 | + fieldQuery = QueryBuilders.wildcardQuery(fieldName, queryText).caseInsensitive(kqlParsingContext.caseInsensitive()); |
| 258 | + } else if (hasWildcard) { |
| 259 | + fieldQuery = QueryBuilders.queryStringQuery(escapeLuceneQueryString(queryText, true)).field(fieldName); |
| 260 | + } else if (isDateField(mappedFieldType)) { |
| 261 | + RangeQueryBuilder rangeFieldQuery = QueryBuilders.rangeQuery(fieldName).gte(queryText).lte(queryText); |
| 262 | + if (kqlParsingContext.timeZone() != null) { |
| 263 | + rangeFieldQuery.timeZone(kqlParsingContext.timeZone().getId()); |
| 264 | + } |
| 265 | + fieldQuery = rangeFieldQuery; |
| 266 | + } else if (isKeywordField(mappedFieldType)) { |
| 267 | + fieldQuery = QueryBuilders.termQuery(fieldName, queryText).caseInsensitive(kqlParsingContext.caseInsensitive()); |
| 268 | + } else if (fieldQueryValueCtx.QUOTED_STRING() != null) { |
| 269 | + fieldQuery = QueryBuilders.matchPhraseQuery(fieldName, queryText); |
| 270 | + } else { |
| 271 | + fieldQuery = QueryBuilders.matchQuery(fieldName, queryText); |
226 | 272 | } |
227 | | - fieldQuery = rangeFieldQuery; |
228 | | - } else if (isKeywordField(mappedFieldType)) { |
229 | | - fieldQuery = QueryBuilders.termQuery(fieldName, queryText).caseInsensitive(kqlParsingContext.caseInsensitive()); |
230 | | - } else if (ctx.fieldQueryValue().QUOTED_STRING() != null) { |
231 | | - fieldQuery = QueryBuilders.matchPhraseQuery(fieldName, queryText); |
232 | | - } else { |
233 | | - fieldQuery = QueryBuilders.matchQuery(fieldName, queryText); |
234 | | - } |
235 | 273 |
|
236 | | - if (fieldQuery != null) { |
237 | | - boolQueryBuilder.should(wrapWithNestedQuery(fieldName, fieldQuery)); |
238 | | - } |
239 | | - }); |
| 274 | + if (fieldQuery != null) { |
| 275 | + boolQueryBuilder.should(wrapWithNestedQuery(fieldName, fieldQuery)); |
| 276 | + } |
| 277 | + }); |
240 | 278 |
|
241 | | - return rewriteDisjunctionQuery(boolQueryBuilder); |
| 279 | + return rewriteDisjunctionQuery(boolQueryBuilder); |
| 280 | + } |
242 | 281 | } |
243 | 282 |
|
244 | 283 | private static boolean isAndQuery(ParserRuleContext ctx) { |
@@ -269,9 +308,7 @@ private void withFields(KqlBaseParser.FieldNameContext ctx, BiConsumer<String, M |
269 | 308 | return; |
270 | 309 | } |
271 | 310 |
|
272 | | - if (ctx.value.getType() == KqlBaseParser.QUOTED_STRING) { |
273 | | - assert fieldNames.size() < 2 : "expecting only one matching field"; |
274 | | - } |
| 311 | + assert ctx.value.getType() != KqlBaseParser.QUOTED_STRING || fieldNames.size() < 2 : "expecting only one matching field"; |
275 | 312 |
|
276 | 313 | fieldNames.forEach(fieldName -> { |
277 | 314 | MappedFieldType fieldType = kqlParsingContext.fieldType(fieldName); |
|
0 commit comments