From f4e6e68ea93aa779a0be00f15877e0d153a10418 Mon Sep 17 00:00:00 2001
From: Aurelien FOUCRET <aurelien.foucret@elastic.co>
Date: Thu, 24 Oct 2024 18:55:20 +0200
Subject: [PATCH] Improved tests.

---
 x-pack/plugin/kql/src/main/antlr/KqlBase.g4   |   2 +-
 .../xpack/kql/parser/KqlAstBuilder.java       |  14 +-
 .../xpack/kql/parser/KqlBase.interp           |   2 +-
 .../xpack/kql/parser/KqlBaseParser.java       | 273 +++++++++---------
 .../kql/parser/AbstractKqlParserTestCase.java |  44 +++
 .../parser/KqlParserBooleanQueryTests.java    |  16 +-
 .../kql/parser/KqlParserExistsQueryTests.java |  12 +-
 .../kql/parser/KqlParserFieldQueryTests.java  | 137 +++++++++
 .../parser/KqlParserFieldlessQueryTests.java  |  44 ++-
 .../xpack/kql/parser/ParserUtilsTests.java    |  26 +-
 .../src/test/resources/unsupported-queries    |   2 -
 11 files changed, 389 insertions(+), 183 deletions(-)
 create mode 100644 x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserFieldQueryTests.java

diff --git a/x-pack/plugin/kql/src/main/antlr/KqlBase.g4 b/x-pack/plugin/kql/src/main/antlr/KqlBase.g4
index 429ebce9b123b..aa5f399c7049a 100644
--- a/x-pack/plugin/kql/src/main/antlr/KqlBase.g4
+++ b/x-pack/plugin/kql/src/main/antlr/KqlBase.g4
@@ -26,7 +26,7 @@ topLevelQuery
     ;
 
 query
-    : <assoc=right> query operator=(AND | OR) query     #booleanQuery
+    : <assoc=right> query operator=(AND | OR) query?    #booleanQuery
     | NOT subQuery=simpleQuery                          #notQuery
     | simpleQuery                                       #defaultQuery
     ;
diff --git a/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlAstBuilder.java b/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlAstBuilder.java
index 8325c1e9fdd0d..b8413b25655e7 100644
--- a/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlAstBuilder.java
+++ b/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlAstBuilder.java
@@ -65,7 +65,7 @@ public QueryBuilder visitAndBooleanQuery(KqlBaseParser.BooleanQueryContext ctx)
             }
         }
 
-        return builder;
+        return rewriteConjonctionQuery(builder);
     }
 
     public QueryBuilder visitOrBooleanQuery(KqlBaseParser.BooleanQueryContext ctx) {
@@ -79,7 +79,7 @@ public QueryBuilder visitOrBooleanQuery(KqlBaseParser.BooleanQueryContext ctx) {
             }
         }
 
-        return builder;
+        return rewriteDisjunctionQuery(builder);
     }
 
     @Override
@@ -207,6 +207,16 @@ private QueryBuilder rewriteDisjunctionQuery(BoolQueryBuilder boolQueryBuilder)
         return boolQueryBuilder.should().size() == 1 ? boolQueryBuilder.should().getFirst() : boolQueryBuilder;
     }
 
+    private QueryBuilder rewriteConjonctionQuery(BoolQueryBuilder boolQueryBuilder) {
+        assert boolQueryBuilder.should().isEmpty() && boolQueryBuilder.filter().isEmpty() && boolQueryBuilder.mustNot().isEmpty();
+
+        if (boolQueryBuilder.must().isEmpty()) {
+            return new MatchNoneQueryBuilder();
+        }
+
+        return boolQueryBuilder.must().size() == 1 ? boolQueryBuilder.must().getFirst() : boolQueryBuilder;
+    }
+
     private BiFunction<RangeQueryBuilder, String, RangeQueryBuilder> rangeOperation(Token operator) {
         return switch (operator.getType()) {
             case KqlBaseParser.OP_LESS -> RangeQueryBuilder::lt;
diff --git a/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlBase.interp b/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlBase.interp
index eac047d5ffaf1..56fadeca97f21 100644
--- a/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlBase.interp
+++ b/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlBase.interp
@@ -53,4 +53,4 @@ fieldName
 
 
 atn:
-[4, 1, 16, 123, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 1, 0, 3, 0, 28, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 36, 8, 1, 1, 1, 1, 1, 1, 1, 5, 1, 41, 8, 1, 10, 1, 12, 1, 44, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 53, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 3, 4, 63, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 4, 7, 76, 8, 7, 11, 7, 12, 7, 77, 1, 7, 3, 7, 81, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 97, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 104, 8, 10, 1, 11, 4, 11, 107, 8, 11, 11, 11, 12, 11, 108, 1, 11, 3, 11, 112, 8, 11, 1, 12, 4, 12, 115, 8, 12, 11, 12, 12, 12, 116, 1, 12, 1, 12, 3, 12, 121, 8, 12, 1, 12, 0, 1, 2, 13, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 0, 3, 1, 0, 2, 3, 1, 0, 6, 9, 2, 0, 14, 14, 16, 16, 128, 0, 27, 1, 0, 0, 0, 2, 35, 1, 0, 0, 0, 4, 52, 1, 0, 0, 0, 6, 54, 1, 0, 0, 0, 8, 62, 1, 0, 0, 0, 10, 66, 1, 0, 0, 0, 12, 70, 1, 0, 0, 0, 14, 80, 1, 0, 0, 0, 16, 82, 1, 0, 0, 0, 18, 96, 1, 0, 0, 0, 20, 103, 1, 0, 0, 0, 22, 111, 1, 0, 0, 0, 24, 120, 1, 0, 0, 0, 26, 28, 3, 2, 1, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 30, 5, 0, 0, 1, 30, 1, 1, 0, 0, 0, 31, 32, 6, 1, -1, 0, 32, 33, 5, 4, 0, 0, 33, 36, 3, 4, 2, 0, 34, 36, 3, 4, 2, 0, 35, 31, 1, 0, 0, 0, 35, 34, 1, 0, 0, 0, 36, 42, 1, 0, 0, 0, 37, 38, 10, 3, 0, 0, 38, 39, 7, 0, 0, 0, 39, 41, 3, 2, 1, 3, 40, 37, 1, 0, 0, 0, 41, 44, 1, 0, 0, 0, 42, 40, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 3, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 45, 53, 3, 6, 3, 0, 46, 53, 3, 10, 5, 0, 47, 53, 3, 8, 4, 0, 48, 53, 3, 16, 8, 0, 49, 53, 3, 12, 6, 0, 50, 53, 3, 18, 9, 0, 51, 53, 3, 20, 10, 0, 52, 45, 1, 0, 0, 0, 52, 46, 1, 0, 0, 0, 52, 47, 1, 0, 0, 0, 52, 48, 1, 0, 0, 0, 52, 49, 1, 0, 0, 0, 52, 50, 1, 0, 0, 0, 52, 51, 1, 0, 0, 0, 53, 5, 1, 0, 0, 0, 54, 55, 3, 24, 12, 0, 55, 56, 5, 5, 0, 0, 56, 57, 5, 12, 0, 0, 57, 58, 3, 2, 1, 0, 58, 59, 5, 13, 0, 0, 59, 7, 1, 0, 0, 0, 60, 61, 5, 16, 0, 0, 61, 63, 5, 5, 0, 0, 62, 60, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, 64, 65, 5, 16, 0, 0, 65, 9, 1, 0, 0, 0, 66, 67, 5, 10, 0, 0, 67, 68, 3, 2, 1, 0, 68, 69, 5, 11, 0, 0, 69, 11, 1, 0, 0, 0, 70, 71, 3, 24, 12, 0, 71, 72, 7, 1, 0, 0, 72, 73, 3, 14, 7, 0, 73, 13, 1, 0, 0, 0, 74, 76, 7, 2, 0, 0, 75, 74, 1, 0, 0, 0, 76, 77, 1, 0, 0, 0, 77, 75, 1, 0, 0, 0, 77, 78, 1, 0, 0, 0, 78, 81, 1, 0, 0, 0, 79, 81, 5, 15, 0, 0, 80, 75, 1, 0, 0, 0, 80, 79, 1, 0, 0, 0, 81, 15, 1, 0, 0, 0, 82, 83, 3, 24, 12, 0, 83, 84, 5, 5, 0, 0, 84, 85, 5, 16, 0, 0, 85, 17, 1, 0, 0, 0, 86, 87, 3, 24, 12, 0, 87, 88, 5, 5, 0, 0, 88, 89, 3, 22, 11, 0, 89, 97, 1, 0, 0, 0, 90, 91, 3, 24, 12, 0, 91, 92, 5, 5, 0, 0, 92, 93, 5, 10, 0, 0, 93, 94, 3, 22, 11, 0, 94, 95, 5, 11, 0, 0, 95, 97, 1, 0, 0, 0, 96, 86, 1, 0, 0, 0, 96, 90, 1, 0, 0, 0, 97, 19, 1, 0, 0, 0, 98, 104, 3, 22, 11, 0, 99, 100, 5, 10, 0, 0, 100, 101, 3, 22, 11, 0, 101, 102, 5, 11, 0, 0, 102, 104, 1, 0, 0, 0, 103, 98, 1, 0, 0, 0, 103, 99, 1, 0, 0, 0, 104, 21, 1, 0, 0, 0, 105, 107, 7, 2, 0, 0, 106, 105, 1, 0, 0, 0, 107, 108, 1, 0, 0, 0, 108, 106, 1, 0, 0, 0, 108, 109, 1, 0, 0, 0, 109, 112, 1, 0, 0, 0, 110, 112, 5, 15, 0, 0, 111, 106, 1, 0, 0, 0, 111, 110, 1, 0, 0, 0, 112, 23, 1, 0, 0, 0, 113, 115, 5, 14, 0, 0, 114, 113, 1, 0, 0, 0, 115, 116, 1, 0, 0, 0, 116, 114, 1, 0, 0, 0, 116, 117, 1, 0, 0, 0, 117, 121, 1, 0, 0, 0, 118, 121, 5, 15, 0, 0, 119, 121, 5, 16, 0, 0, 120, 114, 1, 0, 0, 0, 120, 118, 1, 0, 0, 0, 120, 119, 1, 0, 0, 0, 121, 25, 1, 0, 0, 0, 13, 27, 35, 42, 52, 62, 77, 80, 96, 103, 108, 111, 116, 120]
\ No newline at end of file
+[4, 1, 16, 125, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 1, 0, 3, 0, 28, 8, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 36, 8, 1, 1, 1, 1, 1, 1, 1, 3, 1, 41, 8, 1, 5, 1, 43, 8, 1, 10, 1, 12, 1, 46, 9, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 2, 55, 8, 2, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 4, 1, 4, 3, 4, 65, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 4, 7, 78, 8, 7, 11, 7, 12, 7, 79, 1, 7, 3, 7, 83, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 99, 8, 9, 1, 10, 1, 10, 1, 10, 1, 10, 1, 10, 3, 10, 106, 8, 10, 1, 11, 4, 11, 109, 8, 11, 11, 11, 12, 11, 110, 1, 11, 3, 11, 114, 8, 11, 1, 12, 4, 12, 117, 8, 12, 11, 12, 12, 12, 118, 1, 12, 1, 12, 3, 12, 123, 8, 12, 1, 12, 0, 1, 2, 13, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 0, 3, 1, 0, 2, 3, 1, 0, 6, 9, 2, 0, 14, 14, 16, 16, 131, 0, 27, 1, 0, 0, 0, 2, 35, 1, 0, 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 68, 1, 0, 0, 0, 12, 72, 1, 0, 0, 0, 14, 82, 1, 0, 0, 0, 16, 84, 1, 0, 0, 0, 18, 98, 1, 0, 0, 0, 20, 105, 1, 0, 0, 0, 22, 113, 1, 0, 0, 0, 24, 122, 1, 0, 0, 0, 26, 28, 3, 2, 1, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 30, 5, 0, 0, 1, 30, 1, 1, 0, 0, 0, 31, 32, 6, 1, -1, 0, 32, 33, 5, 4, 0, 0, 33, 36, 3, 4, 2, 0, 34, 36, 3, 4, 2, 0, 35, 31, 1, 0, 0, 0, 35, 34, 1, 0, 0, 0, 36, 44, 1, 0, 0, 0, 37, 38, 10, 3, 0, 0, 38, 40, 7, 0, 0, 0, 39, 41, 3, 2, 1, 0, 40, 39, 1, 0, 0, 0, 40, 41, 1, 0, 0, 0, 41, 43, 1, 0, 0, 0, 42, 37, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 3, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 55, 3, 6, 3, 0, 48, 55, 3, 10, 5, 0, 49, 55, 3, 8, 4, 0, 50, 55, 3, 16, 8, 0, 51, 55, 3, 12, 6, 0, 52, 55, 3, 18, 9, 0, 53, 55, 3, 20, 10, 0, 54, 47, 1, 0, 0, 0, 54, 48, 1, 0, 0, 0, 54, 49, 1, 0, 0, 0, 54, 50, 1, 0, 0, 0, 54, 51, 1, 0, 0, 0, 54, 52, 1, 0, 0, 0, 54, 53, 1, 0, 0, 0, 55, 5, 1, 0, 0, 0, 56, 57, 3, 24, 12, 0, 57, 58, 5, 5, 0, 0, 58, 59, 5, 12, 0, 0, 59, 60, 3, 2, 1, 0, 60, 61, 5, 13, 0, 0, 61, 7, 1, 0, 0, 0, 62, 63, 5, 16, 0, 0, 63, 65, 5, 5, 0, 0, 64, 62, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 66, 1, 0, 0, 0, 66, 67, 5, 16, 0, 0, 67, 9, 1, 0, 0, 0, 68, 69, 5, 10, 0, 0, 69, 70, 3, 2, 1, 0, 70, 71, 5, 11, 0, 0, 71, 11, 1, 0, 0, 0, 72, 73, 3, 24, 12, 0, 73, 74, 7, 1, 0, 0, 74, 75, 3, 14, 7, 0, 75, 13, 1, 0, 0, 0, 76, 78, 7, 2, 0, 0, 77, 76, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 77, 1, 0, 0, 0, 79, 80, 1, 0, 0, 0, 80, 83, 1, 0, 0, 0, 81, 83, 5, 15, 0, 0, 82, 77, 1, 0, 0, 0, 82, 81, 1, 0, 0, 0, 83, 15, 1, 0, 0, 0, 84, 85, 3, 24, 12, 0, 85, 86, 5, 5, 0, 0, 86, 87, 5, 16, 0, 0, 87, 17, 1, 0, 0, 0, 88, 89, 3, 24, 12, 0, 89, 90, 5, 5, 0, 0, 90, 91, 3, 22, 11, 0, 91, 99, 1, 0, 0, 0, 92, 93, 3, 24, 12, 0, 93, 94, 5, 5, 0, 0, 94, 95, 5, 10, 0, 0, 95, 96, 3, 22, 11, 0, 96, 97, 5, 11, 0, 0, 97, 99, 1, 0, 0, 0, 98, 88, 1, 0, 0, 0, 98, 92, 1, 0, 0, 0, 99, 19, 1, 0, 0, 0, 100, 106, 3, 22, 11, 0, 101, 102, 5, 10, 0, 0, 102, 103, 3, 22, 11, 0, 103, 104, 5, 11, 0, 0, 104, 106, 1, 0, 0, 0, 105, 100, 1, 0, 0, 0, 105, 101, 1, 0, 0, 0, 106, 21, 1, 0, 0, 0, 107, 109, 7, 2, 0, 0, 108, 107, 1, 0, 0, 0, 109, 110, 1, 0, 0, 0, 110, 108, 1, 0, 0, 0, 110, 111, 1, 0, 0, 0, 111, 114, 1, 0, 0, 0, 112, 114, 5, 15, 0, 0, 113, 108, 1, 0, 0, 0, 113, 112, 1, 0, 0, 0, 114, 23, 1, 0, 0, 0, 115, 117, 5, 14, 0, 0, 116, 115, 1, 0, 0, 0, 117, 118, 1, 0, 0, 0, 118, 116, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 123, 1, 0, 0, 0, 120, 123, 5, 15, 0, 0, 121, 123, 5, 16, 0, 0, 122, 116, 1, 0, 0, 0, 122, 120, 1, 0, 0, 0, 122, 121, 1, 0, 0, 0, 123, 25, 1, 0, 0, 0, 14, 27, 35, 40, 44, 54, 64, 79, 82, 98, 105, 110, 113, 118, 122]
\ No newline at end of file
diff --git a/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlBaseParser.java b/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlBaseParser.java
index 8c6e6eb040c14..0e95f438702df 100644
--- a/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlBaseParser.java
+++ b/x-pack/plugin/kql/src/main/java/org/elasticsearch/xpack/kql/parser/KqlBaseParser.java
@@ -292,9 +292,9 @@ private QueryContext query(int _p) throws RecognitionException {
                 throw new NoViableAltException(this);
             }
             _ctx.stop = _input.LT(-1);
-            setState(42);
+            setState(44);
             _errHandler.sync(this);
-            _alt = getInterpreter().adaptivePredict(_input,2,_ctx);
+            _alt = getInterpreter().adaptivePredict(_input,3,_ctx);
             while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER ) {
                 if ( _alt==1 ) {
                     if ( _parseListeners!=null ) triggerExitRuleEvent();
@@ -316,14 +316,22 @@ private QueryContext query(int _p) throws RecognitionException {
                         _errHandler.reportMatch(this);
                         consume();
                     }
-                    setState(39);
-                    query(3);
+                    setState(40);
+                    _errHandler.sync(this);
+                    switch ( getInterpreter().adaptivePredict(_input,2,_ctx) ) {
+                    case 1:
+                        {
+                        setState(39);
+                        query(0);
+                        }
+                        break;
+                    }
                     }
                     } 
                 }
-                setState(44);
+                setState(46);
                 _errHandler.sync(this);
-                _alt = getInterpreter().adaptivePredict(_input,2,_ctx);
+                _alt = getInterpreter().adaptivePredict(_input,3,_ctx);
             }
             }
         }
@@ -384,55 +392,55 @@ public final SimpleQueryContext simpleQuery() throws RecognitionException {
         SimpleQueryContext _localctx = new SimpleQueryContext(_ctx, getState());
         enterRule(_localctx, 4, RULE_simpleQuery);
         try {
-            setState(52);
+            setState(54);
             _errHandler.sync(this);
-            switch ( getInterpreter().adaptivePredict(_input,3,_ctx) ) {
+            switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) {
             case 1:
                 enterOuterAlt(_localctx, 1);
                 {
-                setState(45);
+                setState(47);
                 nestedQuery();
                 }
                 break;
             case 2:
                 enterOuterAlt(_localctx, 2);
                 {
-                setState(46);
+                setState(48);
                 parenthesizedQuery();
                 }
                 break;
             case 3:
                 enterOuterAlt(_localctx, 3);
                 {
-                setState(47);
+                setState(49);
                 matchAllQuery();
                 }
                 break;
             case 4:
                 enterOuterAlt(_localctx, 4);
                 {
-                setState(48);
+                setState(50);
                 existsQuery();
                 }
                 break;
             case 5:
                 enterOuterAlt(_localctx, 5);
                 {
-                setState(49);
+                setState(51);
                 rangeQuery();
                 }
                 break;
             case 6:
                 enterOuterAlt(_localctx, 6);
                 {
-                setState(50);
+                setState(52);
                 fieldQuery();
                 }
                 break;
             case 7:
                 enterOuterAlt(_localctx, 7);
                 {
-                setState(51);
+                setState(53);
                 fieldLessQuery();
                 }
                 break;
@@ -485,15 +493,15 @@ public final NestedQueryContext nestedQuery() throws RecognitionException {
         try {
             enterOuterAlt(_localctx, 1);
             {
-            setState(54);
+            setState(56);
             fieldName();
-            setState(55);
+            setState(57);
             match(COLON);
-            setState(56);
+            setState(58);
             match(LEFT_CURLY_BRACKET);
-            setState(57);
+            setState(59);
             query(0);
-            setState(58);
+            setState(60);
             match(RIGHT_CURLY_BRACKET);
             }
         }
@@ -540,19 +548,19 @@ public final MatchAllQueryContext matchAllQuery() throws RecognitionException {
         try {
             enterOuterAlt(_localctx, 1);
             {
-            setState(62);
+            setState(64);
             _errHandler.sync(this);
-            switch ( getInterpreter().adaptivePredict(_input,4,_ctx) ) {
+            switch ( getInterpreter().adaptivePredict(_input,5,_ctx) ) {
             case 1:
                 {
-                setState(60);
+                setState(62);
                 match(WILDCARD);
-                setState(61);
+                setState(63);
                 match(COLON);
                 }
                 break;
             }
-            setState(64);
+            setState(66);
             match(WILDCARD);
             }
         }
@@ -599,11 +607,11 @@ public final ParenthesizedQueryContext parenthesizedQuery() throws RecognitionEx
         try {
             enterOuterAlt(_localctx, 1);
             {
-            setState(66);
+            setState(68);
             match(LEFT_PARENTHESIS);
-            setState(67);
+            setState(69);
             query(0);
-            setState(68);
+            setState(70);
             match(RIGHT_PARENTHESIS);
             }
         }
@@ -657,9 +665,9 @@ public final RangeQueryContext rangeQuery() throws RecognitionException {
         try {
             enterOuterAlt(_localctx, 1);
             {
-            setState(70);
+            setState(72);
             fieldName();
-            setState(71);
+            setState(73);
             ((RangeQueryContext)_localctx).operator = _input.LT(1);
             _la = _input.LA(1);
             if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 960L) != 0)) ) {
@@ -670,7 +678,7 @@ public final RangeQueryContext rangeQuery() throws RecognitionException {
                 _errHandler.reportMatch(this);
                 consume();
             }
-            setState(72);
+            setState(74);
             rangeQueryValue();
             }
         }
@@ -721,14 +729,14 @@ public final RangeQueryValueContext rangeQueryValue() throws RecognitionExceptio
         int _la;
         try {
             int _alt;
-            setState(80);
+            setState(82);
             _errHandler.sync(this);
             switch (_input.LA(1)) {
             case UNQUOTED_LITERAL:
             case WILDCARD:
                 enterOuterAlt(_localctx, 1);
                 {
-                setState(75); 
+                setState(77); 
                 _errHandler.sync(this);
                 _alt = 1;
                 do {
@@ -736,7 +744,7 @@ public final RangeQueryValueContext rangeQueryValue() throws RecognitionExceptio
                     case 1:
                         {
                         {
-                        setState(74);
+                        setState(76);
                         _la = _input.LA(1);
                         if ( !(_la==UNQUOTED_LITERAL || _la==WILDCARD) ) {
                         _errHandler.recoverInline(this);
@@ -752,16 +760,16 @@ public final RangeQueryValueContext rangeQueryValue() throws RecognitionExceptio
                     default:
                         throw new NoViableAltException(this);
                     }
-                    setState(77); 
+                    setState(79); 
                     _errHandler.sync(this);
-                    _alt = getInterpreter().adaptivePredict(_input,5,_ctx);
+                    _alt = getInterpreter().adaptivePredict(_input,6,_ctx);
                 } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER );
                 }
                 break;
             case QUOTED_STRING:
                 enterOuterAlt(_localctx, 2);
                 {
-                setState(79);
+                setState(81);
                 match(QUOTED_STRING);
                 }
                 break;
@@ -812,11 +820,11 @@ public final ExistsQueryContext existsQuery() throws RecognitionException {
         try {
             enterOuterAlt(_localctx, 1);
             {
-            setState(82);
+            setState(84);
             fieldName();
-            setState(83);
+            setState(85);
             match(COLON);
-            setState(84);
+            setState(86);
             match(WILDCARD);
             }
         }
@@ -865,32 +873,32 @@ public final FieldQueryContext fieldQuery() throws RecognitionException {
         FieldQueryContext _localctx = new FieldQueryContext(_ctx, getState());
         enterRule(_localctx, 18, RULE_fieldQuery);
         try {
-            setState(96);
+            setState(98);
             _errHandler.sync(this);
-            switch ( getInterpreter().adaptivePredict(_input,7,_ctx) ) {
+            switch ( getInterpreter().adaptivePredict(_input,8,_ctx) ) {
             case 1:
                 enterOuterAlt(_localctx, 1);
                 {
-                setState(86);
+                setState(88);
                 fieldName();
-                setState(87);
+                setState(89);
                 match(COLON);
-                setState(88);
+                setState(90);
                 fieldQueryValue();
                 }
                 break;
             case 2:
                 enterOuterAlt(_localctx, 2);
                 {
-                setState(90);
+                setState(92);
                 fieldName();
-                setState(91);
+                setState(93);
                 match(COLON);
-                setState(92);
+                setState(94);
                 match(LEFT_PARENTHESIS);
-                setState(93);
+                setState(95);
                 fieldQueryValue();
-                setState(94);
+                setState(96);
                 match(RIGHT_PARENTHESIS);
                 }
                 break;
@@ -937,7 +945,7 @@ public final FieldLessQueryContext fieldLessQuery() throws RecognitionException
         FieldLessQueryContext _localctx = new FieldLessQueryContext(_ctx, getState());
         enterRule(_localctx, 20, RULE_fieldLessQuery);
         try {
-            setState(103);
+            setState(105);
             _errHandler.sync(this);
             switch (_input.LA(1)) {
             case UNQUOTED_LITERAL:
@@ -945,18 +953,18 @@ public final FieldLessQueryContext fieldLessQuery() throws RecognitionException
             case WILDCARD:
                 enterOuterAlt(_localctx, 1);
                 {
-                setState(98);
+                setState(100);
                 fieldQueryValue();
                 }
                 break;
             case LEFT_PARENTHESIS:
                 enterOuterAlt(_localctx, 2);
                 {
-                setState(99);
+                setState(101);
                 match(LEFT_PARENTHESIS);
-                setState(100);
+                setState(102);
                 fieldQueryValue();
-                setState(101);
+                setState(103);
                 match(RIGHT_PARENTHESIS);
                 }
                 break;
@@ -1011,14 +1019,14 @@ public final FieldQueryValueContext fieldQueryValue() throws RecognitionExceptio
         int _la;
         try {
             int _alt;
-            setState(111);
+            setState(113);
             _errHandler.sync(this);
             switch (_input.LA(1)) {
             case UNQUOTED_LITERAL:
             case WILDCARD:
                 enterOuterAlt(_localctx, 1);
                 {
-                setState(106); 
+                setState(108); 
                 _errHandler.sync(this);
                 _alt = 1;
                 do {
@@ -1026,7 +1034,7 @@ public final FieldQueryValueContext fieldQueryValue() throws RecognitionExceptio
                     case 1:
                         {
                         {
-                        setState(105);
+                        setState(107);
                         _la = _input.LA(1);
                         if ( !(_la==UNQUOTED_LITERAL || _la==WILDCARD) ) {
                         _errHandler.recoverInline(this);
@@ -1042,16 +1050,16 @@ public final FieldQueryValueContext fieldQueryValue() throws RecognitionExceptio
                     default:
                         throw new NoViableAltException(this);
                     }
-                    setState(108); 
+                    setState(110); 
                     _errHandler.sync(this);
-                    _alt = getInterpreter().adaptivePredict(_input,9,_ctx);
+                    _alt = getInterpreter().adaptivePredict(_input,10,_ctx);
                 } while ( _alt!=2 && _alt!=org.antlr.v4.runtime.atn.ATN.INVALID_ALT_NUMBER );
                 }
                 break;
             case QUOTED_STRING:
                 enterOuterAlt(_localctx, 2);
                 {
-                setState(110);
+                setState(112);
                 match(QUOTED_STRING);
                 }
                 break;
@@ -1103,23 +1111,23 @@ public final FieldNameContext fieldName() throws RecognitionException {
         enterRule(_localctx, 24, RULE_fieldName);
         int _la;
         try {
-            setState(120);
+            setState(122);
             _errHandler.sync(this);
             switch (_input.LA(1)) {
             case UNQUOTED_LITERAL:
                 enterOuterAlt(_localctx, 1);
                 {
-                setState(114); 
+                setState(116); 
                 _errHandler.sync(this);
                 _la = _input.LA(1);
                 do {
                     {
                     {
-                    setState(113);
+                    setState(115);
                     ((FieldNameContext)_localctx).value = match(UNQUOTED_LITERAL);
                     }
                     }
-                    setState(116); 
+                    setState(118); 
                     _errHandler.sync(this);
                     _la = _input.LA(1);
                 } while ( _la==UNQUOTED_LITERAL );
@@ -1128,14 +1136,14 @@ public final FieldNameContext fieldName() throws RecognitionException {
             case QUOTED_STRING:
                 enterOuterAlt(_localctx, 2);
                 {
-                setState(118);
+                setState(120);
                 ((FieldNameContext)_localctx).value = match(QUOTED_STRING);
                 }
                 break;
             case WILDCARD:
                 enterOuterAlt(_localctx, 3);
                 {
-                setState(119);
+                setState(121);
                 ((FieldNameContext)_localctx).value = match(WILDCARD);
                 }
                 break;
@@ -1170,76 +1178,77 @@ private boolean query_sempred(QueryContext _localctx, int predIndex) {
     }
 
     public static final String _serializedATN =
-        "\u0004\u0001\u0010{\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+
+        "\u0004\u0001\u0010}\u0002\u0000\u0007\u0000\u0002\u0001\u0007\u0001\u0002"+
         "\u0002\u0007\u0002\u0002\u0003\u0007\u0003\u0002\u0004\u0007\u0004\u0002"+
         "\u0005\u0007\u0005\u0002\u0006\u0007\u0006\u0002\u0007\u0007\u0007\u0002"+
         "\b\u0007\b\u0002\t\u0007\t\u0002\n\u0007\n\u0002\u000b\u0007\u000b\u0002"+
         "\f\u0007\f\u0001\u0000\u0003\u0000\u001c\b\u0000\u0001\u0000\u0001\u0000"+
         "\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0001$\b\u0001"+
-        "\u0001\u0001\u0001\u0001\u0001\u0001\u0005\u0001)\b\u0001\n\u0001\f\u0001"+
-        ",\t\u0001\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002"+
-        "\u0001\u0002\u0001\u0002\u0003\u00025\b\u0002\u0001\u0003\u0001\u0003"+
-        "\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0004\u0001\u0004"+
-        "\u0003\u0004?\b\u0004\u0001\u0004\u0001\u0004\u0001\u0005\u0001\u0005"+
-        "\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006\u0001\u0006\u0001\u0006"+
-        "\u0001\u0007\u0004\u0007L\b\u0007\u000b\u0007\f\u0007M\u0001\u0007\u0003"+
-        "\u0007Q\b\u0007\u0001\b\u0001\b\u0001\b\u0001\b\u0001\t\u0001\t\u0001"+
-        "\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0003\ta\b"+
-        "\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0003\nh\b\n\u0001\u000b\u0004"+
-        "\u000bk\b\u000b\u000b\u000b\f\u000bl\u0001\u000b\u0003\u000bp\b\u000b"+
-        "\u0001\f\u0004\fs\b\f\u000b\f\f\ft\u0001\f\u0001\f\u0003\fy\b\f\u0001"+
-        "\f\u0000\u0001\u0002\r\u0000\u0002\u0004\u0006\b\n\f\u000e\u0010\u0012"+
-        "\u0014\u0016\u0018\u0000\u0003\u0001\u0000\u0002\u0003\u0001\u0000\u0006"+
-        "\t\u0002\u0000\u000e\u000e\u0010\u0010\u0080\u0000\u001b\u0001\u0000\u0000"+
-        "\u0000\u0002#\u0001\u0000\u0000\u0000\u00044\u0001\u0000\u0000\u0000\u0006"+
-        "6\u0001\u0000\u0000\u0000\b>\u0001\u0000\u0000\u0000\nB\u0001\u0000\u0000"+
-        "\u0000\fF\u0001\u0000\u0000\u0000\u000eP\u0001\u0000\u0000\u0000\u0010"+
-        "R\u0001\u0000\u0000\u0000\u0012`\u0001\u0000\u0000\u0000\u0014g\u0001"+
-        "\u0000\u0000\u0000\u0016o\u0001\u0000\u0000\u0000\u0018x\u0001\u0000\u0000"+
-        "\u0000\u001a\u001c\u0003\u0002\u0001\u0000\u001b\u001a\u0001\u0000\u0000"+
-        "\u0000\u001b\u001c\u0001\u0000\u0000\u0000\u001c\u001d\u0001\u0000\u0000"+
-        "\u0000\u001d\u001e\u0005\u0000\u0000\u0001\u001e\u0001\u0001\u0000\u0000"+
-        "\u0000\u001f \u0006\u0001\uffff\uffff\u0000 !\u0005\u0004\u0000\u0000"+
-        "!$\u0003\u0004\u0002\u0000\"$\u0003\u0004\u0002\u0000#\u001f\u0001\u0000"+
-        "\u0000\u0000#\"\u0001\u0000\u0000\u0000$*\u0001\u0000\u0000\u0000%&\n"+
-        "\u0003\u0000\u0000&\'\u0007\u0000\u0000\u0000\')\u0003\u0002\u0001\u0003"+
-        "(%\u0001\u0000\u0000\u0000),\u0001\u0000\u0000\u0000*(\u0001\u0000\u0000"+
-        "\u0000*+\u0001\u0000\u0000\u0000+\u0003\u0001\u0000\u0000\u0000,*\u0001"+
-        "\u0000\u0000\u0000-5\u0003\u0006\u0003\u0000.5\u0003\n\u0005\u0000/5\u0003"+
-        "\b\u0004\u000005\u0003\u0010\b\u000015\u0003\f\u0006\u000025\u0003\u0012"+
-        "\t\u000035\u0003\u0014\n\u00004-\u0001\u0000\u0000\u00004.\u0001\u0000"+
-        "\u0000\u00004/\u0001\u0000\u0000\u000040\u0001\u0000\u0000\u000041\u0001"+
-        "\u0000\u0000\u000042\u0001\u0000\u0000\u000043\u0001\u0000\u0000\u0000"+
-        "5\u0005\u0001\u0000\u0000\u000067\u0003\u0018\f\u000078\u0005\u0005\u0000"+
-        "\u000089\u0005\f\u0000\u00009:\u0003\u0002\u0001\u0000:;\u0005\r\u0000"+
-        "\u0000;\u0007\u0001\u0000\u0000\u0000<=\u0005\u0010\u0000\u0000=?\u0005"+
-        "\u0005\u0000\u0000><\u0001\u0000\u0000\u0000>?\u0001\u0000\u0000\u0000"+
-        "?@\u0001\u0000\u0000\u0000@A\u0005\u0010\u0000\u0000A\t\u0001\u0000\u0000"+
-        "\u0000BC\u0005\n\u0000\u0000CD\u0003\u0002\u0001\u0000DE\u0005\u000b\u0000"+
-        "\u0000E\u000b\u0001\u0000\u0000\u0000FG\u0003\u0018\f\u0000GH\u0007\u0001"+
-        "\u0000\u0000HI\u0003\u000e\u0007\u0000I\r\u0001\u0000\u0000\u0000JL\u0007"+
-        "\u0002\u0000\u0000KJ\u0001\u0000\u0000\u0000LM\u0001\u0000\u0000\u0000"+
-        "MK\u0001\u0000\u0000\u0000MN\u0001\u0000\u0000\u0000NQ\u0001\u0000\u0000"+
-        "\u0000OQ\u0005\u000f\u0000\u0000PK\u0001\u0000\u0000\u0000PO\u0001\u0000"+
-        "\u0000\u0000Q\u000f\u0001\u0000\u0000\u0000RS\u0003\u0018\f\u0000ST\u0005"+
-        "\u0005\u0000\u0000TU\u0005\u0010\u0000\u0000U\u0011\u0001\u0000\u0000"+
-        "\u0000VW\u0003\u0018\f\u0000WX\u0005\u0005\u0000\u0000XY\u0003\u0016\u000b"+
-        "\u0000Ya\u0001\u0000\u0000\u0000Z[\u0003\u0018\f\u0000[\\\u0005\u0005"+
-        "\u0000\u0000\\]\u0005\n\u0000\u0000]^\u0003\u0016\u000b\u0000^_\u0005"+
-        "\u000b\u0000\u0000_a\u0001\u0000\u0000\u0000`V\u0001\u0000\u0000\u0000"+
-        "`Z\u0001\u0000\u0000\u0000a\u0013\u0001\u0000\u0000\u0000bh\u0003\u0016"+
-        "\u000b\u0000cd\u0005\n\u0000\u0000de\u0003\u0016\u000b\u0000ef\u0005\u000b"+
-        "\u0000\u0000fh\u0001\u0000\u0000\u0000gb\u0001\u0000\u0000\u0000gc\u0001"+
-        "\u0000\u0000\u0000h\u0015\u0001\u0000\u0000\u0000ik\u0007\u0002\u0000"+
-        "\u0000ji\u0001\u0000\u0000\u0000kl\u0001\u0000\u0000\u0000lj\u0001\u0000"+
-        "\u0000\u0000lm\u0001\u0000\u0000\u0000mp\u0001\u0000\u0000\u0000np\u0005"+
-        "\u000f\u0000\u0000oj\u0001\u0000\u0000\u0000on\u0001\u0000\u0000\u0000"+
-        "p\u0017\u0001\u0000\u0000\u0000qs\u0005\u000e\u0000\u0000rq\u0001\u0000"+
-        "\u0000\u0000st\u0001\u0000\u0000\u0000tr\u0001\u0000\u0000\u0000tu\u0001"+
-        "\u0000\u0000\u0000uy\u0001\u0000\u0000\u0000vy\u0005\u000f\u0000\u0000"+
-        "wy\u0005\u0010\u0000\u0000xr\u0001\u0000\u0000\u0000xv\u0001\u0000\u0000"+
-        "\u0000xw\u0001\u0000\u0000\u0000y\u0019\u0001\u0000\u0000\u0000\r\u001b"+
-        "#*4>MP`glotx";
+        "\u0001\u0001\u0001\u0001\u0001\u0001\u0003\u0001)\b\u0001\u0005\u0001"+
+        "+\b\u0001\n\u0001\f\u0001.\t\u0001\u0001\u0002\u0001\u0002\u0001\u0002"+
+        "\u0001\u0002\u0001\u0002\u0001\u0002\u0001\u0002\u0003\u00027\b\u0002"+
+        "\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003\u0001\u0003"+
+        "\u0001\u0004\u0001\u0004\u0003\u0004A\b\u0004\u0001\u0004\u0001\u0004"+
+        "\u0001\u0005\u0001\u0005\u0001\u0005\u0001\u0005\u0001\u0006\u0001\u0006"+
+        "\u0001\u0006\u0001\u0006\u0001\u0007\u0004\u0007N\b\u0007\u000b\u0007"+
+        "\f\u0007O\u0001\u0007\u0003\u0007S\b\u0007\u0001\b\u0001\b\u0001\b\u0001"+
+        "\b\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001\t\u0001"+
+        "\t\u0001\t\u0003\tc\b\t\u0001\n\u0001\n\u0001\n\u0001\n\u0001\n\u0003"+
+        "\nj\b\n\u0001\u000b\u0004\u000bm\b\u000b\u000b\u000b\f\u000bn\u0001\u000b"+
+        "\u0003\u000br\b\u000b\u0001\f\u0004\fu\b\f\u000b\f\f\fv\u0001\f\u0001"+
+        "\f\u0003\f{\b\f\u0001\f\u0000\u0001\u0002\r\u0000\u0002\u0004\u0006\b"+
+        "\n\f\u000e\u0010\u0012\u0014\u0016\u0018\u0000\u0003\u0001\u0000\u0002"+
+        "\u0003\u0001\u0000\u0006\t\u0002\u0000\u000e\u000e\u0010\u0010\u0083\u0000"+
+        "\u001b\u0001\u0000\u0000\u0000\u0002#\u0001\u0000\u0000\u0000\u00046\u0001"+
+        "\u0000\u0000\u0000\u00068\u0001\u0000\u0000\u0000\b@\u0001\u0000\u0000"+
+        "\u0000\nD\u0001\u0000\u0000\u0000\fH\u0001\u0000\u0000\u0000\u000eR\u0001"+
+        "\u0000\u0000\u0000\u0010T\u0001\u0000\u0000\u0000\u0012b\u0001\u0000\u0000"+
+        "\u0000\u0014i\u0001\u0000\u0000\u0000\u0016q\u0001\u0000\u0000\u0000\u0018"+
+        "z\u0001\u0000\u0000\u0000\u001a\u001c\u0003\u0002\u0001\u0000\u001b\u001a"+
+        "\u0001\u0000\u0000\u0000\u001b\u001c\u0001\u0000\u0000\u0000\u001c\u001d"+
+        "\u0001\u0000\u0000\u0000\u001d\u001e\u0005\u0000\u0000\u0001\u001e\u0001"+
+        "\u0001\u0000\u0000\u0000\u001f \u0006\u0001\uffff\uffff\u0000 !\u0005"+
+        "\u0004\u0000\u0000!$\u0003\u0004\u0002\u0000\"$\u0003\u0004\u0002\u0000"+
+        "#\u001f\u0001\u0000\u0000\u0000#\"\u0001\u0000\u0000\u0000$,\u0001\u0000"+
+        "\u0000\u0000%&\n\u0003\u0000\u0000&(\u0007\u0000\u0000\u0000\')\u0003"+
+        "\u0002\u0001\u0000(\'\u0001\u0000\u0000\u0000()\u0001\u0000\u0000\u0000"+
+        ")+\u0001\u0000\u0000\u0000*%\u0001\u0000\u0000\u0000+.\u0001\u0000\u0000"+
+        "\u0000,*\u0001\u0000\u0000\u0000,-\u0001\u0000\u0000\u0000-\u0003\u0001"+
+        "\u0000\u0000\u0000.,\u0001\u0000\u0000\u0000/7\u0003\u0006\u0003\u0000"+
+        "07\u0003\n\u0005\u000017\u0003\b\u0004\u000027\u0003\u0010\b\u000037\u0003"+
+        "\f\u0006\u000047\u0003\u0012\t\u000057\u0003\u0014\n\u00006/\u0001\u0000"+
+        "\u0000\u000060\u0001\u0000\u0000\u000061\u0001\u0000\u0000\u000062\u0001"+
+        "\u0000\u0000\u000063\u0001\u0000\u0000\u000064\u0001\u0000\u0000\u0000"+
+        "65\u0001\u0000\u0000\u00007\u0005\u0001\u0000\u0000\u000089\u0003\u0018"+
+        "\f\u00009:\u0005\u0005\u0000\u0000:;\u0005\f\u0000\u0000;<\u0003\u0002"+
+        "\u0001\u0000<=\u0005\r\u0000\u0000=\u0007\u0001\u0000\u0000\u0000>?\u0005"+
+        "\u0010\u0000\u0000?A\u0005\u0005\u0000\u0000@>\u0001\u0000\u0000\u0000"+
+        "@A\u0001\u0000\u0000\u0000AB\u0001\u0000\u0000\u0000BC\u0005\u0010\u0000"+
+        "\u0000C\t\u0001\u0000\u0000\u0000DE\u0005\n\u0000\u0000EF\u0003\u0002"+
+        "\u0001\u0000FG\u0005\u000b\u0000\u0000G\u000b\u0001\u0000\u0000\u0000"+
+        "HI\u0003\u0018\f\u0000IJ\u0007\u0001\u0000\u0000JK\u0003\u000e\u0007\u0000"+
+        "K\r\u0001\u0000\u0000\u0000LN\u0007\u0002\u0000\u0000ML\u0001\u0000\u0000"+
+        "\u0000NO\u0001\u0000\u0000\u0000OM\u0001\u0000\u0000\u0000OP\u0001\u0000"+
+        "\u0000\u0000PS\u0001\u0000\u0000\u0000QS\u0005\u000f\u0000\u0000RM\u0001"+
+        "\u0000\u0000\u0000RQ\u0001\u0000\u0000\u0000S\u000f\u0001\u0000\u0000"+
+        "\u0000TU\u0003\u0018\f\u0000UV\u0005\u0005\u0000\u0000VW\u0005\u0010\u0000"+
+        "\u0000W\u0011\u0001\u0000\u0000\u0000XY\u0003\u0018\f\u0000YZ\u0005\u0005"+
+        "\u0000\u0000Z[\u0003\u0016\u000b\u0000[c\u0001\u0000\u0000\u0000\\]\u0003"+
+        "\u0018\f\u0000]^\u0005\u0005\u0000\u0000^_\u0005\n\u0000\u0000_`\u0003"+
+        "\u0016\u000b\u0000`a\u0005\u000b\u0000\u0000ac\u0001\u0000\u0000\u0000"+
+        "bX\u0001\u0000\u0000\u0000b\\\u0001\u0000\u0000\u0000c\u0013\u0001\u0000"+
+        "\u0000\u0000dj\u0003\u0016\u000b\u0000ef\u0005\n\u0000\u0000fg\u0003\u0016"+
+        "\u000b\u0000gh\u0005\u000b\u0000\u0000hj\u0001\u0000\u0000\u0000id\u0001"+
+        "\u0000\u0000\u0000ie\u0001\u0000\u0000\u0000j\u0015\u0001\u0000\u0000"+
+        "\u0000km\u0007\u0002\u0000\u0000lk\u0001\u0000\u0000\u0000mn\u0001\u0000"+
+        "\u0000\u0000nl\u0001\u0000\u0000\u0000no\u0001\u0000\u0000\u0000or\u0001"+
+        "\u0000\u0000\u0000pr\u0005\u000f\u0000\u0000ql\u0001\u0000\u0000\u0000"+
+        "qp\u0001\u0000\u0000\u0000r\u0017\u0001\u0000\u0000\u0000su\u0005\u000e"+
+        "\u0000\u0000ts\u0001\u0000\u0000\u0000uv\u0001\u0000\u0000\u0000vt\u0001"+
+        "\u0000\u0000\u0000vw\u0001\u0000\u0000\u0000w{\u0001\u0000\u0000\u0000"+
+        "x{\u0005\u000f\u0000\u0000y{\u0005\u0010\u0000\u0000zt\u0001\u0000\u0000"+
+        "\u0000zx\u0001\u0000\u0000\u0000zy\u0001\u0000\u0000\u0000{\u0019\u0001"+
+        "\u0000\u0000\u0000\u000e\u001b#(,6@ORbinqvz";
     public static final ATN _ATN =
         new ATNDeserializer().deserialize(_serializedATN.toCharArray());
     static {
diff --git a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/AbstractKqlParserTestCase.java b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/AbstractKqlParserTestCase.java
index 2395aec7e933f..f31feb9b604bb 100644
--- a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/AbstractKqlParserTestCase.java
+++ b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/AbstractKqlParserTestCase.java
@@ -9,8 +9,13 @@
 
 import org.elasticsearch.core.Predicates;
 import org.elasticsearch.core.SuppressForbidden;
+import org.elasticsearch.index.query.MatchQueryBuilder;
+import org.elasticsearch.index.query.MultiMatchQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryStringQueryBuilder;
+import org.elasticsearch.index.query.RangeQueryBuilder;
 import org.elasticsearch.index.query.SearchExecutionContext;
+import org.elasticsearch.index.query.TermQueryBuilder;
 import org.elasticsearch.test.AbstractBuilderTestCase;
 
 import java.io.BufferedReader;
@@ -24,10 +29,14 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static org.hamcrest.Matchers.anEmptyMap;
+import static org.hamcrest.Matchers.equalTo;
+
 public abstract class AbstractKqlParserTestCase extends AbstractBuilderTestCase {
 
     protected static final String SUPPORTED_QUERY_FILE_PATH = "/supported-queries";
@@ -91,4 +100,39 @@ protected QueryBuilder parseKqlQuery(String kqlQuery) {
 
         return parser.parseKqlQuery(kqlQuery, searchExecutionContext);
     }
+
+    protected static void assertMultiMatchQuery(QueryBuilder query, String expectedValue, MultiMatchQueryBuilder.Type expectedType) {
+        MultiMatchQueryBuilder multiMatchQuery = asInstanceOf(MultiMatchQueryBuilder.class, query);
+        assertThat(multiMatchQuery.fields(), anEmptyMap());
+        assertThat(multiMatchQuery.lenient(), equalTo(true));
+        assertThat(multiMatchQuery.type(), equalTo(expectedType));
+        assertThat(multiMatchQuery.value(), equalTo(expectedValue));
+    }
+
+    protected static void assertQueryStringBuilder(QueryBuilder query, String expectedValue) {
+        QueryStringQueryBuilder queryStringQuery = asInstanceOf(QueryStringQueryBuilder.class, query);
+        assertThat(queryStringQuery.queryString(), equalTo(expectedValue));
+    }
+
+    protected static void assertTermQueryBuilder(QueryBuilder queryBuilder, String expectedFieldName, String expectedValue) {
+        TermQueryBuilder termQuery = asInstanceOf(TermQueryBuilder.class, queryBuilder);
+        assertThat(termQuery.fieldName(), equalTo(expectedFieldName));
+        assertThat(termQuery.value(), equalTo(expectedValue));
+    }
+
+    protected static void assertMatchQueryBuilder(QueryBuilder queryBuilder, String expectedFieldName, String expectedValue) {
+        MatchQueryBuilder matchQuery = asInstanceOf(MatchQueryBuilder.class, queryBuilder);
+        assertThat(matchQuery.fieldName(), equalTo(expectedFieldName));
+        assertThat(matchQuery.value(), equalTo(expectedValue));
+    }
+
+    protected static void assertRangeQueryBuilder(
+        QueryBuilder queryBuilder,
+        String expectedFieldName,
+        Consumer<RangeQueryBuilder> codeBlock
+    ) {
+        RangeQueryBuilder rangeQuery = asInstanceOf(RangeQueryBuilder.class, queryBuilder);
+        assertThat(rangeQuery.fieldName(), equalTo(expectedFieldName));
+        codeBlock.accept(rangeQuery);
+    }
 }
diff --git a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserBooleanQueryTests.java b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserBooleanQueryTests.java
index 0e836495717d8..e25e5b7049961 100644
--- a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserBooleanQueryTests.java
+++ b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserBooleanQueryTests.java
@@ -23,7 +23,7 @@
 
 public class KqlParserBooleanQueryTests extends AbstractKqlParserTestCase {
 
-    public void testNotQuery() throws IOException {
+    public void testParseNotQuery() throws IOException {
         for (String baseQuery : readQueries(SUPPORTED_QUERY_FILE_PATH)) {
             if (baseQuery.startsWith("NOT") || BOOLEAN_QUERY_FILTER.test(baseQuery)) {
                 baseQuery = wrapWithRandomWhitespaces("(") + baseQuery + wrapWithRandomWhitespaces(")");
@@ -46,7 +46,7 @@ public void testNotQuery() throws IOException {
         }
     }
 
-    public void testOrQuery() throws IOException {
+    public void testParseOrQuery() throws IOException {
         List<String> supportedQueries = readQueries(SUPPORTED_QUERY_FILE_PATH, Predicate.not(BOOLEAN_QUERY_FILTER));
 
         for (int runs = 0; runs < 100; runs++) {
@@ -83,7 +83,7 @@ public void testOrQuery() throws IOException {
         }
     }
 
-    public void testAndQuery() throws IOException {
+    public void testParseAndQuery() throws IOException {
         List<String> supportedQueries = readQueries(SUPPORTED_QUERY_FILE_PATH, Predicate.not(BOOLEAN_QUERY_FILTER));
 
         for (int runs = 0; runs < 100; runs++) {
@@ -119,6 +119,16 @@ public void testAndQuery() throws IOException {
         }
     }
 
+    public void testIgnoreTrailingOperatorsDuringParsing() throws IOException {
+        List<String> supportedQueries = readQueries(SUPPORTED_QUERY_FILE_PATH, Predicate.not(BOOLEAN_QUERY_FILTER));
+
+        for (String query : supportedQueries) {
+            // KQL ignore the following trailing operators
+            assertThat(parseKqlQuery(query), equalTo(parseKqlQuery(Strings.format("%s AND", query))));
+            assertThat(parseKqlQuery(query), equalTo(parseKqlQuery(Strings.format("%s OR", query))));
+        }
+    }
+
     public void testOperatorPrecedence() throws IOException {
         KqlParser parser = new KqlParser();
         SearchExecutionContext searchExecutionContext = createSearchExecutionContext();
diff --git a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserExistsQueryTests.java b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserExistsQueryTests.java
index 49c2b58e6370f..78550922593e4 100644
--- a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserExistsQueryTests.java
+++ b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserExistsQueryTests.java
@@ -19,12 +19,12 @@
 
 public class KqlParserExistsQueryTests extends AbstractKqlParserTestCase {
 
-    public void testExistsQueryWithNonExistingField() {
+    public void testParseExistsQueryWithNoMatchingFields() {
         // Using an unquoted literal
-        assertThat(parseKqlQuery(kqlExistsQuery("foo")), isA(MatchNoneQueryBuilder.class));
+        assertThat(parseKqlQuery(kqlExistsQuery("not_a_valid_field")), isA(MatchNoneQueryBuilder.class));
 
         // Using an a quoted string
-        assertThat(parseKqlQuery(kqlExistsQuery("\"foo\"")), isA(MatchNoneQueryBuilder.class));
+        assertThat(parseKqlQuery(kqlExistsQuery("\"not_a_valid_field\"")), isA(MatchNoneQueryBuilder.class));
 
         // Not expanding wildcard with quoted string
         assertThat(parseKqlQuery(kqlExistsQuery("\"mapped_*\"")), isA(MatchNoneQueryBuilder.class));
@@ -33,7 +33,7 @@ public void testExistsQueryWithNonExistingField() {
         assertThat(parseKqlQuery(kqlExistsQuery(OBJECT_FIELD_NAME)), isA(MatchNoneQueryBuilder.class));
     }
 
-    public void testExistsQueryWithASingleField() {
+    public void testParseExistsQueryWithASingleField() {
         for (String fieldName : mappedLeafFields()) {
             ExistsQueryBuilder parsedQuery = asInstanceOf(ExistsQueryBuilder.class, parseKqlQuery(kqlExistsQuery(fieldName)));
             assertThat(parsedQuery.fieldName(), equalTo(fieldName));
@@ -43,7 +43,7 @@ public void testExistsQueryWithASingleField() {
         }
     }
 
-    public void testExistsQueryUsingAWildcard() {
+    public void testParseExistsQueryUsingWildcardFieldName() {
         BoolQueryBuilder parsedQuery = asInstanceOf(BoolQueryBuilder.class, parseKqlQuery(kqlExistsQuery("mapped_*")));
         assertThat(parsedQuery.minimumShouldMatch(), equalTo("1"));
         assertThat(parsedQuery.must(), empty());
@@ -53,7 +53,7 @@ public void testExistsQueryUsingAWildcard() {
         assertThat(parsedQuery.should(), containsInAnyOrder(mappedLeafFields().stream().map(QueryBuilders::existsQuery).toArray()));
     }
 
-    private String kqlExistsQuery(String field) {
+    private static String kqlExistsQuery(String field) {
         return wrapWithRandomWhitespaces(field) + wrapWithRandomWhitespaces(":") + wrapWithRandomWhitespaces("*");
     }
 }
diff --git a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserFieldQueryTests.java b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserFieldQueryTests.java
new file mode 100644
index 0000000000000..6f8f6e07b9722
--- /dev/null
+++ b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserFieldQueryTests.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.kql.parser;
+
+import org.elasticsearch.core.Strings;
+import org.elasticsearch.index.query.MatchNoneQueryBuilder;
+
+import java.util.List;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.isA;
+
+public class KqlParserFieldQueryTests extends AbstractKqlParserTestCase {
+
+    public void testParseFieldQueryWithNoMatchingFields() {
+        // Using an unquoted literal
+        assertThat(parseKqlQuery(kqlFieldQuery("not_a_field", randomIdentifier())), isA(MatchNoneQueryBuilder.class));
+        assertThat(parseKqlQuery(kqlFieldQuery("not_a_field", randomInt())), isA(MatchNoneQueryBuilder.class));
+        assertThat(parseKqlQuery(kqlFieldQuery("not_a_field", quoteString(randomIdentifier()))), isA(MatchNoneQueryBuilder.class));
+
+        // Using a quoted string as field name
+        assertThat(parseKqlQuery(kqlFieldQuery(quoteString("not_a_field"), randomIdentifier())), isA(MatchNoneQueryBuilder.class));
+        assertThat(parseKqlQuery(kqlFieldQuery(quoteString("not_a_field"), randomInt())), isA(MatchNoneQueryBuilder.class));
+        assertThat(
+            parseKqlQuery(kqlFieldQuery(quoteString("not_a_field"), quoteString(randomIdentifier()))),
+            isA(MatchNoneQueryBuilder.class)
+        );
+
+        // Not expanding wildcard with quoted string
+        assertThat(parseKqlQuery(kqlFieldQuery(quoteString("mapped_*"), randomIdentifier())), isA(MatchNoneQueryBuilder.class));
+        assertThat(parseKqlQuery(kqlFieldQuery(quoteString("mapped_*"), randomInt())), isA(MatchNoneQueryBuilder.class));
+        assertThat(
+            parseKqlQuery(kqlFieldQuery(quoteString("mapped_*"), quoteString(randomIdentifier()))),
+            isA(MatchNoneQueryBuilder.class)
+        );
+    }
+
+    public void testParseUnquotedLiteralKeywordFieldQuery() {
+        // Single word
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo")), KEYWORD_FIELD_NAME, "foo");
+
+        // Multiple words
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo bar")), KEYWORD_FIELD_NAME, "foo bar");
+
+        // Escaped keywords
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo \\and bar")), KEYWORD_FIELD_NAME, "foo and bar");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo \\or bar")), KEYWORD_FIELD_NAME, "foo or bar");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "\\not foo bar")), KEYWORD_FIELD_NAME, "not foo bar");
+
+        // Escaped characters
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo \\* bar")), KEYWORD_FIELD_NAME, "foo * bar");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo\\(bar\\)")), KEYWORD_FIELD_NAME, "foo(bar)");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo\\{bar\\}")), KEYWORD_FIELD_NAME, "foo{bar}");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo\\:bar")), KEYWORD_FIELD_NAME, "foo:bar");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo\\<bar")), KEYWORD_FIELD_NAME, "foo<bar");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo\\>bar")), KEYWORD_FIELD_NAME, "foo>bar");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo \\\\ bar")), KEYWORD_FIELD_NAME, "foo \\ bar");
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo\\\"bar\\\"")), KEYWORD_FIELD_NAME, "foo\"bar\"");
+
+        // Wrapping terms into parentheses
+        assertTermQueryBuilder(parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "(foo baz)")), KEYWORD_FIELD_NAME, "foo baz");
+
+        // Check we can use quoted field name as well
+        assertThat(
+            parseKqlQuery(kqlFieldQuery(KEYWORD_FIELD_NAME, "foo")),
+            equalTo(parseKqlQuery(kqlFieldQuery(quoteString(KEYWORD_FIELD_NAME), "foo")))
+        );
+    }
+
+    public void testParseDateFieldQuery() {
+
+        assertRangeQueryBuilder(parseKqlQuery(kqlFieldQuery(DATE_FIELD_NAME, "2010-06-03")), DATE_FIELD_NAME, (rangeQuery) -> {
+            assertThat(rangeQuery.from(), equalTo("2010-06-03"));
+            assertThat(rangeQuery.includeLower(), equalTo(true));
+            assertThat(rangeQuery.to(), equalTo("2010-06-03"));
+            assertThat(rangeQuery.includeUpper(), equalTo(true));
+
+            // Check we can use quoted field name as well
+            assertThat(parseKqlQuery(kqlFieldQuery(quoteString(DATE_FIELD_NAME), "2010-06-03")), equalTo(rangeQuery));
+
+            // Check we can use quoted value as well
+            assertThat(parseKqlQuery(kqlFieldQuery(DATE_FIELD_NAME, quoteString("2010-06-03"))), equalTo(rangeQuery));
+        });
+
+    }
+
+    public void testParseUnquotedLiteralMatchFieldsQuery() {
+        for (String fieldName : List.of(TEXT_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME)) {
+            // Single word
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo")), fieldName, "foo");
+
+            // Numbers are converted to string
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, 1)), fieldName, "1");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, 1.5)), fieldName, "1.5");
+
+            // Multiple words
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo bar")), fieldName, "foo bar");
+
+            // Escaped keywords
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo \\and bar")), fieldName, "foo and bar");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo \\or bar")), fieldName, "foo or bar");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "\\not foo bar")), fieldName, "not foo bar");
+
+            // Escaped characters
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo \\* bar")), fieldName, "foo * bar");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo\\(bar\\)")), fieldName, "foo(bar)");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo\\{bar\\}")), fieldName, "foo{bar}");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo\\:bar")), fieldName, "foo:bar");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo\\<bar")), fieldName, "foo<bar");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo\\>bar")), fieldName, "foo>bar");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo \\\\ bar")), fieldName, "foo \\ bar");
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "foo\\\"bar\\\"")), fieldName, "foo\"bar\"");
+
+            // Wrapping terms into parentheses
+            assertMatchQueryBuilder(parseKqlQuery(kqlFieldQuery(fieldName, "(foo baz)")), fieldName, "foo baz");
+
+            // Check we can use quoted field name as well
+            assertThat(
+                parseKqlQuery(kqlFieldQuery(fieldName, "foo")),
+                equalTo(parseKqlQuery(kqlFieldQuery(quoteString(fieldName), "foo")))
+            );
+        }
+    }
+
+    private static String kqlFieldQuery(String field, Object value) {
+        return wrapWithRandomWhitespaces(field) + wrapWithRandomWhitespaces(":") + wrapWithRandomWhitespaces(value.toString());
+    }
+
+    private static String quoteString(String input) {
+        return Strings.format("\"%s\"", input);
+    }
+}
diff --git a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserFieldlessQueryTests.java b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserFieldlessQueryTests.java
index 2edcd6b395557..9a4a801ba8a86 100644
--- a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserFieldlessQueryTests.java
+++ b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/KqlParserFieldlessQueryTests.java
@@ -8,26 +8,35 @@
 package org.elasticsearch.xpack.kql.parser;
 
 import org.elasticsearch.index.query.MultiMatchQueryBuilder;
-import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.index.query.QueryStringQueryBuilder;
-
-import static org.hamcrest.Matchers.anEmptyMap;
-import static org.hamcrest.Matchers.equalTo;
 
 public class KqlParserFieldlessQueryTests extends AbstractKqlParserTestCase {
 
-    public void testLiteralFieldlessQuery() {
+    public void testParseUnquotedLiteralQuery() {
         // Single word
         assertMultiMatchQuery(parseKqlQuery("foo"), "foo", MultiMatchQueryBuilder.Type.BEST_FIELDS);
         // Multiple words
         assertMultiMatchQuery(parseKqlQuery("foo bar baz"), "foo bar baz", MultiMatchQueryBuilder.Type.BEST_FIELDS);
-        // With an escaped wildcard
-        assertMultiMatchQuery(parseKqlQuery("foo \\* baz"), "foo * baz", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+
+        // Escaped keywords
+        assertMultiMatchQuery(parseKqlQuery("foo \\and bar"), "foo and bar", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("foo \\or bar"), "foo or bar", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("\\not foo bar"), "not foo bar", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+
+        // With an escaped characters
+        assertMultiMatchQuery(parseKqlQuery("foo \\* bar"), "foo * bar", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("foo\\(bar\\)"), "foo(bar)", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("foo\\{bar\\}"), "foo{bar}", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("foo\\:bar"), "foo:bar", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("foo\\<bar"), "foo<bar", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("foo\\>bar"), "foo>bar", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("foo \\\\ bar"), "foo \\ bar", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("foo\\\"bar\\\""), "foo\"bar\"", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+
         // Wrapping terms into parentheses
-        assertMultiMatchQuery(parseKqlQuery("foo baz"), "foo baz", MultiMatchQueryBuilder.Type.BEST_FIELDS);
+        assertMultiMatchQuery(parseKqlQuery("(foo baz)"), "foo baz", MultiMatchQueryBuilder.Type.BEST_FIELDS);
     }
 
-    public void testWildcardFieldlessQuery() {
+    public void testParseWildcardQuery() {
         // Single word
         assertQueryStringBuilder(parseKqlQuery("foo*"), "foo*");
         assertQueryStringBuilder(parseKqlQuery("*foo"), "*foo");
@@ -44,7 +53,7 @@ public void testWildcardFieldlessQuery() {
         assertQueryStringBuilder(parseKqlQuery("+foo* -bar"), "\\+foo* \\-bar");
     }
 
-    public void testMatchPhraseQuery() {
+    public void testParseQuotedStringQuery() {
         // Single word
         assertMultiMatchQuery(parseKqlQuery("\"foo\""), "foo", MultiMatchQueryBuilder.Type.PHRASE);
         // Multiple words
@@ -54,17 +63,4 @@ public void testMatchPhraseQuery() {
         // Containing unescaped KQL reserved characters
         assertMultiMatchQuery(parseKqlQuery("\"foo*: {(<bar>})\""), "foo*: {(<bar>})", MultiMatchQueryBuilder.Type.PHRASE);
     }
-
-    private void assertMultiMatchQuery(QueryBuilder query, String expectedValue, MultiMatchQueryBuilder.Type expectedType) {
-        MultiMatchQueryBuilder multiMatchQuery = asInstanceOf(MultiMatchQueryBuilder.class, query);
-        assertThat(multiMatchQuery.fields(), anEmptyMap());
-        assertThat(multiMatchQuery.lenient(), equalTo(true));
-        assertThat(multiMatchQuery.type(), equalTo(expectedType));
-        assertThat(multiMatchQuery.value(), equalTo(expectedValue));
-    }
-
-    private void assertQueryStringBuilder(QueryBuilder query, String expectedValue) {
-        QueryStringQueryBuilder queryStringQuery = asInstanceOf(QueryStringQueryBuilder.class, query);
-        assertThat(queryStringQuery.queryString(), equalTo(expectedValue));
-    }
 }
diff --git a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/ParserUtilsTests.java b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/ParserUtilsTests.java
index ddcd09c40f530..1d374b11a790b 100644
--- a/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/ParserUtilsTests.java
+++ b/x-pack/plugin/kql/src/test/java/org/elasticsearch/xpack/kql/parser/ParserUtilsTests.java
@@ -141,7 +141,7 @@ public void testHasWildcard() {
         assertTrue(hasWildcard(parserRuleContext(randomTextNodeListWithNode(wildcardNode()))));
 
         // All children are literals
-        assertFalse(hasWildcard(parserRuleContext(randomList(1, randomInt(100), this::randomLiteralNode))));
+        assertFalse(hasWildcard(parserRuleContext(randomList(1, randomInt(100), ParserUtilsTests::randomLiteralNode))));
 
         // Quoted string
         assertFalse(hasWildcard(parserRuleContext(randomQuotedStringNode())));
@@ -186,30 +186,32 @@ public void testEscapeQueryString() {
         assertThat(escapeQueryString("foo*", false), equalTo("foo\\*"));
     }
 
-    private ParserRuleContext parserRuleContext(ParseTree child) {
+    private static ParserRuleContext parserRuleContext(ParseTree child) {
         return parserRuleContext(List.of(child));
     }
 
-    private ParserRuleContext parserRuleContext(List<ParseTree> children) {
+    private static ParserRuleContext parserRuleContext(List<ParseTree> children) {
         ParserRuleContext ctx = new ParserRuleContext(null, randomInt());
         ctx.children = children;
         return ctx;
     }
 
-    private TerminalNode terminalNode(int type, String text) {
+    private static TerminalNode terminalNode(int type, String text) {
         Token symbol = mock(Token.class);
         when(symbol.getType()).thenReturn(type);
         when(symbol.getText()).thenReturn(text);
         return new TerminalNodeImpl(symbol);
     }
 
-    private List<ParseTree> randomTextNodeListWithNode(TerminalNode node) {
-        List<ParseTree> nodes = new ArrayList<>(Stream.concat(Stream.generate(this::randomTextNode).limit(100), Stream.of(node)).toList());
+    private static List<ParseTree> randomTextNodeListWithNode(TerminalNode node) {
+        List<ParseTree> nodes = new ArrayList<>(
+            Stream.concat(Stream.generate(ParserUtilsTests::randomTextNode).limit(100), Stream.of(node)).toList()
+        );
         Collections.shuffle(nodes, random());
         return nodes;
     }
 
-    private TerminalNode randomTextNode() {
+    private static TerminalNode randomTextNode() {
         return switch (randomInt() % 3) {
             case 0 -> wildcardNode();
             case 1 -> randomQuotedStringNode();
@@ -217,23 +219,23 @@ private TerminalNode randomTextNode() {
         };
     }
 
-    private TerminalNode quotedStringNode(String quotedStringText) {
+    private static TerminalNode quotedStringNode(String quotedStringText) {
         return terminalNode(QUOTED_STRING, "\"" + quotedStringText + "\"");
     }
 
-    private TerminalNode randomQuotedStringNode() {
+    private static TerminalNode randomQuotedStringNode() {
         return quotedStringNode(randomIdentifier());
     }
 
-    private TerminalNode literalNode(String literalText) {
+    private static TerminalNode literalNode(String literalText) {
         return terminalNode(UNQUOTED_LITERAL, literalText);
     }
 
-    private TerminalNode randomLiteralNode() {
+    private static TerminalNode randomLiteralNode() {
         return terminalNode(UNQUOTED_LITERAL, randomIdentifier());
     }
 
-    private TerminalNode wildcardNode() {
+    private static TerminalNode wildcardNode() {
         return terminalNode(WILDCARD, "*");
     }
 }
diff --git a/x-pack/plugin/kql/src/test/resources/unsupported-queries b/x-pack/plugin/kql/src/test/resources/unsupported-queries
index 9c9876db79d2b..0b377ce8d5d82 100644
--- a/x-pack/plugin/kql/src/test/resources/unsupported-queries
+++ b/x-pack/plugin/kql/src/test/resources/unsupported-queries
@@ -21,9 +21,7 @@ foo_field: foo bar"
 
 
 // Invalid boolean queries
-foo AND
 AND foo
-foo OR
 OR foo
 NOT foo: