diff --git a/grammar/FilterQuery.g4 b/grammar/FilterQuery.g4
index a87e399232..507689754a 100644
--- a/grammar/FilterQuery.g4
+++ b/grammar/FilterQuery.g4
@@ -39,6 +39,7 @@ primary
| functionCall
| fullText
| key
+ | value
;
/*
@@ -189,9 +190,13 @@ BOOL
| [Ff][Aa][Ll][Ss][Ee]
;
-// Numbers (integer or float). Adjust as needed for your domain.
+fragment SIGN : [+-] ;
+
+// Numbers: optional sign, then digits, optional fractional part,
+// optional scientific notation (handy for future use)
NUMBER
- : DIGIT+ ( '.' DIGIT+ )?
+ : SIGN? DIGIT+ ('.' DIGIT*)? ([eE] SIGN? DIGIT+)? // -10.25 42 +3.14 6.02e23
+ | SIGN? '.' DIGIT+ ([eE] SIGN? DIGIT+)? // -.75 .5 -.5e-3
;
// Double/single-quoted text, capturing full text search strings, values, etc.
@@ -201,10 +206,12 @@ QUOTED_TEXT
)
;
-// Keys can have letters, digits, underscores, dots, and bracket pairs
-// e.g. service.name, service.namespace, db.queries[].query_duration
+fragment SEGMENT : [a-zA-Z] [a-zA-Z0-9_:\-]* ;
+fragment EMPTY_BRACKS : '[' ']' ;
+fragment OLD_JSON_BRACKS: '[' '*' ']';
+
KEY
- : [a-zA-Z0-9_] [a-zA-Z0-9_.*[\]]*
+ : SEGMENT ( '.' SEGMENT | EMPTY_BRACKS | OLD_JSON_BRACKS)*
;
// Ignore whitespace
diff --git a/pkg/parser/grammar/FilterQuery.interp b/pkg/parser/grammar/FilterQuery.interp
index 154786c76c..b78a05c21a 100644
--- a/pkg/parser/grammar/FilterQuery.interp
+++ b/pkg/parser/grammar/FilterQuery.interp
@@ -91,4 +91,4 @@ key
atn:
-[4, 1, 33, 212, 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, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 5, 2, 43, 8, 2, 10, 2, 12, 2, 46, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 52, 8, 3, 10, 3, 12, 3, 55, 9, 3, 1, 4, 3, 4, 58, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 70, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 148, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 160, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 174, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 179, 8, 9, 10, 9, 12, 9, 182, 9, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 5, 12, 194, 8, 12, 10, 12, 12, 12, 197, 9, 12, 1, 13, 1, 13, 1, 13, 3, 13, 202, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 0, 6, 1, 0, 7, 8, 2, 0, 13, 13, 15, 15, 2, 0, 14, 14, 16, 16, 2, 0, 30, 30, 33, 33, 1, 0, 25, 27, 1, 0, 28, 31, 225, 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 47, 1, 0, 0, 0, 8, 57, 1, 0, 0, 0, 10, 69, 1, 0, 0, 0, 12, 147, 1, 0, 0, 0, 14, 159, 1, 0, 0, 0, 16, 173, 1, 0, 0, 0, 18, 175, 1, 0, 0, 0, 20, 183, 1, 0, 0, 0, 22, 185, 1, 0, 0, 0, 24, 190, 1, 0, 0, 0, 26, 201, 1, 0, 0, 0, 28, 203, 1, 0, 0, 0, 30, 207, 1, 0, 0, 0, 32, 209, 1, 0, 0, 0, 34, 35, 3, 2, 1, 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 38, 3, 4, 2, 0, 38, 3, 1, 0, 0, 0, 39, 44, 3, 6, 3, 0, 40, 41, 5, 24, 0, 0, 41, 43, 3, 6, 3, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 5, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 53, 3, 8, 4, 0, 48, 49, 5, 23, 0, 0, 49, 52, 3, 8, 4, 0, 50, 52, 3, 8, 4, 0, 51, 48, 1, 0, 0, 0, 51, 50, 1, 0, 0, 0, 52, 55, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, 7, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 56, 58, 5, 22, 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, 63, 64, 5, 2, 0, 0, 64, 70, 1, 0, 0, 0, 65, 70, 3, 12, 6, 0, 66, 70, 3, 22, 11, 0, 67, 70, 3, 20, 10, 0, 68, 70, 3, 32, 16, 0, 69, 61, 1, 0, 0, 0, 69, 65, 1, 0, 0, 0, 69, 66, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 69, 68, 1, 0, 0, 0, 70, 11, 1, 0, 0, 0, 71, 72, 3, 32, 16, 0, 72, 73, 5, 6, 0, 0, 73, 74, 3, 30, 15, 0, 74, 148, 1, 0, 0, 0, 75, 76, 3, 32, 16, 0, 76, 77, 7, 0, 0, 0, 77, 78, 3, 30, 15, 0, 78, 148, 1, 0, 0, 0, 79, 80, 3, 32, 16, 0, 80, 81, 5, 9, 0, 0, 81, 82, 3, 30, 15, 0, 82, 148, 1, 0, 0, 0, 83, 84, 3, 32, 16, 0, 84, 85, 5, 10, 0, 0, 85, 86, 3, 30, 15, 0, 86, 148, 1, 0, 0, 0, 87, 88, 3, 32, 16, 0, 88, 89, 5, 11, 0, 0, 89, 90, 3, 30, 15, 0, 90, 148, 1, 0, 0, 0, 91, 92, 3, 32, 16, 0, 92, 93, 5, 12, 0, 0, 93, 94, 3, 30, 15, 0, 94, 148, 1, 0, 0, 0, 95, 96, 3, 32, 16, 0, 96, 97, 7, 1, 0, 0, 97, 98, 3, 30, 15, 0, 98, 148, 1, 0, 0, 0, 99, 100, 3, 32, 16, 0, 100, 101, 7, 2, 0, 0, 101, 102, 3, 30, 15, 0, 102, 148, 1, 0, 0, 0, 103, 104, 3, 32, 16, 0, 104, 105, 5, 17, 0, 0, 105, 106, 3, 30, 15, 0, 106, 107, 5, 23, 0, 0, 107, 108, 3, 30, 15, 0, 108, 148, 1, 0, 0, 0, 109, 110, 3, 32, 16, 0, 110, 111, 5, 22, 0, 0, 111, 112, 5, 17, 0, 0, 112, 113, 3, 30, 15, 0, 113, 114, 5, 23, 0, 0, 114, 115, 3, 30, 15, 0, 115, 148, 1, 0, 0, 0, 116, 117, 3, 32, 16, 0, 117, 118, 3, 14, 7, 0, 118, 148, 1, 0, 0, 0, 119, 120, 3, 32, 16, 0, 120, 121, 3, 16, 8, 0, 121, 148, 1, 0, 0, 0, 122, 123, 3, 32, 16, 0, 123, 124, 5, 18, 0, 0, 124, 148, 1, 0, 0, 0, 125, 126, 3, 32, 16, 0, 126, 127, 5, 22, 0, 0, 127, 128, 5, 18, 0, 0, 128, 148, 1, 0, 0, 0, 129, 130, 3, 32, 16, 0, 130, 131, 5, 19, 0, 0, 131, 132, 3, 30, 15, 0, 132, 148, 1, 0, 0, 0, 133, 134, 3, 32, 16, 0, 134, 135, 5, 22, 0, 0, 135, 136, 5, 19, 0, 0, 136, 137, 3, 30, 15, 0, 137, 148, 1, 0, 0, 0, 138, 139, 3, 32, 16, 0, 139, 140, 5, 20, 0, 0, 140, 141, 3, 30, 15, 0, 141, 148, 1, 0, 0, 0, 142, 143, 3, 32, 16, 0, 143, 144, 5, 22, 0, 0, 144, 145, 5, 20, 0, 0, 145, 146, 3, 30, 15, 0, 146, 148, 1, 0, 0, 0, 147, 71, 1, 0, 0, 0, 147, 75, 1, 0, 0, 0, 147, 79, 1, 0, 0, 0, 147, 83, 1, 0, 0, 0, 147, 87, 1, 0, 0, 0, 147, 91, 1, 0, 0, 0, 147, 95, 1, 0, 0, 0, 147, 99, 1, 0, 0, 0, 147, 103, 1, 0, 0, 0, 147, 109, 1, 0, 0, 0, 147, 116, 1, 0, 0, 0, 147, 119, 1, 0, 0, 0, 147, 122, 1, 0, 0, 0, 147, 125, 1, 0, 0, 0, 147, 129, 1, 0, 0, 0, 147, 133, 1, 0, 0, 0, 147, 138, 1, 0, 0, 0, 147, 142, 1, 0, 0, 0, 148, 13, 1, 0, 0, 0, 149, 150, 5, 21, 0, 0, 150, 151, 5, 1, 0, 0, 151, 152, 3, 18, 9, 0, 152, 153, 5, 2, 0, 0, 153, 160, 1, 0, 0, 0, 154, 155, 5, 21, 0, 0, 155, 156, 5, 3, 0, 0, 156, 157, 3, 18, 9, 0, 157, 158, 5, 4, 0, 0, 158, 160, 1, 0, 0, 0, 159, 149, 1, 0, 0, 0, 159, 154, 1, 0, 0, 0, 160, 15, 1, 0, 0, 0, 161, 162, 5, 22, 0, 0, 162, 163, 5, 21, 0, 0, 163, 164, 5, 1, 0, 0, 164, 165, 3, 18, 9, 0, 165, 166, 5, 2, 0, 0, 166, 174, 1, 0, 0, 0, 167, 168, 5, 22, 0, 0, 168, 169, 5, 21, 0, 0, 169, 170, 5, 3, 0, 0, 170, 171, 3, 18, 9, 0, 171, 172, 5, 4, 0, 0, 172, 174, 1, 0, 0, 0, 173, 161, 1, 0, 0, 0, 173, 167, 1, 0, 0, 0, 174, 17, 1, 0, 0, 0, 175, 180, 3, 30, 15, 0, 176, 177, 5, 5, 0, 0, 177, 179, 3, 30, 15, 0, 178, 176, 1, 0, 0, 0, 179, 182, 1, 0, 0, 0, 180, 178, 1, 0, 0, 0, 180, 181, 1, 0, 0, 0, 181, 19, 1, 0, 0, 0, 182, 180, 1, 0, 0, 0, 183, 184, 7, 3, 0, 0, 184, 21, 1, 0, 0, 0, 185, 186, 7, 4, 0, 0, 186, 187, 5, 1, 0, 0, 187, 188, 3, 24, 12, 0, 188, 189, 5, 2, 0, 0, 189, 23, 1, 0, 0, 0, 190, 195, 3, 26, 13, 0, 191, 192, 5, 5, 0, 0, 192, 194, 3, 26, 13, 0, 193, 191, 1, 0, 0, 0, 194, 197, 1, 0, 0, 0, 195, 193, 1, 0, 0, 0, 195, 196, 1, 0, 0, 0, 196, 25, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0, 198, 202, 3, 32, 16, 0, 199, 202, 3, 30, 15, 0, 200, 202, 3, 28, 14, 0, 201, 198, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 201, 200, 1, 0, 0, 0, 202, 27, 1, 0, 0, 0, 203, 204, 5, 3, 0, 0, 204, 205, 3, 18, 9, 0, 205, 206, 5, 4, 0, 0, 206, 29, 1, 0, 0, 0, 207, 208, 7, 5, 0, 0, 208, 31, 1, 0, 0, 0, 209, 210, 5, 31, 0, 0, 210, 33, 1, 0, 0, 0, 11, 44, 51, 53, 57, 69, 147, 159, 173, 180, 195, 201]
\ No newline at end of file
+[4, 1, 33, 213, 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, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 5, 2, 43, 8, 2, 10, 2, 12, 2, 46, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 52, 8, 3, 10, 3, 12, 3, 55, 9, 3, 1, 4, 3, 4, 58, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 71, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3, 6, 149, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 3, 7, 161, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 175, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 180, 8, 9, 10, 9, 12, 9, 183, 9, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 5, 12, 195, 8, 12, 10, 12, 12, 12, 198, 9, 12, 1, 13, 1, 13, 1, 13, 3, 13, 203, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 0, 6, 1, 0, 7, 8, 2, 0, 13, 13, 15, 15, 2, 0, 14, 14, 16, 16, 2, 0, 30, 30, 33, 33, 1, 0, 25, 27, 1, 0, 28, 31, 227, 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 47, 1, 0, 0, 0, 8, 57, 1, 0, 0, 0, 10, 70, 1, 0, 0, 0, 12, 148, 1, 0, 0, 0, 14, 160, 1, 0, 0, 0, 16, 174, 1, 0, 0, 0, 18, 176, 1, 0, 0, 0, 20, 184, 1, 0, 0, 0, 22, 186, 1, 0, 0, 0, 24, 191, 1, 0, 0, 0, 26, 202, 1, 0, 0, 0, 28, 204, 1, 0, 0, 0, 30, 208, 1, 0, 0, 0, 32, 210, 1, 0, 0, 0, 34, 35, 3, 2, 1, 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 38, 3, 4, 2, 0, 38, 3, 1, 0, 0, 0, 39, 44, 3, 6, 3, 0, 40, 41, 5, 24, 0, 0, 41, 43, 3, 6, 3, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 5, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 53, 3, 8, 4, 0, 48, 49, 5, 23, 0, 0, 49, 52, 3, 8, 4, 0, 50, 52, 3, 8, 4, 0, 51, 48, 1, 0, 0, 0, 51, 50, 1, 0, 0, 0, 52, 55, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, 7, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 56, 58, 5, 22, 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, 63, 64, 5, 2, 0, 0, 64, 71, 1, 0, 0, 0, 65, 71, 3, 12, 6, 0, 66, 71, 3, 22, 11, 0, 67, 71, 3, 20, 10, 0, 68, 71, 3, 32, 16, 0, 69, 71, 3, 30, 15, 0, 70, 61, 1, 0, 0, 0, 70, 65, 1, 0, 0, 0, 70, 66, 1, 0, 0, 0, 70, 67, 1, 0, 0, 0, 70, 68, 1, 0, 0, 0, 70, 69, 1, 0, 0, 0, 71, 11, 1, 0, 0, 0, 72, 73, 3, 32, 16, 0, 73, 74, 5, 6, 0, 0, 74, 75, 3, 30, 15, 0, 75, 149, 1, 0, 0, 0, 76, 77, 3, 32, 16, 0, 77, 78, 7, 0, 0, 0, 78, 79, 3, 30, 15, 0, 79, 149, 1, 0, 0, 0, 80, 81, 3, 32, 16, 0, 81, 82, 5, 9, 0, 0, 82, 83, 3, 30, 15, 0, 83, 149, 1, 0, 0, 0, 84, 85, 3, 32, 16, 0, 85, 86, 5, 10, 0, 0, 86, 87, 3, 30, 15, 0, 87, 149, 1, 0, 0, 0, 88, 89, 3, 32, 16, 0, 89, 90, 5, 11, 0, 0, 90, 91, 3, 30, 15, 0, 91, 149, 1, 0, 0, 0, 92, 93, 3, 32, 16, 0, 93, 94, 5, 12, 0, 0, 94, 95, 3, 30, 15, 0, 95, 149, 1, 0, 0, 0, 96, 97, 3, 32, 16, 0, 97, 98, 7, 1, 0, 0, 98, 99, 3, 30, 15, 0, 99, 149, 1, 0, 0, 0, 100, 101, 3, 32, 16, 0, 101, 102, 7, 2, 0, 0, 102, 103, 3, 30, 15, 0, 103, 149, 1, 0, 0, 0, 104, 105, 3, 32, 16, 0, 105, 106, 5, 17, 0, 0, 106, 107, 3, 30, 15, 0, 107, 108, 5, 23, 0, 0, 108, 109, 3, 30, 15, 0, 109, 149, 1, 0, 0, 0, 110, 111, 3, 32, 16, 0, 111, 112, 5, 22, 0, 0, 112, 113, 5, 17, 0, 0, 113, 114, 3, 30, 15, 0, 114, 115, 5, 23, 0, 0, 115, 116, 3, 30, 15, 0, 116, 149, 1, 0, 0, 0, 117, 118, 3, 32, 16, 0, 118, 119, 3, 14, 7, 0, 119, 149, 1, 0, 0, 0, 120, 121, 3, 32, 16, 0, 121, 122, 3, 16, 8, 0, 122, 149, 1, 0, 0, 0, 123, 124, 3, 32, 16, 0, 124, 125, 5, 18, 0, 0, 125, 149, 1, 0, 0, 0, 126, 127, 3, 32, 16, 0, 127, 128, 5, 22, 0, 0, 128, 129, 5, 18, 0, 0, 129, 149, 1, 0, 0, 0, 130, 131, 3, 32, 16, 0, 131, 132, 5, 19, 0, 0, 132, 133, 3, 30, 15, 0, 133, 149, 1, 0, 0, 0, 134, 135, 3, 32, 16, 0, 135, 136, 5, 22, 0, 0, 136, 137, 5, 19, 0, 0, 137, 138, 3, 30, 15, 0, 138, 149, 1, 0, 0, 0, 139, 140, 3, 32, 16, 0, 140, 141, 5, 20, 0, 0, 141, 142, 3, 30, 15, 0, 142, 149, 1, 0, 0, 0, 143, 144, 3, 32, 16, 0, 144, 145, 5, 22, 0, 0, 145, 146, 5, 20, 0, 0, 146, 147, 3, 30, 15, 0, 147, 149, 1, 0, 0, 0, 148, 72, 1, 0, 0, 0, 148, 76, 1, 0, 0, 0, 148, 80, 1, 0, 0, 0, 148, 84, 1, 0, 0, 0, 148, 88, 1, 0, 0, 0, 148, 92, 1, 0, 0, 0, 148, 96, 1, 0, 0, 0, 148, 100, 1, 0, 0, 0, 148, 104, 1, 0, 0, 0, 148, 110, 1, 0, 0, 0, 148, 117, 1, 0, 0, 0, 148, 120, 1, 0, 0, 0, 148, 123, 1, 0, 0, 0, 148, 126, 1, 0, 0, 0, 148, 130, 1, 0, 0, 0, 148, 134, 1, 0, 0, 0, 148, 139, 1, 0, 0, 0, 148, 143, 1, 0, 0, 0, 149, 13, 1, 0, 0, 0, 150, 151, 5, 21, 0, 0, 151, 152, 5, 1, 0, 0, 152, 153, 3, 18, 9, 0, 153, 154, 5, 2, 0, 0, 154, 161, 1, 0, 0, 0, 155, 156, 5, 21, 0, 0, 156, 157, 5, 3, 0, 0, 157, 158, 3, 18, 9, 0, 158, 159, 5, 4, 0, 0, 159, 161, 1, 0, 0, 0, 160, 150, 1, 0, 0, 0, 160, 155, 1, 0, 0, 0, 161, 15, 1, 0, 0, 0, 162, 163, 5, 22, 0, 0, 163, 164, 5, 21, 0, 0, 164, 165, 5, 1, 0, 0, 165, 166, 3, 18, 9, 0, 166, 167, 5, 2, 0, 0, 167, 175, 1, 0, 0, 0, 168, 169, 5, 22, 0, 0, 169, 170, 5, 21, 0, 0, 170, 171, 5, 3, 0, 0, 171, 172, 3, 18, 9, 0, 172, 173, 5, 4, 0, 0, 173, 175, 1, 0, 0, 0, 174, 162, 1, 0, 0, 0, 174, 168, 1, 0, 0, 0, 175, 17, 1, 0, 0, 0, 176, 181, 3, 30, 15, 0, 177, 178, 5, 5, 0, 0, 178, 180, 3, 30, 15, 0, 179, 177, 1, 0, 0, 0, 180, 183, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 19, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 184, 185, 7, 3, 0, 0, 185, 21, 1, 0, 0, 0, 186, 187, 7, 4, 0, 0, 187, 188, 5, 1, 0, 0, 188, 189, 3, 24, 12, 0, 189, 190, 5, 2, 0, 0, 190, 23, 1, 0, 0, 0, 191, 196, 3, 26, 13, 0, 192, 193, 5, 5, 0, 0, 193, 195, 3, 26, 13, 0, 194, 192, 1, 0, 0, 0, 195, 198, 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 25, 1, 0, 0, 0, 198, 196, 1, 0, 0, 0, 199, 203, 3, 32, 16, 0, 200, 203, 3, 30, 15, 0, 201, 203, 3, 28, 14, 0, 202, 199, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0, 202, 201, 1, 0, 0, 0, 203, 27, 1, 0, 0, 0, 204, 205, 5, 3, 0, 0, 205, 206, 3, 18, 9, 0, 206, 207, 5, 4, 0, 0, 207, 29, 1, 0, 0, 0, 208, 209, 7, 5, 0, 0, 209, 31, 1, 0, 0, 0, 210, 211, 5, 31, 0, 0, 211, 33, 1, 0, 0, 0, 11, 44, 51, 53, 57, 70, 148, 160, 174, 181, 196, 202]
\ No newline at end of file
diff --git a/pkg/parser/grammar/FilterQueryLexer.interp b/pkg/parser/grammar/FilterQueryLexer.interp
index 5a08c7b188..888c0b75ba 100644
--- a/pkg/parser/grammar/FilterQueryLexer.interp
+++ b/pkg/parser/grammar/FilterQueryLexer.interp
@@ -99,8 +99,12 @@ HAS
HASANY
HASALL
BOOL
+SIGN
NUMBER
QUOTED_TEXT
+SEGMENT
+EMPTY_BRACKS
+OLD_JSON_BRACKS
KEY
WS
DIGIT
@@ -114,4 +118,4 @@ mode names:
DEFAULT_MODE
atn:
-[4, 0, 33, 270, 6, -1, 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, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 83, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 4, 13, 110, 8, 13, 11, 13, 12, 13, 111, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 4, 15, 129, 8, 15, 11, 15, 12, 15, 130, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 153, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 170, 8, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 213, 8, 27, 1, 28, 4, 28, 216, 8, 28, 11, 28, 12, 28, 217, 1, 28, 1, 28, 4, 28, 222, 8, 28, 11, 28, 12, 28, 223, 3, 28, 226, 8, 28, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 232, 8, 29, 10, 29, 12, 29, 235, 9, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 242, 8, 29, 10, 29, 12, 29, 245, 9, 29, 1, 29, 3, 29, 248, 8, 29, 1, 30, 1, 30, 5, 30, 252, 8, 30, 10, 30, 12, 30, 255, 9, 30, 1, 31, 4, 31, 258, 8, 31, 11, 31, 12, 31, 259, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 4, 33, 267, 8, 33, 11, 33, 12, 33, 268, 0, 0, 34, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 0, 67, 33, 1, 0, 29, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, 75, 75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 84, 84, 116, 116, 2, 0, 9, 9, 32, 32, 2, 0, 66, 66, 98, 98, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71, 71, 103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, 104, 104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, 102, 102, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 7, 0, 42, 42, 46, 46, 48, 57, 65, 91, 93, 93, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 8, 0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 285, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 1, 69, 1, 0, 0, 0, 3, 71, 1, 0, 0, 0, 5, 73, 1, 0, 0, 0, 7, 75, 1, 0, 0, 0, 9, 77, 1, 0, 0, 0, 11, 82, 1, 0, 0, 0, 13, 84, 1, 0, 0, 0, 15, 87, 1, 0, 0, 0, 17, 90, 1, 0, 0, 0, 19, 92, 1, 0, 0, 0, 21, 95, 1, 0, 0, 0, 23, 97, 1, 0, 0, 0, 25, 100, 1, 0, 0, 0, 27, 105, 1, 0, 0, 0, 29, 118, 1, 0, 0, 0, 31, 124, 1, 0, 0, 0, 33, 138, 1, 0, 0, 0, 35, 146, 1, 0, 0, 0, 37, 154, 1, 0, 0, 0, 39, 161, 1, 0, 0, 0, 41, 171, 1, 0, 0, 0, 43, 174, 1, 0, 0, 0, 45, 178, 1, 0, 0, 0, 47, 182, 1, 0, 0, 0, 49, 185, 1, 0, 0, 0, 51, 189, 1, 0, 0, 0, 53, 196, 1, 0, 0, 0, 55, 212, 1, 0, 0, 0, 57, 215, 1, 0, 0, 0, 59, 247, 1, 0, 0, 0, 61, 249, 1, 0, 0, 0, 63, 257, 1, 0, 0, 0, 65, 263, 1, 0, 0, 0, 67, 266, 1, 0, 0, 0, 69, 70, 5, 40, 0, 0, 70, 2, 1, 0, 0, 0, 71, 72, 5, 41, 0, 0, 72, 4, 1, 0, 0, 0, 73, 74, 5, 91, 0, 0, 74, 6, 1, 0, 0, 0, 75, 76, 5, 93, 0, 0, 76, 8, 1, 0, 0, 0, 77, 78, 5, 44, 0, 0, 78, 10, 1, 0, 0, 0, 79, 83, 5, 61, 0, 0, 80, 81, 5, 61, 0, 0, 81, 83, 5, 61, 0, 0, 82, 79, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 83, 12, 1, 0, 0, 0, 84, 85, 5, 33, 0, 0, 85, 86, 5, 61, 0, 0, 86, 14, 1, 0, 0, 0, 87, 88, 5, 60, 0, 0, 88, 89, 5, 62, 0, 0, 89, 16, 1, 0, 0, 0, 90, 91, 5, 60, 0, 0, 91, 18, 1, 0, 0, 0, 92, 93, 5, 60, 0, 0, 93, 94, 5, 61, 0, 0, 94, 20, 1, 0, 0, 0, 95, 96, 5, 62, 0, 0, 96, 22, 1, 0, 0, 0, 97, 98, 5, 62, 0, 0, 98, 99, 5, 61, 0, 0, 99, 24, 1, 0, 0, 0, 100, 101, 7, 0, 0, 0, 101, 102, 7, 1, 0, 0, 102, 103, 7, 2, 0, 0, 103, 104, 7, 3, 0, 0, 104, 26, 1, 0, 0, 0, 105, 106, 7, 4, 0, 0, 106, 107, 7, 5, 0, 0, 107, 109, 7, 6, 0, 0, 108, 110, 7, 7, 0, 0, 109, 108, 1, 0, 0, 0, 110, 111, 1, 0, 0, 0, 111, 109, 1, 0, 0, 0, 111, 112, 1, 0, 0, 0, 112, 113, 1, 0, 0, 0, 113, 114, 7, 0, 0, 0, 114, 115, 7, 1, 0, 0, 115, 116, 7, 2, 0, 0, 116, 117, 7, 3, 0, 0, 117, 28, 1, 0, 0, 0, 118, 119, 7, 1, 0, 0, 119, 120, 7, 0, 0, 0, 120, 121, 7, 1, 0, 0, 121, 122, 7, 2, 0, 0, 122, 123, 7, 3, 0, 0, 123, 30, 1, 0, 0, 0, 124, 125, 7, 4, 0, 0, 125, 126, 7, 5, 0, 0, 126, 128, 7, 6, 0, 0, 127, 129, 7, 7, 0, 0, 128, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 128, 1, 0, 0, 0, 130, 131, 1, 0, 0, 0, 131, 132, 1, 0, 0, 0, 132, 133, 7, 1, 0, 0, 133, 134, 7, 0, 0, 0, 134, 135, 7, 1, 0, 0, 135, 136, 7, 2, 0, 0, 136, 137, 7, 3, 0, 0, 137, 32, 1, 0, 0, 0, 138, 139, 7, 8, 0, 0, 139, 140, 7, 3, 0, 0, 140, 141, 7, 6, 0, 0, 141, 142, 7, 9, 0, 0, 142, 143, 7, 3, 0, 0, 143, 144, 7, 3, 0, 0, 144, 145, 7, 4, 0, 0, 145, 34, 1, 0, 0, 0, 146, 147, 7, 3, 0, 0, 147, 148, 7, 10, 0, 0, 148, 149, 7, 1, 0, 0, 149, 150, 7, 11, 0, 0, 150, 152, 7, 6, 0, 0, 151, 153, 7, 11, 0, 0, 152, 151, 1, 0, 0, 0, 152, 153, 1, 0, 0, 0, 153, 36, 1, 0, 0, 0, 154, 155, 7, 12, 0, 0, 155, 156, 7, 3, 0, 0, 156, 157, 7, 13, 0, 0, 157, 158, 7, 3, 0, 0, 158, 159, 7, 10, 0, 0, 159, 160, 7, 14, 0, 0, 160, 38, 1, 0, 0, 0, 161, 162, 7, 15, 0, 0, 162, 163, 7, 5, 0, 0, 163, 164, 7, 4, 0, 0, 164, 165, 7, 6, 0, 0, 165, 166, 7, 16, 0, 0, 166, 167, 7, 1, 0, 0, 167, 169, 7, 4, 0, 0, 168, 170, 7, 11, 0, 0, 169, 168, 1, 0, 0, 0, 169, 170, 1, 0, 0, 0, 170, 40, 1, 0, 0, 0, 171, 172, 7, 1, 0, 0, 172, 173, 7, 4, 0, 0, 173, 42, 1, 0, 0, 0, 174, 175, 7, 4, 0, 0, 175, 176, 7, 5, 0, 0, 176, 177, 7, 6, 0, 0, 177, 44, 1, 0, 0, 0, 178, 179, 7, 16, 0, 0, 179, 180, 7, 4, 0, 0, 180, 181, 7, 17, 0, 0, 181, 46, 1, 0, 0, 0, 182, 183, 7, 5, 0, 0, 183, 184, 7, 12, 0, 0, 184, 48, 1, 0, 0, 0, 185, 186, 7, 18, 0, 0, 186, 187, 7, 16, 0, 0, 187, 188, 7, 11, 0, 0, 188, 50, 1, 0, 0, 0, 189, 190, 7, 18, 0, 0, 190, 191, 7, 16, 0, 0, 191, 192, 7, 11, 0, 0, 192, 193, 7, 16, 0, 0, 193, 194, 7, 4, 0, 0, 194, 195, 7, 19, 0, 0, 195, 52, 1, 0, 0, 0, 196, 197, 7, 18, 0, 0, 197, 198, 7, 16, 0, 0, 198, 199, 7, 11, 0, 0, 199, 200, 7, 16, 0, 0, 200, 201, 7, 0, 0, 0, 201, 202, 7, 0, 0, 0, 202, 54, 1, 0, 0, 0, 203, 204, 7, 6, 0, 0, 204, 205, 7, 12, 0, 0, 205, 206, 7, 20, 0, 0, 206, 213, 7, 3, 0, 0, 207, 208, 7, 21, 0, 0, 208, 209, 7, 16, 0, 0, 209, 210, 7, 0, 0, 0, 210, 211, 7, 11, 0, 0, 211, 213, 7, 3, 0, 0, 212, 203, 1, 0, 0, 0, 212, 207, 1, 0, 0, 0, 213, 56, 1, 0, 0, 0, 214, 216, 3, 65, 32, 0, 215, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 215, 1, 0, 0, 0, 217, 218, 1, 0, 0, 0, 218, 225, 1, 0, 0, 0, 219, 221, 5, 46, 0, 0, 220, 222, 3, 65, 32, 0, 221, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 221, 1, 0, 0, 0, 223, 224, 1, 0, 0, 0, 224, 226, 1, 0, 0, 0, 225, 219, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 58, 1, 0, 0, 0, 227, 233, 5, 34, 0, 0, 228, 232, 8, 22, 0, 0, 229, 230, 5, 92, 0, 0, 230, 232, 9, 0, 0, 0, 231, 228, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 232, 235, 1, 0, 0, 0, 233, 231, 1, 0, 0, 0, 233, 234, 1, 0, 0, 0, 234, 236, 1, 0, 0, 0, 235, 233, 1, 0, 0, 0, 236, 248, 5, 34, 0, 0, 237, 243, 5, 39, 0, 0, 238, 242, 8, 23, 0, 0, 239, 240, 5, 92, 0, 0, 240, 242, 9, 0, 0, 0, 241, 238, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 242, 245, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 246, 1, 0, 0, 0, 245, 243, 1, 0, 0, 0, 246, 248, 5, 39, 0, 0, 247, 227, 1, 0, 0, 0, 247, 237, 1, 0, 0, 0, 248, 60, 1, 0, 0, 0, 249, 253, 7, 24, 0, 0, 250, 252, 7, 25, 0, 0, 251, 250, 1, 0, 0, 0, 252, 255, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 62, 1, 0, 0, 0, 255, 253, 1, 0, 0, 0, 256, 258, 7, 26, 0, 0, 257, 256, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 261, 1, 0, 0, 0, 261, 262, 6, 31, 0, 0, 262, 64, 1, 0, 0, 0, 263, 264, 7, 27, 0, 0, 264, 66, 1, 0, 0, 0, 265, 267, 8, 28, 0, 0, 266, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 68, 1, 0, 0, 0, 18, 0, 82, 111, 130, 152, 169, 212, 217, 223, 225, 231, 233, 241, 243, 247, 253, 259, 268, 1, 6, 0, 0]
\ No newline at end of file
+[4, 0, 33, 334, 6, -1, 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, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 91, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 4, 13, 118, 8, 13, 11, 13, 12, 13, 119, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 4, 15, 137, 8, 15, 11, 15, 12, 15, 138, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 161, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 3, 19, 178, 8, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 221, 8, 27, 1, 28, 1, 28, 1, 29, 3, 29, 226, 8, 29, 1, 29, 4, 29, 229, 8, 29, 11, 29, 12, 29, 230, 1, 29, 1, 29, 5, 29, 235, 8, 29, 10, 29, 12, 29, 238, 9, 29, 3, 29, 240, 8, 29, 1, 29, 1, 29, 3, 29, 244, 8, 29, 1, 29, 4, 29, 247, 8, 29, 11, 29, 12, 29, 248, 3, 29, 251, 8, 29, 1, 29, 3, 29, 254, 8, 29, 1, 29, 1, 29, 4, 29, 258, 8, 29, 11, 29, 12, 29, 259, 1, 29, 1, 29, 3, 29, 264, 8, 29, 1, 29, 4, 29, 267, 8, 29, 11, 29, 12, 29, 268, 3, 29, 271, 8, 29, 3, 29, 273, 8, 29, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 279, 8, 30, 10, 30, 12, 30, 282, 9, 30, 1, 30, 1, 30, 1, 30, 1, 30, 1, 30, 5, 30, 289, 8, 30, 10, 30, 12, 30, 292, 9, 30, 1, 30, 3, 30, 295, 8, 30, 1, 31, 1, 31, 5, 31, 299, 8, 31, 10, 31, 12, 31, 302, 9, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 316, 8, 34, 10, 34, 12, 34, 319, 9, 34, 1, 35, 4, 35, 322, 8, 35, 11, 35, 12, 35, 323, 1, 35, 1, 35, 1, 36, 1, 36, 1, 37, 4, 37, 331, 8, 37, 11, 37, 12, 37, 332, 0, 0, 38, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 0, 59, 29, 61, 30, 63, 0, 65, 0, 67, 0, 69, 31, 71, 32, 73, 0, 75, 33, 1, 0, 30, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, 75, 75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 84, 84, 116, 116, 2, 0, 9, 9, 32, 32, 2, 0, 66, 66, 98, 98, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71, 71, 103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72, 104, 104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70, 102, 102, 2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92, 2, 0, 65, 90, 97, 122, 5, 0, 45, 45, 48, 58, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 8, 0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 358, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 1, 77, 1, 0, 0, 0, 3, 79, 1, 0, 0, 0, 5, 81, 1, 0, 0, 0, 7, 83, 1, 0, 0, 0, 9, 85, 1, 0, 0, 0, 11, 90, 1, 0, 0, 0, 13, 92, 1, 0, 0, 0, 15, 95, 1, 0, 0, 0, 17, 98, 1, 0, 0, 0, 19, 100, 1, 0, 0, 0, 21, 103, 1, 0, 0, 0, 23, 105, 1, 0, 0, 0, 25, 108, 1, 0, 0, 0, 27, 113, 1, 0, 0, 0, 29, 126, 1, 0, 0, 0, 31, 132, 1, 0, 0, 0, 33, 146, 1, 0, 0, 0, 35, 154, 1, 0, 0, 0, 37, 162, 1, 0, 0, 0, 39, 169, 1, 0, 0, 0, 41, 179, 1, 0, 0, 0, 43, 182, 1, 0, 0, 0, 45, 186, 1, 0, 0, 0, 47, 190, 1, 0, 0, 0, 49, 193, 1, 0, 0, 0, 51, 197, 1, 0, 0, 0, 53, 204, 1, 0, 0, 0, 55, 220, 1, 0, 0, 0, 57, 222, 1, 0, 0, 0, 59, 272, 1, 0, 0, 0, 61, 294, 1, 0, 0, 0, 63, 296, 1, 0, 0, 0, 65, 303, 1, 0, 0, 0, 67, 306, 1, 0, 0, 0, 69, 310, 1, 0, 0, 0, 71, 321, 1, 0, 0, 0, 73, 327, 1, 0, 0, 0, 75, 330, 1, 0, 0, 0, 77, 78, 5, 40, 0, 0, 78, 2, 1, 0, 0, 0, 79, 80, 5, 41, 0, 0, 80, 4, 1, 0, 0, 0, 81, 82, 5, 91, 0, 0, 82, 6, 1, 0, 0, 0, 83, 84, 5, 93, 0, 0, 84, 8, 1, 0, 0, 0, 85, 86, 5, 44, 0, 0, 86, 10, 1, 0, 0, 0, 87, 91, 5, 61, 0, 0, 88, 89, 5, 61, 0, 0, 89, 91, 5, 61, 0, 0, 90, 87, 1, 0, 0, 0, 90, 88, 1, 0, 0, 0, 91, 12, 1, 0, 0, 0, 92, 93, 5, 33, 0, 0, 93, 94, 5, 61, 0, 0, 94, 14, 1, 0, 0, 0, 95, 96, 5, 60, 0, 0, 96, 97, 5, 62, 0, 0, 97, 16, 1, 0, 0, 0, 98, 99, 5, 60, 0, 0, 99, 18, 1, 0, 0, 0, 100, 101, 5, 60, 0, 0, 101, 102, 5, 61, 0, 0, 102, 20, 1, 0, 0, 0, 103, 104, 5, 62, 0, 0, 104, 22, 1, 0, 0, 0, 105, 106, 5, 62, 0, 0, 106, 107, 5, 61, 0, 0, 107, 24, 1, 0, 0, 0, 108, 109, 7, 0, 0, 0, 109, 110, 7, 1, 0, 0, 110, 111, 7, 2, 0, 0, 111, 112, 7, 3, 0, 0, 112, 26, 1, 0, 0, 0, 113, 114, 7, 4, 0, 0, 114, 115, 7, 5, 0, 0, 115, 117, 7, 6, 0, 0, 116, 118, 7, 7, 0, 0, 117, 116, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 117, 1, 0, 0, 0, 119, 120, 1, 0, 0, 0, 120, 121, 1, 0, 0, 0, 121, 122, 7, 0, 0, 0, 122, 123, 7, 1, 0, 0, 123, 124, 7, 2, 0, 0, 124, 125, 7, 3, 0, 0, 125, 28, 1, 0, 0, 0, 126, 127, 7, 1, 0, 0, 127, 128, 7, 0, 0, 0, 128, 129, 7, 1, 0, 0, 129, 130, 7, 2, 0, 0, 130, 131, 7, 3, 0, 0, 131, 30, 1, 0, 0, 0, 132, 133, 7, 4, 0, 0, 133, 134, 7, 5, 0, 0, 134, 136, 7, 6, 0, 0, 135, 137, 7, 7, 0, 0, 136, 135, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 138, 139, 1, 0, 0, 0, 139, 140, 1, 0, 0, 0, 140, 141, 7, 1, 0, 0, 141, 142, 7, 0, 0, 0, 142, 143, 7, 1, 0, 0, 143, 144, 7, 2, 0, 0, 144, 145, 7, 3, 0, 0, 145, 32, 1, 0, 0, 0, 146, 147, 7, 8, 0, 0, 147, 148, 7, 3, 0, 0, 148, 149, 7, 6, 0, 0, 149, 150, 7, 9, 0, 0, 150, 151, 7, 3, 0, 0, 151, 152, 7, 3, 0, 0, 152, 153, 7, 4, 0, 0, 153, 34, 1, 0, 0, 0, 154, 155, 7, 3, 0, 0, 155, 156, 7, 10, 0, 0, 156, 157, 7, 1, 0, 0, 157, 158, 7, 11, 0, 0, 158, 160, 7, 6, 0, 0, 159, 161, 7, 11, 0, 0, 160, 159, 1, 0, 0, 0, 160, 161, 1, 0, 0, 0, 161, 36, 1, 0, 0, 0, 162, 163, 7, 12, 0, 0, 163, 164, 7, 3, 0, 0, 164, 165, 7, 13, 0, 0, 165, 166, 7, 3, 0, 0, 166, 167, 7, 10, 0, 0, 167, 168, 7, 14, 0, 0, 168, 38, 1, 0, 0, 0, 169, 170, 7, 15, 0, 0, 170, 171, 7, 5, 0, 0, 171, 172, 7, 4, 0, 0, 172, 173, 7, 6, 0, 0, 173, 174, 7, 16, 0, 0, 174, 175, 7, 1, 0, 0, 175, 177, 7, 4, 0, 0, 176, 178, 7, 11, 0, 0, 177, 176, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 40, 1, 0, 0, 0, 179, 180, 7, 1, 0, 0, 180, 181, 7, 4, 0, 0, 181, 42, 1, 0, 0, 0, 182, 183, 7, 4, 0, 0, 183, 184, 7, 5, 0, 0, 184, 185, 7, 6, 0, 0, 185, 44, 1, 0, 0, 0, 186, 187, 7, 16, 0, 0, 187, 188, 7, 4, 0, 0, 188, 189, 7, 17, 0, 0, 189, 46, 1, 0, 0, 0, 190, 191, 7, 5, 0, 0, 191, 192, 7, 12, 0, 0, 192, 48, 1, 0, 0, 0, 193, 194, 7, 18, 0, 0, 194, 195, 7, 16, 0, 0, 195, 196, 7, 11, 0, 0, 196, 50, 1, 0, 0, 0, 197, 198, 7, 18, 0, 0, 198, 199, 7, 16, 0, 0, 199, 200, 7, 11, 0, 0, 200, 201, 7, 16, 0, 0, 201, 202, 7, 4, 0, 0, 202, 203, 7, 19, 0, 0, 203, 52, 1, 0, 0, 0, 204, 205, 7, 18, 0, 0, 205, 206, 7, 16, 0, 0, 206, 207, 7, 11, 0, 0, 207, 208, 7, 16, 0, 0, 208, 209, 7, 0, 0, 0, 209, 210, 7, 0, 0, 0, 210, 54, 1, 0, 0, 0, 211, 212, 7, 6, 0, 0, 212, 213, 7, 12, 0, 0, 213, 214, 7, 20, 0, 0, 214, 221, 7, 3, 0, 0, 215, 216, 7, 21, 0, 0, 216, 217, 7, 16, 0, 0, 217, 218, 7, 0, 0, 0, 218, 219, 7, 11, 0, 0, 219, 221, 7, 3, 0, 0, 220, 211, 1, 0, 0, 0, 220, 215, 1, 0, 0, 0, 221, 56, 1, 0, 0, 0, 222, 223, 7, 22, 0, 0, 223, 58, 1, 0, 0, 0, 224, 226, 3, 57, 28, 0, 225, 224, 1, 0, 0, 0, 225, 226, 1, 0, 0, 0, 226, 228, 1, 0, 0, 0, 227, 229, 3, 73, 36, 0, 228, 227, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 228, 1, 0, 0, 0, 230, 231, 1, 0, 0, 0, 231, 239, 1, 0, 0, 0, 232, 236, 5, 46, 0, 0, 233, 235, 3, 73, 36, 0, 234, 233, 1, 0, 0, 0, 235, 238, 1, 0, 0, 0, 236, 234, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 240, 1, 0, 0, 0, 238, 236, 1, 0, 0, 0, 239, 232, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 250, 1, 0, 0, 0, 241, 243, 7, 3, 0, 0, 242, 244, 3, 57, 28, 0, 243, 242, 1, 0, 0, 0, 243, 244, 1, 0, 0, 0, 244, 246, 1, 0, 0, 0, 245, 247, 3, 73, 36, 0, 246, 245, 1, 0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 246, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 251, 1, 0, 0, 0, 250, 241, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251, 273, 1, 0, 0, 0, 252, 254, 3, 57, 28, 0, 253, 252, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 257, 5, 46, 0, 0, 256, 258, 3, 73, 36, 0, 257, 256, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260, 270, 1, 0, 0, 0, 261, 263, 7, 3, 0, 0, 262, 264, 3, 57, 28, 0, 263, 262, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 266, 1, 0, 0, 0, 265, 267, 3, 73, 36, 0, 266, 265, 1, 0, 0, 0, 267, 268, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 271, 1, 0, 0, 0, 270, 261, 1, 0, 0, 0, 270, 271, 1, 0, 0, 0, 271, 273, 1, 0, 0, 0, 272, 225, 1, 0, 0, 0, 272, 253, 1, 0, 0, 0, 273, 60, 1, 0, 0, 0, 274, 280, 5, 34, 0, 0, 275, 279, 8, 23, 0, 0, 276, 277, 5, 92, 0, 0, 277, 279, 9, 0, 0, 0, 278, 275, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0, 280, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 283, 1, 0, 0, 0, 282, 280, 1, 0, 0, 0, 283, 295, 5, 34, 0, 0, 284, 290, 5, 39, 0, 0, 285, 289, 8, 24, 0, 0, 286, 287, 5, 92, 0, 0, 287, 289, 9, 0, 0, 0, 288, 285, 1, 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 292, 1, 0, 0, 0, 290, 288, 1, 0, 0, 0, 290, 291, 1, 0, 0, 0, 291, 293, 1, 0, 0, 0, 292, 290, 1, 0, 0, 0, 293, 295, 5, 39, 0, 0, 294, 274, 1, 0, 0, 0, 294, 284, 1, 0, 0, 0, 295, 62, 1, 0, 0, 0, 296, 300, 7, 25, 0, 0, 297, 299, 7, 26, 0, 0, 298, 297, 1, 0, 0, 0, 299, 302, 1, 0, 0, 0, 300, 298, 1, 0, 0, 0, 300, 301, 1, 0, 0, 0, 301, 64, 1, 0, 0, 0, 302, 300, 1, 0, 0, 0, 303, 304, 5, 91, 0, 0, 304, 305, 5, 93, 0, 0, 305, 66, 1, 0, 0, 0, 306, 307, 5, 91, 0, 0, 307, 308, 5, 42, 0, 0, 308, 309, 5, 93, 0, 0, 309, 68, 1, 0, 0, 0, 310, 317, 3, 63, 31, 0, 311, 312, 5, 46, 0, 0, 312, 316, 3, 63, 31, 0, 313, 316, 3, 65, 32, 0, 314, 316, 3, 67, 33, 0, 315, 311, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 315, 314, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 70, 1, 0, 0, 0, 319, 317, 1, 0, 0, 0, 320, 322, 7, 27, 0, 0, 321, 320, 1, 0, 0, 0, 322, 323, 1, 0, 0, 0, 323, 321, 1, 0, 0, 0, 323, 324, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 326, 6, 35, 0, 0, 326, 72, 1, 0, 0, 0, 327, 328, 7, 28, 0, 0, 328, 74, 1, 0, 0, 0, 329, 331, 8, 29, 0, 0, 330, 329, 1, 0, 0, 0, 331, 332, 1, 0, 0, 0, 332, 330, 1, 0, 0, 0, 332, 333, 1, 0, 0, 0, 333, 76, 1, 0, 0, 0, 30, 0, 90, 119, 138, 160, 177, 220, 225, 230, 236, 239, 243, 248, 250, 253, 259, 263, 268, 270, 272, 278, 280, 288, 290, 294, 300, 315, 317, 323, 332, 1, 6, 0, 0]
\ No newline at end of file
diff --git a/pkg/parser/grammar/filterquery_lexer.go b/pkg/parser/grammar/filterquery_lexer.go
index e1588f5adb..2903775376 100644
--- a/pkg/parser/grammar/filterquery_lexer.go
+++ b/pkg/parser/grammar/filterquery_lexer.go
@@ -57,139 +57,171 @@ func filterquerylexerLexerInit() {
"LPAREN", "RPAREN", "LBRACK", "RBRACK", "COMMA", "EQUALS", "NOT_EQUALS",
"NEQ", "LT", "LE", "GT", "GE", "LIKE", "NOT_LIKE", "ILIKE", "NOT_ILIKE",
"BETWEEN", "EXISTS", "REGEXP", "CONTAINS", "IN", "NOT", "AND", "OR",
- "HAS", "HASANY", "HASALL", "BOOL", "NUMBER", "QUOTED_TEXT", "KEY", "WS",
- "DIGIT", "FREETEXT",
+ "HAS", "HASANY", "HASALL", "BOOL", "SIGN", "NUMBER", "QUOTED_TEXT",
+ "SEGMENT", "EMPTY_BRACKS", "OLD_JSON_BRACKS", "KEY", "WS", "DIGIT",
+ "FREETEXT",
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
- 4, 0, 33, 270, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
+ 4, 0, 33, 334, 6, -1, 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, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25,
2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2,
- 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1,
- 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 83, 8, 5, 1, 6, 1, 6,
- 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1,
- 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13,
- 1, 13, 4, 13, 110, 8, 13, 11, 13, 12, 13, 111, 1, 13, 1, 13, 1, 13, 1,
- 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15,
- 1, 15, 4, 15, 129, 8, 15, 11, 15, 12, 15, 130, 1, 15, 1, 15, 1, 15, 1,
- 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16,
- 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 153, 8, 17, 1, 18, 1,
- 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19,
- 1, 19, 1, 19, 1, 19, 3, 19, 170, 8, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1,
- 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24,
- 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1,
- 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27,
- 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 213, 8, 27, 1, 28, 4, 28, 216,
- 8, 28, 11, 28, 12, 28, 217, 1, 28, 1, 28, 4, 28, 222, 8, 28, 11, 28, 12,
- 28, 223, 3, 28, 226, 8, 28, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29, 232, 8,
- 29, 10, 29, 12, 29, 235, 9, 29, 1, 29, 1, 29, 1, 29, 1, 29, 1, 29, 5, 29,
- 242, 8, 29, 10, 29, 12, 29, 245, 9, 29, 1, 29, 3, 29, 248, 8, 29, 1, 30,
- 1, 30, 5, 30, 252, 8, 30, 10, 30, 12, 30, 255, 9, 30, 1, 31, 4, 31, 258,
- 8, 31, 11, 31, 12, 31, 259, 1, 31, 1, 31, 1, 32, 1, 32, 1, 33, 4, 33, 267,
- 8, 33, 11, 33, 12, 33, 268, 0, 0, 34, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11,
- 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15,
- 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24,
- 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 0,
- 67, 33, 1, 0, 29, 2, 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0,
- 75, 75, 107, 107, 2, 0, 69, 69, 101, 101, 2, 0, 78, 78, 110, 110, 2, 0,
- 79, 79, 111, 111, 2, 0, 84, 84, 116, 116, 2, 0, 9, 9, 32, 32, 2, 0, 66,
- 66, 98, 98, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 83, 83,
- 115, 115, 2, 0, 82, 82, 114, 114, 2, 0, 71, 71, 103, 103, 2, 0, 80, 80,
- 112, 112, 2, 0, 67, 67, 99, 99, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100,
- 100, 2, 0, 72, 72, 104, 104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117,
- 117, 2, 0, 70, 70, 102, 102, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92, 92,
- 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 7, 0, 42, 42, 46, 46, 48, 57, 65,
- 91, 93, 93, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57,
- 8, 0, 9, 10, 13, 13, 32, 34, 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 285,
- 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0,
- 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0,
- 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0,
- 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1,
- 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39,
- 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0,
- 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0,
- 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0,
- 0, 0, 63, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 1, 69, 1, 0, 0, 0, 3, 71, 1, 0,
- 0, 0, 5, 73, 1, 0, 0, 0, 7, 75, 1, 0, 0, 0, 9, 77, 1, 0, 0, 0, 11, 82,
- 1, 0, 0, 0, 13, 84, 1, 0, 0, 0, 15, 87, 1, 0, 0, 0, 17, 90, 1, 0, 0, 0,
- 19, 92, 1, 0, 0, 0, 21, 95, 1, 0, 0, 0, 23, 97, 1, 0, 0, 0, 25, 100, 1,
- 0, 0, 0, 27, 105, 1, 0, 0, 0, 29, 118, 1, 0, 0, 0, 31, 124, 1, 0, 0, 0,
- 33, 138, 1, 0, 0, 0, 35, 146, 1, 0, 0, 0, 37, 154, 1, 0, 0, 0, 39, 161,
- 1, 0, 0, 0, 41, 171, 1, 0, 0, 0, 43, 174, 1, 0, 0, 0, 45, 178, 1, 0, 0,
- 0, 47, 182, 1, 0, 0, 0, 49, 185, 1, 0, 0, 0, 51, 189, 1, 0, 0, 0, 53, 196,
- 1, 0, 0, 0, 55, 212, 1, 0, 0, 0, 57, 215, 1, 0, 0, 0, 59, 247, 1, 0, 0,
- 0, 61, 249, 1, 0, 0, 0, 63, 257, 1, 0, 0, 0, 65, 263, 1, 0, 0, 0, 67, 266,
- 1, 0, 0, 0, 69, 70, 5, 40, 0, 0, 70, 2, 1, 0, 0, 0, 71, 72, 5, 41, 0, 0,
- 72, 4, 1, 0, 0, 0, 73, 74, 5, 91, 0, 0, 74, 6, 1, 0, 0, 0, 75, 76, 5, 93,
- 0, 0, 76, 8, 1, 0, 0, 0, 77, 78, 5, 44, 0, 0, 78, 10, 1, 0, 0, 0, 79, 83,
- 5, 61, 0, 0, 80, 81, 5, 61, 0, 0, 81, 83, 5, 61, 0, 0, 82, 79, 1, 0, 0,
- 0, 82, 80, 1, 0, 0, 0, 83, 12, 1, 0, 0, 0, 84, 85, 5, 33, 0, 0, 85, 86,
- 5, 61, 0, 0, 86, 14, 1, 0, 0, 0, 87, 88, 5, 60, 0, 0, 88, 89, 5, 62, 0,
- 0, 89, 16, 1, 0, 0, 0, 90, 91, 5, 60, 0, 0, 91, 18, 1, 0, 0, 0, 92, 93,
- 5, 60, 0, 0, 93, 94, 5, 61, 0, 0, 94, 20, 1, 0, 0, 0, 95, 96, 5, 62, 0,
- 0, 96, 22, 1, 0, 0, 0, 97, 98, 5, 62, 0, 0, 98, 99, 5, 61, 0, 0, 99, 24,
- 1, 0, 0, 0, 100, 101, 7, 0, 0, 0, 101, 102, 7, 1, 0, 0, 102, 103, 7, 2,
- 0, 0, 103, 104, 7, 3, 0, 0, 104, 26, 1, 0, 0, 0, 105, 106, 7, 4, 0, 0,
- 106, 107, 7, 5, 0, 0, 107, 109, 7, 6, 0, 0, 108, 110, 7, 7, 0, 0, 109,
- 108, 1, 0, 0, 0, 110, 111, 1, 0, 0, 0, 111, 109, 1, 0, 0, 0, 111, 112,
- 1, 0, 0, 0, 112, 113, 1, 0, 0, 0, 113, 114, 7, 0, 0, 0, 114, 115, 7, 1,
- 0, 0, 115, 116, 7, 2, 0, 0, 116, 117, 7, 3, 0, 0, 117, 28, 1, 0, 0, 0,
- 118, 119, 7, 1, 0, 0, 119, 120, 7, 0, 0, 0, 120, 121, 7, 1, 0, 0, 121,
- 122, 7, 2, 0, 0, 122, 123, 7, 3, 0, 0, 123, 30, 1, 0, 0, 0, 124, 125, 7,
- 4, 0, 0, 125, 126, 7, 5, 0, 0, 126, 128, 7, 6, 0, 0, 127, 129, 7, 7, 0,
- 0, 128, 127, 1, 0, 0, 0, 129, 130, 1, 0, 0, 0, 130, 128, 1, 0, 0, 0, 130,
- 131, 1, 0, 0, 0, 131, 132, 1, 0, 0, 0, 132, 133, 7, 1, 0, 0, 133, 134,
- 7, 0, 0, 0, 134, 135, 7, 1, 0, 0, 135, 136, 7, 2, 0, 0, 136, 137, 7, 3,
- 0, 0, 137, 32, 1, 0, 0, 0, 138, 139, 7, 8, 0, 0, 139, 140, 7, 3, 0, 0,
- 140, 141, 7, 6, 0, 0, 141, 142, 7, 9, 0, 0, 142, 143, 7, 3, 0, 0, 143,
- 144, 7, 3, 0, 0, 144, 145, 7, 4, 0, 0, 145, 34, 1, 0, 0, 0, 146, 147, 7,
- 3, 0, 0, 147, 148, 7, 10, 0, 0, 148, 149, 7, 1, 0, 0, 149, 150, 7, 11,
- 0, 0, 150, 152, 7, 6, 0, 0, 151, 153, 7, 11, 0, 0, 152, 151, 1, 0, 0, 0,
- 152, 153, 1, 0, 0, 0, 153, 36, 1, 0, 0, 0, 154, 155, 7, 12, 0, 0, 155,
- 156, 7, 3, 0, 0, 156, 157, 7, 13, 0, 0, 157, 158, 7, 3, 0, 0, 158, 159,
- 7, 10, 0, 0, 159, 160, 7, 14, 0, 0, 160, 38, 1, 0, 0, 0, 161, 162, 7, 15,
- 0, 0, 162, 163, 7, 5, 0, 0, 163, 164, 7, 4, 0, 0, 164, 165, 7, 6, 0, 0,
- 165, 166, 7, 16, 0, 0, 166, 167, 7, 1, 0, 0, 167, 169, 7, 4, 0, 0, 168,
- 170, 7, 11, 0, 0, 169, 168, 1, 0, 0, 0, 169, 170, 1, 0, 0, 0, 170, 40,
- 1, 0, 0, 0, 171, 172, 7, 1, 0, 0, 172, 173, 7, 4, 0, 0, 173, 42, 1, 0,
- 0, 0, 174, 175, 7, 4, 0, 0, 175, 176, 7, 5, 0, 0, 176, 177, 7, 6, 0, 0,
- 177, 44, 1, 0, 0, 0, 178, 179, 7, 16, 0, 0, 179, 180, 7, 4, 0, 0, 180,
- 181, 7, 17, 0, 0, 181, 46, 1, 0, 0, 0, 182, 183, 7, 5, 0, 0, 183, 184,
- 7, 12, 0, 0, 184, 48, 1, 0, 0, 0, 185, 186, 7, 18, 0, 0, 186, 187, 7, 16,
- 0, 0, 187, 188, 7, 11, 0, 0, 188, 50, 1, 0, 0, 0, 189, 190, 7, 18, 0, 0,
- 190, 191, 7, 16, 0, 0, 191, 192, 7, 11, 0, 0, 192, 193, 7, 16, 0, 0, 193,
- 194, 7, 4, 0, 0, 194, 195, 7, 19, 0, 0, 195, 52, 1, 0, 0, 0, 196, 197,
- 7, 18, 0, 0, 197, 198, 7, 16, 0, 0, 198, 199, 7, 11, 0, 0, 199, 200, 7,
- 16, 0, 0, 200, 201, 7, 0, 0, 0, 201, 202, 7, 0, 0, 0, 202, 54, 1, 0, 0,
- 0, 203, 204, 7, 6, 0, 0, 204, 205, 7, 12, 0, 0, 205, 206, 7, 20, 0, 0,
- 206, 213, 7, 3, 0, 0, 207, 208, 7, 21, 0, 0, 208, 209, 7, 16, 0, 0, 209,
- 210, 7, 0, 0, 0, 210, 211, 7, 11, 0, 0, 211, 213, 7, 3, 0, 0, 212, 203,
- 1, 0, 0, 0, 212, 207, 1, 0, 0, 0, 213, 56, 1, 0, 0, 0, 214, 216, 3, 65,
- 32, 0, 215, 214, 1, 0, 0, 0, 216, 217, 1, 0, 0, 0, 217, 215, 1, 0, 0, 0,
- 217, 218, 1, 0, 0, 0, 218, 225, 1, 0, 0, 0, 219, 221, 5, 46, 0, 0, 220,
- 222, 3, 65, 32, 0, 221, 220, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 221,
- 1, 0, 0, 0, 223, 224, 1, 0, 0, 0, 224, 226, 1, 0, 0, 0, 225, 219, 1, 0,
- 0, 0, 225, 226, 1, 0, 0, 0, 226, 58, 1, 0, 0, 0, 227, 233, 5, 34, 0, 0,
- 228, 232, 8, 22, 0, 0, 229, 230, 5, 92, 0, 0, 230, 232, 9, 0, 0, 0, 231,
- 228, 1, 0, 0, 0, 231, 229, 1, 0, 0, 0, 232, 235, 1, 0, 0, 0, 233, 231,
- 1, 0, 0, 0, 233, 234, 1, 0, 0, 0, 234, 236, 1, 0, 0, 0, 235, 233, 1, 0,
- 0, 0, 236, 248, 5, 34, 0, 0, 237, 243, 5, 39, 0, 0, 238, 242, 8, 23, 0,
- 0, 239, 240, 5, 92, 0, 0, 240, 242, 9, 0, 0, 0, 241, 238, 1, 0, 0, 0, 241,
- 239, 1, 0, 0, 0, 242, 245, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 243, 244,
- 1, 0, 0, 0, 244, 246, 1, 0, 0, 0, 245, 243, 1, 0, 0, 0, 246, 248, 5, 39,
- 0, 0, 247, 227, 1, 0, 0, 0, 247, 237, 1, 0, 0, 0, 248, 60, 1, 0, 0, 0,
- 249, 253, 7, 24, 0, 0, 250, 252, 7, 25, 0, 0, 251, 250, 1, 0, 0, 0, 252,
- 255, 1, 0, 0, 0, 253, 251, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 62, 1,
- 0, 0, 0, 255, 253, 1, 0, 0, 0, 256, 258, 7, 26, 0, 0, 257, 256, 1, 0, 0,
- 0, 258, 259, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0, 259, 260, 1, 0, 0, 0, 260,
- 261, 1, 0, 0, 0, 261, 262, 6, 31, 0, 0, 262, 64, 1, 0, 0, 0, 263, 264,
- 7, 27, 0, 0, 264, 66, 1, 0, 0, 0, 265, 267, 8, 28, 0, 0, 266, 265, 1, 0,
- 0, 0, 267, 268, 1, 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0,
- 269, 68, 1, 0, 0, 0, 18, 0, 82, 111, 130, 152, 169, 212, 217, 223, 225,
- 231, 233, 241, 243, 247, 253, 259, 268, 1, 6, 0, 0,
+ 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36,
+ 7, 36, 2, 37, 7, 37, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1,
+ 4, 1, 4, 1, 5, 1, 5, 1, 5, 3, 5, 91, 8, 5, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7,
+ 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11,
+ 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 13, 4, 13, 118,
+ 8, 13, 11, 13, 12, 13, 119, 1, 13, 1, 13, 1, 13, 1, 13, 1, 13, 1, 14, 1,
+ 14, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 4, 15, 137,
+ 8, 15, 11, 15, 12, 15, 138, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1,
+ 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17,
+ 1, 17, 1, 17, 1, 17, 3, 17, 161, 8, 17, 1, 18, 1, 18, 1, 18, 1, 18, 1,
+ 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19, 1, 19,
+ 3, 19, 178, 8, 19, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1,
+ 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24,
+ 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1,
+ 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27,
+ 1, 27, 1, 27, 3, 27, 221, 8, 27, 1, 28, 1, 28, 1, 29, 3, 29, 226, 8, 29,
+ 1, 29, 4, 29, 229, 8, 29, 11, 29, 12, 29, 230, 1, 29, 1, 29, 5, 29, 235,
+ 8, 29, 10, 29, 12, 29, 238, 9, 29, 3, 29, 240, 8, 29, 1, 29, 1, 29, 3,
+ 29, 244, 8, 29, 1, 29, 4, 29, 247, 8, 29, 11, 29, 12, 29, 248, 3, 29, 251,
+ 8, 29, 1, 29, 3, 29, 254, 8, 29, 1, 29, 1, 29, 4, 29, 258, 8, 29, 11, 29,
+ 12, 29, 259, 1, 29, 1, 29, 3, 29, 264, 8, 29, 1, 29, 4, 29, 267, 8, 29,
+ 11, 29, 12, 29, 268, 3, 29, 271, 8, 29, 3, 29, 273, 8, 29, 1, 30, 1, 30,
+ 1, 30, 1, 30, 5, 30, 279, 8, 30, 10, 30, 12, 30, 282, 9, 30, 1, 30, 1,
+ 30, 1, 30, 1, 30, 1, 30, 5, 30, 289, 8, 30, 10, 30, 12, 30, 292, 9, 30,
+ 1, 30, 3, 30, 295, 8, 30, 1, 31, 1, 31, 5, 31, 299, 8, 31, 10, 31, 12,
+ 31, 302, 9, 31, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34,
+ 1, 34, 1, 34, 1, 34, 1, 34, 5, 34, 316, 8, 34, 10, 34, 12, 34, 319, 9,
+ 34, 1, 35, 4, 35, 322, 8, 35, 11, 35, 12, 35, 323, 1, 35, 1, 35, 1, 36,
+ 1, 36, 1, 37, 4, 37, 331, 8, 37, 11, 37, 12, 37, 332, 0, 0, 38, 1, 1, 3,
+ 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12,
+ 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21,
+ 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 0, 59, 29,
+ 61, 30, 63, 0, 65, 0, 67, 0, 69, 31, 71, 32, 73, 0, 75, 33, 1, 0, 30, 2,
+ 0, 76, 76, 108, 108, 2, 0, 73, 73, 105, 105, 2, 0, 75, 75, 107, 107, 2,
+ 0, 69, 69, 101, 101, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2,
+ 0, 84, 84, 116, 116, 2, 0, 9, 9, 32, 32, 2, 0, 66, 66, 98, 98, 2, 0, 87,
+ 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 83, 83, 115, 115, 2, 0, 82,
+ 82, 114, 114, 2, 0, 71, 71, 103, 103, 2, 0, 80, 80, 112, 112, 2, 0, 67,
+ 67, 99, 99, 2, 0, 65, 65, 97, 97, 2, 0, 68, 68, 100, 100, 2, 0, 72, 72,
+ 104, 104, 2, 0, 89, 89, 121, 121, 2, 0, 85, 85, 117, 117, 2, 0, 70, 70,
+ 102, 102, 2, 0, 43, 43, 45, 45, 2, 0, 34, 34, 92, 92, 2, 0, 39, 39, 92,
+ 92, 2, 0, 65, 90, 97, 122, 5, 0, 45, 45, 48, 58, 65, 90, 95, 95, 97, 122,
+ 3, 0, 9, 10, 13, 13, 32, 32, 1, 0, 48, 57, 8, 0, 9, 10, 13, 13, 32, 34,
+ 39, 41, 44, 44, 60, 62, 91, 91, 93, 93, 358, 0, 1, 1, 0, 0, 0, 0, 3, 1,
+ 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1,
+ 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19,
+ 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0,
+ 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0,
+ 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0,
+ 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0,
+ 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 59, 1,
+ 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 75,
+ 1, 0, 0, 0, 1, 77, 1, 0, 0, 0, 3, 79, 1, 0, 0, 0, 5, 81, 1, 0, 0, 0, 7,
+ 83, 1, 0, 0, 0, 9, 85, 1, 0, 0, 0, 11, 90, 1, 0, 0, 0, 13, 92, 1, 0, 0,
+ 0, 15, 95, 1, 0, 0, 0, 17, 98, 1, 0, 0, 0, 19, 100, 1, 0, 0, 0, 21, 103,
+ 1, 0, 0, 0, 23, 105, 1, 0, 0, 0, 25, 108, 1, 0, 0, 0, 27, 113, 1, 0, 0,
+ 0, 29, 126, 1, 0, 0, 0, 31, 132, 1, 0, 0, 0, 33, 146, 1, 0, 0, 0, 35, 154,
+ 1, 0, 0, 0, 37, 162, 1, 0, 0, 0, 39, 169, 1, 0, 0, 0, 41, 179, 1, 0, 0,
+ 0, 43, 182, 1, 0, 0, 0, 45, 186, 1, 0, 0, 0, 47, 190, 1, 0, 0, 0, 49, 193,
+ 1, 0, 0, 0, 51, 197, 1, 0, 0, 0, 53, 204, 1, 0, 0, 0, 55, 220, 1, 0, 0,
+ 0, 57, 222, 1, 0, 0, 0, 59, 272, 1, 0, 0, 0, 61, 294, 1, 0, 0, 0, 63, 296,
+ 1, 0, 0, 0, 65, 303, 1, 0, 0, 0, 67, 306, 1, 0, 0, 0, 69, 310, 1, 0, 0,
+ 0, 71, 321, 1, 0, 0, 0, 73, 327, 1, 0, 0, 0, 75, 330, 1, 0, 0, 0, 77, 78,
+ 5, 40, 0, 0, 78, 2, 1, 0, 0, 0, 79, 80, 5, 41, 0, 0, 80, 4, 1, 0, 0, 0,
+ 81, 82, 5, 91, 0, 0, 82, 6, 1, 0, 0, 0, 83, 84, 5, 93, 0, 0, 84, 8, 1,
+ 0, 0, 0, 85, 86, 5, 44, 0, 0, 86, 10, 1, 0, 0, 0, 87, 91, 5, 61, 0, 0,
+ 88, 89, 5, 61, 0, 0, 89, 91, 5, 61, 0, 0, 90, 87, 1, 0, 0, 0, 90, 88, 1,
+ 0, 0, 0, 91, 12, 1, 0, 0, 0, 92, 93, 5, 33, 0, 0, 93, 94, 5, 61, 0, 0,
+ 94, 14, 1, 0, 0, 0, 95, 96, 5, 60, 0, 0, 96, 97, 5, 62, 0, 0, 97, 16, 1,
+ 0, 0, 0, 98, 99, 5, 60, 0, 0, 99, 18, 1, 0, 0, 0, 100, 101, 5, 60, 0, 0,
+ 101, 102, 5, 61, 0, 0, 102, 20, 1, 0, 0, 0, 103, 104, 5, 62, 0, 0, 104,
+ 22, 1, 0, 0, 0, 105, 106, 5, 62, 0, 0, 106, 107, 5, 61, 0, 0, 107, 24,
+ 1, 0, 0, 0, 108, 109, 7, 0, 0, 0, 109, 110, 7, 1, 0, 0, 110, 111, 7, 2,
+ 0, 0, 111, 112, 7, 3, 0, 0, 112, 26, 1, 0, 0, 0, 113, 114, 7, 4, 0, 0,
+ 114, 115, 7, 5, 0, 0, 115, 117, 7, 6, 0, 0, 116, 118, 7, 7, 0, 0, 117,
+ 116, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 117, 1, 0, 0, 0, 119, 120,
+ 1, 0, 0, 0, 120, 121, 1, 0, 0, 0, 121, 122, 7, 0, 0, 0, 122, 123, 7, 1,
+ 0, 0, 123, 124, 7, 2, 0, 0, 124, 125, 7, 3, 0, 0, 125, 28, 1, 0, 0, 0,
+ 126, 127, 7, 1, 0, 0, 127, 128, 7, 0, 0, 0, 128, 129, 7, 1, 0, 0, 129,
+ 130, 7, 2, 0, 0, 130, 131, 7, 3, 0, 0, 131, 30, 1, 0, 0, 0, 132, 133, 7,
+ 4, 0, 0, 133, 134, 7, 5, 0, 0, 134, 136, 7, 6, 0, 0, 135, 137, 7, 7, 0,
+ 0, 136, 135, 1, 0, 0, 0, 137, 138, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 138,
+ 139, 1, 0, 0, 0, 139, 140, 1, 0, 0, 0, 140, 141, 7, 1, 0, 0, 141, 142,
+ 7, 0, 0, 0, 142, 143, 7, 1, 0, 0, 143, 144, 7, 2, 0, 0, 144, 145, 7, 3,
+ 0, 0, 145, 32, 1, 0, 0, 0, 146, 147, 7, 8, 0, 0, 147, 148, 7, 3, 0, 0,
+ 148, 149, 7, 6, 0, 0, 149, 150, 7, 9, 0, 0, 150, 151, 7, 3, 0, 0, 151,
+ 152, 7, 3, 0, 0, 152, 153, 7, 4, 0, 0, 153, 34, 1, 0, 0, 0, 154, 155, 7,
+ 3, 0, 0, 155, 156, 7, 10, 0, 0, 156, 157, 7, 1, 0, 0, 157, 158, 7, 11,
+ 0, 0, 158, 160, 7, 6, 0, 0, 159, 161, 7, 11, 0, 0, 160, 159, 1, 0, 0, 0,
+ 160, 161, 1, 0, 0, 0, 161, 36, 1, 0, 0, 0, 162, 163, 7, 12, 0, 0, 163,
+ 164, 7, 3, 0, 0, 164, 165, 7, 13, 0, 0, 165, 166, 7, 3, 0, 0, 166, 167,
+ 7, 10, 0, 0, 167, 168, 7, 14, 0, 0, 168, 38, 1, 0, 0, 0, 169, 170, 7, 15,
+ 0, 0, 170, 171, 7, 5, 0, 0, 171, 172, 7, 4, 0, 0, 172, 173, 7, 6, 0, 0,
+ 173, 174, 7, 16, 0, 0, 174, 175, 7, 1, 0, 0, 175, 177, 7, 4, 0, 0, 176,
+ 178, 7, 11, 0, 0, 177, 176, 1, 0, 0, 0, 177, 178, 1, 0, 0, 0, 178, 40,
+ 1, 0, 0, 0, 179, 180, 7, 1, 0, 0, 180, 181, 7, 4, 0, 0, 181, 42, 1, 0,
+ 0, 0, 182, 183, 7, 4, 0, 0, 183, 184, 7, 5, 0, 0, 184, 185, 7, 6, 0, 0,
+ 185, 44, 1, 0, 0, 0, 186, 187, 7, 16, 0, 0, 187, 188, 7, 4, 0, 0, 188,
+ 189, 7, 17, 0, 0, 189, 46, 1, 0, 0, 0, 190, 191, 7, 5, 0, 0, 191, 192,
+ 7, 12, 0, 0, 192, 48, 1, 0, 0, 0, 193, 194, 7, 18, 0, 0, 194, 195, 7, 16,
+ 0, 0, 195, 196, 7, 11, 0, 0, 196, 50, 1, 0, 0, 0, 197, 198, 7, 18, 0, 0,
+ 198, 199, 7, 16, 0, 0, 199, 200, 7, 11, 0, 0, 200, 201, 7, 16, 0, 0, 201,
+ 202, 7, 4, 0, 0, 202, 203, 7, 19, 0, 0, 203, 52, 1, 0, 0, 0, 204, 205,
+ 7, 18, 0, 0, 205, 206, 7, 16, 0, 0, 206, 207, 7, 11, 0, 0, 207, 208, 7,
+ 16, 0, 0, 208, 209, 7, 0, 0, 0, 209, 210, 7, 0, 0, 0, 210, 54, 1, 0, 0,
+ 0, 211, 212, 7, 6, 0, 0, 212, 213, 7, 12, 0, 0, 213, 214, 7, 20, 0, 0,
+ 214, 221, 7, 3, 0, 0, 215, 216, 7, 21, 0, 0, 216, 217, 7, 16, 0, 0, 217,
+ 218, 7, 0, 0, 0, 218, 219, 7, 11, 0, 0, 219, 221, 7, 3, 0, 0, 220, 211,
+ 1, 0, 0, 0, 220, 215, 1, 0, 0, 0, 221, 56, 1, 0, 0, 0, 222, 223, 7, 22,
+ 0, 0, 223, 58, 1, 0, 0, 0, 224, 226, 3, 57, 28, 0, 225, 224, 1, 0, 0, 0,
+ 225, 226, 1, 0, 0, 0, 226, 228, 1, 0, 0, 0, 227, 229, 3, 73, 36, 0, 228,
+ 227, 1, 0, 0, 0, 229, 230, 1, 0, 0, 0, 230, 228, 1, 0, 0, 0, 230, 231,
+ 1, 0, 0, 0, 231, 239, 1, 0, 0, 0, 232, 236, 5, 46, 0, 0, 233, 235, 3, 73,
+ 36, 0, 234, 233, 1, 0, 0, 0, 235, 238, 1, 0, 0, 0, 236, 234, 1, 0, 0, 0,
+ 236, 237, 1, 0, 0, 0, 237, 240, 1, 0, 0, 0, 238, 236, 1, 0, 0, 0, 239,
+ 232, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 250, 1, 0, 0, 0, 241, 243,
+ 7, 3, 0, 0, 242, 244, 3, 57, 28, 0, 243, 242, 1, 0, 0, 0, 243, 244, 1,
+ 0, 0, 0, 244, 246, 1, 0, 0, 0, 245, 247, 3, 73, 36, 0, 246, 245, 1, 0,
+ 0, 0, 247, 248, 1, 0, 0, 0, 248, 246, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0,
+ 249, 251, 1, 0, 0, 0, 250, 241, 1, 0, 0, 0, 250, 251, 1, 0, 0, 0, 251,
+ 273, 1, 0, 0, 0, 252, 254, 3, 57, 28, 0, 253, 252, 1, 0, 0, 0, 253, 254,
+ 1, 0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 257, 5, 46, 0, 0, 256, 258, 3, 73,
+ 36, 0, 257, 256, 1, 0, 0, 0, 258, 259, 1, 0, 0, 0, 259, 257, 1, 0, 0, 0,
+ 259, 260, 1, 0, 0, 0, 260, 270, 1, 0, 0, 0, 261, 263, 7, 3, 0, 0, 262,
+ 264, 3, 57, 28, 0, 263, 262, 1, 0, 0, 0, 263, 264, 1, 0, 0, 0, 264, 266,
+ 1, 0, 0, 0, 265, 267, 3, 73, 36, 0, 266, 265, 1, 0, 0, 0, 267, 268, 1,
+ 0, 0, 0, 268, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269, 271, 1, 0, 0,
+ 0, 270, 261, 1, 0, 0, 0, 270, 271, 1, 0, 0, 0, 271, 273, 1, 0, 0, 0, 272,
+ 225, 1, 0, 0, 0, 272, 253, 1, 0, 0, 0, 273, 60, 1, 0, 0, 0, 274, 280, 5,
+ 34, 0, 0, 275, 279, 8, 23, 0, 0, 276, 277, 5, 92, 0, 0, 277, 279, 9, 0,
+ 0, 0, 278, 275, 1, 0, 0, 0, 278, 276, 1, 0, 0, 0, 279, 282, 1, 0, 0, 0,
+ 280, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 283, 1, 0, 0, 0, 282,
+ 280, 1, 0, 0, 0, 283, 295, 5, 34, 0, 0, 284, 290, 5, 39, 0, 0, 285, 289,
+ 8, 24, 0, 0, 286, 287, 5, 92, 0, 0, 287, 289, 9, 0, 0, 0, 288, 285, 1,
+ 0, 0, 0, 288, 286, 1, 0, 0, 0, 289, 292, 1, 0, 0, 0, 290, 288, 1, 0, 0,
+ 0, 290, 291, 1, 0, 0, 0, 291, 293, 1, 0, 0, 0, 292, 290, 1, 0, 0, 0, 293,
+ 295, 5, 39, 0, 0, 294, 274, 1, 0, 0, 0, 294, 284, 1, 0, 0, 0, 295, 62,
+ 1, 0, 0, 0, 296, 300, 7, 25, 0, 0, 297, 299, 7, 26, 0, 0, 298, 297, 1,
+ 0, 0, 0, 299, 302, 1, 0, 0, 0, 300, 298, 1, 0, 0, 0, 300, 301, 1, 0, 0,
+ 0, 301, 64, 1, 0, 0, 0, 302, 300, 1, 0, 0, 0, 303, 304, 5, 91, 0, 0, 304,
+ 305, 5, 93, 0, 0, 305, 66, 1, 0, 0, 0, 306, 307, 5, 91, 0, 0, 307, 308,
+ 5, 42, 0, 0, 308, 309, 5, 93, 0, 0, 309, 68, 1, 0, 0, 0, 310, 317, 3, 63,
+ 31, 0, 311, 312, 5, 46, 0, 0, 312, 316, 3, 63, 31, 0, 313, 316, 3, 65,
+ 32, 0, 314, 316, 3, 67, 33, 0, 315, 311, 1, 0, 0, 0, 315, 313, 1, 0, 0,
+ 0, 315, 314, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 317,
+ 318, 1, 0, 0, 0, 318, 70, 1, 0, 0, 0, 319, 317, 1, 0, 0, 0, 320, 322, 7,
+ 27, 0, 0, 321, 320, 1, 0, 0, 0, 322, 323, 1, 0, 0, 0, 323, 321, 1, 0, 0,
+ 0, 323, 324, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 326, 6, 35, 0, 0, 326,
+ 72, 1, 0, 0, 0, 327, 328, 7, 28, 0, 0, 328, 74, 1, 0, 0, 0, 329, 331, 8,
+ 29, 0, 0, 330, 329, 1, 0, 0, 0, 331, 332, 1, 0, 0, 0, 332, 330, 1, 0, 0,
+ 0, 332, 333, 1, 0, 0, 0, 333, 76, 1, 0, 0, 0, 30, 0, 90, 119, 138, 160,
+ 177, 220, 225, 230, 236, 239, 243, 248, 250, 253, 259, 263, 268, 270, 272,
+ 278, 280, 288, 290, 294, 300, 315, 317, 323, 332, 1, 6, 0, 0,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
diff --git a/pkg/parser/grammar/filterquery_parser.go b/pkg/parser/grammar/filterquery_parser.go
index aa8285b63c..dfdc007568 100644
--- a/pkg/parser/grammar/filterquery_parser.go
+++ b/pkg/parser/grammar/filterquery_parser.go
@@ -51,97 +51,97 @@ func filterqueryParserInit() {
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
- 4, 1, 33, 212, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7,
+ 4, 1, 33, 213, 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, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15,
2, 16, 7, 16, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 5, 2, 43,
8, 2, 10, 2, 12, 2, 46, 9, 2, 1, 3, 1, 3, 1, 3, 1, 3, 5, 3, 52, 8, 3, 10,
3, 12, 3, 55, 9, 3, 1, 4, 3, 4, 58, 8, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5,
- 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 70, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1,
+ 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 71, 8, 5, 1, 6, 1, 6, 1, 6, 1,
6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1,
6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1,
6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1,
6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1,
6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1,
- 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 3,
- 6, 148, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1,
- 7, 3, 7, 160, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1,
- 8, 1, 8, 1, 8, 1, 8, 3, 8, 174, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 179, 8, 9,
- 10, 9, 12, 9, 182, 9, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11,
- 1, 12, 1, 12, 1, 12, 5, 12, 194, 8, 12, 10, 12, 12, 12, 197, 9, 12, 1,
- 13, 1, 13, 1, 13, 3, 13, 202, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14, 1, 15,
- 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18,
- 20, 22, 24, 26, 28, 30, 32, 0, 6, 1, 0, 7, 8, 2, 0, 13, 13, 15, 15, 2,
- 0, 14, 14, 16, 16, 2, 0, 30, 30, 33, 33, 1, 0, 25, 27, 1, 0, 28, 31, 225,
- 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 47, 1, 0, 0,
- 0, 8, 57, 1, 0, 0, 0, 10, 69, 1, 0, 0, 0, 12, 147, 1, 0, 0, 0, 14, 159,
- 1, 0, 0, 0, 16, 173, 1, 0, 0, 0, 18, 175, 1, 0, 0, 0, 20, 183, 1, 0, 0,
- 0, 22, 185, 1, 0, 0, 0, 24, 190, 1, 0, 0, 0, 26, 201, 1, 0, 0, 0, 28, 203,
- 1, 0, 0, 0, 30, 207, 1, 0, 0, 0, 32, 209, 1, 0, 0, 0, 34, 35, 3, 2, 1,
- 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 38, 3, 4, 2, 0, 38, 3, 1,
- 0, 0, 0, 39, 44, 3, 6, 3, 0, 40, 41, 5, 24, 0, 0, 41, 43, 3, 6, 3, 0, 42,
- 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0,
- 0, 45, 5, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 53, 3, 8, 4, 0, 48, 49, 5,
- 23, 0, 0, 49, 52, 3, 8, 4, 0, 50, 52, 3, 8, 4, 0, 51, 48, 1, 0, 0, 0, 51,
- 50, 1, 0, 0, 0, 52, 55, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0,
- 0, 54, 7, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 56, 58, 5, 22, 0, 0, 57, 56,
- 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0,
- 60, 9, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0, 63, 64, 5, 2,
- 0, 0, 64, 70, 1, 0, 0, 0, 65, 70, 3, 12, 6, 0, 66, 70, 3, 22, 11, 0, 67,
- 70, 3, 20, 10, 0, 68, 70, 3, 32, 16, 0, 69, 61, 1, 0, 0, 0, 69, 65, 1,
- 0, 0, 0, 69, 66, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 69, 68, 1, 0, 0, 0, 70,
- 11, 1, 0, 0, 0, 71, 72, 3, 32, 16, 0, 72, 73, 5, 6, 0, 0, 73, 74, 3, 30,
- 15, 0, 74, 148, 1, 0, 0, 0, 75, 76, 3, 32, 16, 0, 76, 77, 7, 0, 0, 0, 77,
- 78, 3, 30, 15, 0, 78, 148, 1, 0, 0, 0, 79, 80, 3, 32, 16, 0, 80, 81, 5,
- 9, 0, 0, 81, 82, 3, 30, 15, 0, 82, 148, 1, 0, 0, 0, 83, 84, 3, 32, 16,
- 0, 84, 85, 5, 10, 0, 0, 85, 86, 3, 30, 15, 0, 86, 148, 1, 0, 0, 0, 87,
- 88, 3, 32, 16, 0, 88, 89, 5, 11, 0, 0, 89, 90, 3, 30, 15, 0, 90, 148, 1,
- 0, 0, 0, 91, 92, 3, 32, 16, 0, 92, 93, 5, 12, 0, 0, 93, 94, 3, 30, 15,
- 0, 94, 148, 1, 0, 0, 0, 95, 96, 3, 32, 16, 0, 96, 97, 7, 1, 0, 0, 97, 98,
- 3, 30, 15, 0, 98, 148, 1, 0, 0, 0, 99, 100, 3, 32, 16, 0, 100, 101, 7,
- 2, 0, 0, 101, 102, 3, 30, 15, 0, 102, 148, 1, 0, 0, 0, 103, 104, 3, 32,
- 16, 0, 104, 105, 5, 17, 0, 0, 105, 106, 3, 30, 15, 0, 106, 107, 5, 23,
- 0, 0, 107, 108, 3, 30, 15, 0, 108, 148, 1, 0, 0, 0, 109, 110, 3, 32, 16,
- 0, 110, 111, 5, 22, 0, 0, 111, 112, 5, 17, 0, 0, 112, 113, 3, 30, 15, 0,
- 113, 114, 5, 23, 0, 0, 114, 115, 3, 30, 15, 0, 115, 148, 1, 0, 0, 0, 116,
- 117, 3, 32, 16, 0, 117, 118, 3, 14, 7, 0, 118, 148, 1, 0, 0, 0, 119, 120,
- 3, 32, 16, 0, 120, 121, 3, 16, 8, 0, 121, 148, 1, 0, 0, 0, 122, 123, 3,
- 32, 16, 0, 123, 124, 5, 18, 0, 0, 124, 148, 1, 0, 0, 0, 125, 126, 3, 32,
- 16, 0, 126, 127, 5, 22, 0, 0, 127, 128, 5, 18, 0, 0, 128, 148, 1, 0, 0,
- 0, 129, 130, 3, 32, 16, 0, 130, 131, 5, 19, 0, 0, 131, 132, 3, 30, 15,
- 0, 132, 148, 1, 0, 0, 0, 133, 134, 3, 32, 16, 0, 134, 135, 5, 22, 0, 0,
- 135, 136, 5, 19, 0, 0, 136, 137, 3, 30, 15, 0, 137, 148, 1, 0, 0, 0, 138,
- 139, 3, 32, 16, 0, 139, 140, 5, 20, 0, 0, 140, 141, 3, 30, 15, 0, 141,
- 148, 1, 0, 0, 0, 142, 143, 3, 32, 16, 0, 143, 144, 5, 22, 0, 0, 144, 145,
- 5, 20, 0, 0, 145, 146, 3, 30, 15, 0, 146, 148, 1, 0, 0, 0, 147, 71, 1,
- 0, 0, 0, 147, 75, 1, 0, 0, 0, 147, 79, 1, 0, 0, 0, 147, 83, 1, 0, 0, 0,
- 147, 87, 1, 0, 0, 0, 147, 91, 1, 0, 0, 0, 147, 95, 1, 0, 0, 0, 147, 99,
- 1, 0, 0, 0, 147, 103, 1, 0, 0, 0, 147, 109, 1, 0, 0, 0, 147, 116, 1, 0,
- 0, 0, 147, 119, 1, 0, 0, 0, 147, 122, 1, 0, 0, 0, 147, 125, 1, 0, 0, 0,
- 147, 129, 1, 0, 0, 0, 147, 133, 1, 0, 0, 0, 147, 138, 1, 0, 0, 0, 147,
- 142, 1, 0, 0, 0, 148, 13, 1, 0, 0, 0, 149, 150, 5, 21, 0, 0, 150, 151,
- 5, 1, 0, 0, 151, 152, 3, 18, 9, 0, 152, 153, 5, 2, 0, 0, 153, 160, 1, 0,
- 0, 0, 154, 155, 5, 21, 0, 0, 155, 156, 5, 3, 0, 0, 156, 157, 3, 18, 9,
- 0, 157, 158, 5, 4, 0, 0, 158, 160, 1, 0, 0, 0, 159, 149, 1, 0, 0, 0, 159,
- 154, 1, 0, 0, 0, 160, 15, 1, 0, 0, 0, 161, 162, 5, 22, 0, 0, 162, 163,
- 5, 21, 0, 0, 163, 164, 5, 1, 0, 0, 164, 165, 3, 18, 9, 0, 165, 166, 5,
- 2, 0, 0, 166, 174, 1, 0, 0, 0, 167, 168, 5, 22, 0, 0, 168, 169, 5, 21,
- 0, 0, 169, 170, 5, 3, 0, 0, 170, 171, 3, 18, 9, 0, 171, 172, 5, 4, 0, 0,
- 172, 174, 1, 0, 0, 0, 173, 161, 1, 0, 0, 0, 173, 167, 1, 0, 0, 0, 174,
- 17, 1, 0, 0, 0, 175, 180, 3, 30, 15, 0, 176, 177, 5, 5, 0, 0, 177, 179,
- 3, 30, 15, 0, 178, 176, 1, 0, 0, 0, 179, 182, 1, 0, 0, 0, 180, 178, 1,
- 0, 0, 0, 180, 181, 1, 0, 0, 0, 181, 19, 1, 0, 0, 0, 182, 180, 1, 0, 0,
- 0, 183, 184, 7, 3, 0, 0, 184, 21, 1, 0, 0, 0, 185, 186, 7, 4, 0, 0, 186,
- 187, 5, 1, 0, 0, 187, 188, 3, 24, 12, 0, 188, 189, 5, 2, 0, 0, 189, 23,
- 1, 0, 0, 0, 190, 195, 3, 26, 13, 0, 191, 192, 5, 5, 0, 0, 192, 194, 3,
- 26, 13, 0, 193, 191, 1, 0, 0, 0, 194, 197, 1, 0, 0, 0, 195, 193, 1, 0,
- 0, 0, 195, 196, 1, 0, 0, 0, 196, 25, 1, 0, 0, 0, 197, 195, 1, 0, 0, 0,
- 198, 202, 3, 32, 16, 0, 199, 202, 3, 30, 15, 0, 200, 202, 3, 28, 14, 0,
- 201, 198, 1, 0, 0, 0, 201, 199, 1, 0, 0, 0, 201, 200, 1, 0, 0, 0, 202,
- 27, 1, 0, 0, 0, 203, 204, 5, 3, 0, 0, 204, 205, 3, 18, 9, 0, 205, 206,
- 5, 4, 0, 0, 206, 29, 1, 0, 0, 0, 207, 208, 7, 5, 0, 0, 208, 31, 1, 0, 0,
- 0, 209, 210, 5, 31, 0, 0, 210, 33, 1, 0, 0, 0, 11, 44, 51, 53, 57, 69,
- 147, 159, 173, 180, 195, 201,
+ 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1, 6, 1,
+ 6, 3, 6, 149, 8, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1,
+ 7, 1, 7, 3, 7, 161, 8, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1,
+ 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 175, 8, 8, 1, 9, 1, 9, 1, 9, 5, 9, 180,
+ 8, 9, 10, 9, 12, 9, 183, 9, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 11,
+ 1, 11, 1, 12, 1, 12, 1, 12, 5, 12, 195, 8, 12, 10, 12, 12, 12, 198, 9,
+ 12, 1, 13, 1, 13, 1, 13, 3, 13, 203, 8, 13, 1, 14, 1, 14, 1, 14, 1, 14,
+ 1, 15, 1, 15, 1, 16, 1, 16, 1, 16, 0, 0, 17, 0, 2, 4, 6, 8, 10, 12, 14,
+ 16, 18, 20, 22, 24, 26, 28, 30, 32, 0, 6, 1, 0, 7, 8, 2, 0, 13, 13, 15,
+ 15, 2, 0, 14, 14, 16, 16, 2, 0, 30, 30, 33, 33, 1, 0, 25, 27, 1, 0, 28,
+ 31, 227, 0, 34, 1, 0, 0, 0, 2, 37, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 47,
+ 1, 0, 0, 0, 8, 57, 1, 0, 0, 0, 10, 70, 1, 0, 0, 0, 12, 148, 1, 0, 0, 0,
+ 14, 160, 1, 0, 0, 0, 16, 174, 1, 0, 0, 0, 18, 176, 1, 0, 0, 0, 20, 184,
+ 1, 0, 0, 0, 22, 186, 1, 0, 0, 0, 24, 191, 1, 0, 0, 0, 26, 202, 1, 0, 0,
+ 0, 28, 204, 1, 0, 0, 0, 30, 208, 1, 0, 0, 0, 32, 210, 1, 0, 0, 0, 34, 35,
+ 3, 2, 1, 0, 35, 36, 5, 0, 0, 1, 36, 1, 1, 0, 0, 0, 37, 38, 3, 4, 2, 0,
+ 38, 3, 1, 0, 0, 0, 39, 44, 3, 6, 3, 0, 40, 41, 5, 24, 0, 0, 41, 43, 3,
+ 6, 3, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44,
+ 45, 1, 0, 0, 0, 45, 5, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 53, 3, 8, 4,
+ 0, 48, 49, 5, 23, 0, 0, 49, 52, 3, 8, 4, 0, 50, 52, 3, 8, 4, 0, 51, 48,
+ 1, 0, 0, 0, 51, 50, 1, 0, 0, 0, 52, 55, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0,
+ 53, 54, 1, 0, 0, 0, 54, 7, 1, 0, 0, 0, 55, 53, 1, 0, 0, 0, 56, 58, 5, 22,
+ 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 59, 1, 0, 0, 0, 59, 60,
+ 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 62, 5, 1, 0, 0, 62, 63, 3, 4, 2, 0,
+ 63, 64, 5, 2, 0, 0, 64, 71, 1, 0, 0, 0, 65, 71, 3, 12, 6, 0, 66, 71, 3,
+ 22, 11, 0, 67, 71, 3, 20, 10, 0, 68, 71, 3, 32, 16, 0, 69, 71, 3, 30, 15,
+ 0, 70, 61, 1, 0, 0, 0, 70, 65, 1, 0, 0, 0, 70, 66, 1, 0, 0, 0, 70, 67,
+ 1, 0, 0, 0, 70, 68, 1, 0, 0, 0, 70, 69, 1, 0, 0, 0, 71, 11, 1, 0, 0, 0,
+ 72, 73, 3, 32, 16, 0, 73, 74, 5, 6, 0, 0, 74, 75, 3, 30, 15, 0, 75, 149,
+ 1, 0, 0, 0, 76, 77, 3, 32, 16, 0, 77, 78, 7, 0, 0, 0, 78, 79, 3, 30, 15,
+ 0, 79, 149, 1, 0, 0, 0, 80, 81, 3, 32, 16, 0, 81, 82, 5, 9, 0, 0, 82, 83,
+ 3, 30, 15, 0, 83, 149, 1, 0, 0, 0, 84, 85, 3, 32, 16, 0, 85, 86, 5, 10,
+ 0, 0, 86, 87, 3, 30, 15, 0, 87, 149, 1, 0, 0, 0, 88, 89, 3, 32, 16, 0,
+ 89, 90, 5, 11, 0, 0, 90, 91, 3, 30, 15, 0, 91, 149, 1, 0, 0, 0, 92, 93,
+ 3, 32, 16, 0, 93, 94, 5, 12, 0, 0, 94, 95, 3, 30, 15, 0, 95, 149, 1, 0,
+ 0, 0, 96, 97, 3, 32, 16, 0, 97, 98, 7, 1, 0, 0, 98, 99, 3, 30, 15, 0, 99,
+ 149, 1, 0, 0, 0, 100, 101, 3, 32, 16, 0, 101, 102, 7, 2, 0, 0, 102, 103,
+ 3, 30, 15, 0, 103, 149, 1, 0, 0, 0, 104, 105, 3, 32, 16, 0, 105, 106, 5,
+ 17, 0, 0, 106, 107, 3, 30, 15, 0, 107, 108, 5, 23, 0, 0, 108, 109, 3, 30,
+ 15, 0, 109, 149, 1, 0, 0, 0, 110, 111, 3, 32, 16, 0, 111, 112, 5, 22, 0,
+ 0, 112, 113, 5, 17, 0, 0, 113, 114, 3, 30, 15, 0, 114, 115, 5, 23, 0, 0,
+ 115, 116, 3, 30, 15, 0, 116, 149, 1, 0, 0, 0, 117, 118, 3, 32, 16, 0, 118,
+ 119, 3, 14, 7, 0, 119, 149, 1, 0, 0, 0, 120, 121, 3, 32, 16, 0, 121, 122,
+ 3, 16, 8, 0, 122, 149, 1, 0, 0, 0, 123, 124, 3, 32, 16, 0, 124, 125, 5,
+ 18, 0, 0, 125, 149, 1, 0, 0, 0, 126, 127, 3, 32, 16, 0, 127, 128, 5, 22,
+ 0, 0, 128, 129, 5, 18, 0, 0, 129, 149, 1, 0, 0, 0, 130, 131, 3, 32, 16,
+ 0, 131, 132, 5, 19, 0, 0, 132, 133, 3, 30, 15, 0, 133, 149, 1, 0, 0, 0,
+ 134, 135, 3, 32, 16, 0, 135, 136, 5, 22, 0, 0, 136, 137, 5, 19, 0, 0, 137,
+ 138, 3, 30, 15, 0, 138, 149, 1, 0, 0, 0, 139, 140, 3, 32, 16, 0, 140, 141,
+ 5, 20, 0, 0, 141, 142, 3, 30, 15, 0, 142, 149, 1, 0, 0, 0, 143, 144, 3,
+ 32, 16, 0, 144, 145, 5, 22, 0, 0, 145, 146, 5, 20, 0, 0, 146, 147, 3, 30,
+ 15, 0, 147, 149, 1, 0, 0, 0, 148, 72, 1, 0, 0, 0, 148, 76, 1, 0, 0, 0,
+ 148, 80, 1, 0, 0, 0, 148, 84, 1, 0, 0, 0, 148, 88, 1, 0, 0, 0, 148, 92,
+ 1, 0, 0, 0, 148, 96, 1, 0, 0, 0, 148, 100, 1, 0, 0, 0, 148, 104, 1, 0,
+ 0, 0, 148, 110, 1, 0, 0, 0, 148, 117, 1, 0, 0, 0, 148, 120, 1, 0, 0, 0,
+ 148, 123, 1, 0, 0, 0, 148, 126, 1, 0, 0, 0, 148, 130, 1, 0, 0, 0, 148,
+ 134, 1, 0, 0, 0, 148, 139, 1, 0, 0, 0, 148, 143, 1, 0, 0, 0, 149, 13, 1,
+ 0, 0, 0, 150, 151, 5, 21, 0, 0, 151, 152, 5, 1, 0, 0, 152, 153, 3, 18,
+ 9, 0, 153, 154, 5, 2, 0, 0, 154, 161, 1, 0, 0, 0, 155, 156, 5, 21, 0, 0,
+ 156, 157, 5, 3, 0, 0, 157, 158, 3, 18, 9, 0, 158, 159, 5, 4, 0, 0, 159,
+ 161, 1, 0, 0, 0, 160, 150, 1, 0, 0, 0, 160, 155, 1, 0, 0, 0, 161, 15, 1,
+ 0, 0, 0, 162, 163, 5, 22, 0, 0, 163, 164, 5, 21, 0, 0, 164, 165, 5, 1,
+ 0, 0, 165, 166, 3, 18, 9, 0, 166, 167, 5, 2, 0, 0, 167, 175, 1, 0, 0, 0,
+ 168, 169, 5, 22, 0, 0, 169, 170, 5, 21, 0, 0, 170, 171, 5, 3, 0, 0, 171,
+ 172, 3, 18, 9, 0, 172, 173, 5, 4, 0, 0, 173, 175, 1, 0, 0, 0, 174, 162,
+ 1, 0, 0, 0, 174, 168, 1, 0, 0, 0, 175, 17, 1, 0, 0, 0, 176, 181, 3, 30,
+ 15, 0, 177, 178, 5, 5, 0, 0, 178, 180, 3, 30, 15, 0, 179, 177, 1, 0, 0,
+ 0, 180, 183, 1, 0, 0, 0, 181, 179, 1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182,
+ 19, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 184, 185, 7, 3, 0, 0, 185, 21, 1,
+ 0, 0, 0, 186, 187, 7, 4, 0, 0, 187, 188, 5, 1, 0, 0, 188, 189, 3, 24, 12,
+ 0, 189, 190, 5, 2, 0, 0, 190, 23, 1, 0, 0, 0, 191, 196, 3, 26, 13, 0, 192,
+ 193, 5, 5, 0, 0, 193, 195, 3, 26, 13, 0, 194, 192, 1, 0, 0, 0, 195, 198,
+ 1, 0, 0, 0, 196, 194, 1, 0, 0, 0, 196, 197, 1, 0, 0, 0, 197, 25, 1, 0,
+ 0, 0, 198, 196, 1, 0, 0, 0, 199, 203, 3, 32, 16, 0, 200, 203, 3, 30, 15,
+ 0, 201, 203, 3, 28, 14, 0, 202, 199, 1, 0, 0, 0, 202, 200, 1, 0, 0, 0,
+ 202, 201, 1, 0, 0, 0, 203, 27, 1, 0, 0, 0, 204, 205, 5, 3, 0, 0, 205, 206,
+ 3, 18, 9, 0, 206, 207, 5, 4, 0, 0, 207, 29, 1, 0, 0, 0, 208, 209, 7, 5,
+ 0, 0, 209, 31, 1, 0, 0, 0, 210, 211, 5, 31, 0, 0, 211, 33, 1, 0, 0, 0,
+ 11, 44, 51, 53, 57, 70, 148, 160, 174, 181, 196, 202,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
@@ -802,7 +802,7 @@ func (p *FilterQueryParser) AndExpression() (localctx IAndExpressionContext) {
}
_la = p.GetTokenStream().LA(1)
- for (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&12058624002) != 0 {
+ for (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&12863930370) != 0 {
p.SetState(51)
p.GetErrorHandler().Sync(p)
if p.HasError() {
@@ -824,7 +824,7 @@ func (p *FilterQueryParser) AndExpression() (localctx IAndExpressionContext) {
p.UnaryExpression()
}
- case FilterQueryParserLPAREN, FilterQueryParserNOT, FilterQueryParserHAS, FilterQueryParserHASANY, FilterQueryParserHASALL, FilterQueryParserQUOTED_TEXT, FilterQueryParserKEY, FilterQueryParserFREETEXT:
+ case FilterQueryParserLPAREN, FilterQueryParserNOT, FilterQueryParserHAS, FilterQueryParserHASANY, FilterQueryParserHASALL, FilterQueryParserBOOL, FilterQueryParserNUMBER, FilterQueryParserQUOTED_TEXT, FilterQueryParserKEY, FilterQueryParserFREETEXT:
{
p.SetState(50)
p.UnaryExpression()
@@ -1010,6 +1010,7 @@ type IPrimaryContext interface {
FunctionCall() IFunctionCallContext
FullText() IFullTextContext
Key() IKeyContext
+ Value() IValueContext
// IsPrimaryContext differentiates from other interfaces.
IsPrimaryContext()
@@ -1135,6 +1136,22 @@ func (s *PrimaryContext) Key() IKeyContext {
return t.(IKeyContext)
}
+func (s *PrimaryContext) Value() IValueContext {
+ var t antlr.RuleContext
+ for _, ctx := range s.GetChildren() {
+ if _, ok := ctx.(IValueContext); ok {
+ t = ctx.(antlr.RuleContext)
+ break
+ }
+ }
+
+ if t == nil {
+ return nil
+ }
+
+ return t.(IValueContext)
+}
+
func (s *PrimaryContext) GetRuleContext() antlr.RuleContext {
return s
}
@@ -1168,7 +1185,7 @@ func (s *PrimaryContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
func (p *FilterQueryParser) Primary() (localctx IPrimaryContext) {
localctx = NewPrimaryContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 10, FilterQueryParserRULE_primary)
- p.SetState(69)
+ p.SetState(70)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -1226,6 +1243,13 @@ func (p *FilterQueryParser) Primary() (localctx IPrimaryContext) {
p.Key()
}
+ case 6:
+ p.EnterOuterAlt(localctx, 6)
+ {
+ p.SetState(69)
+ p.Value()
+ }
+
case antlr.ATNInvalidAltNumber:
goto errorExit
}
@@ -1502,7 +1526,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
p.EnterRule(localctx, 12, FilterQueryParserRULE_comparison)
var _la int
- p.SetState(147)
+ p.SetState(148)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -1512,11 +1536,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
case 1:
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(71)
+ p.SetState(72)
p.Key()
}
{
- p.SetState(72)
+ p.SetState(73)
p.Match(FilterQueryParserEQUALS)
if p.HasError() {
// Recognition error - abort rule
@@ -1524,18 +1548,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(73)
+ p.SetState(74)
p.Value()
}
case 2:
p.EnterOuterAlt(localctx, 2)
{
- p.SetState(75)
+ p.SetState(76)
p.Key()
}
{
- p.SetState(76)
+ p.SetState(77)
_la = p.GetTokenStream().LA(1)
if !(_la == FilterQueryParserNOT_EQUALS || _la == FilterQueryParserNEQ) {
@@ -1546,18 +1570,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(77)
+ p.SetState(78)
p.Value()
}
case 3:
p.EnterOuterAlt(localctx, 3)
{
- p.SetState(79)
+ p.SetState(80)
p.Key()
}
{
- p.SetState(80)
+ p.SetState(81)
p.Match(FilterQueryParserLT)
if p.HasError() {
// Recognition error - abort rule
@@ -1565,18 +1589,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(81)
+ p.SetState(82)
p.Value()
}
case 4:
p.EnterOuterAlt(localctx, 4)
{
- p.SetState(83)
+ p.SetState(84)
p.Key()
}
{
- p.SetState(84)
+ p.SetState(85)
p.Match(FilterQueryParserLE)
if p.HasError() {
// Recognition error - abort rule
@@ -1584,18 +1608,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(85)
+ p.SetState(86)
p.Value()
}
case 5:
p.EnterOuterAlt(localctx, 5)
{
- p.SetState(87)
+ p.SetState(88)
p.Key()
}
{
- p.SetState(88)
+ p.SetState(89)
p.Match(FilterQueryParserGT)
if p.HasError() {
// Recognition error - abort rule
@@ -1603,18 +1627,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(89)
+ p.SetState(90)
p.Value()
}
case 6:
p.EnterOuterAlt(localctx, 6)
{
- p.SetState(91)
+ p.SetState(92)
p.Key()
}
{
- p.SetState(92)
+ p.SetState(93)
p.Match(FilterQueryParserGE)
if p.HasError() {
// Recognition error - abort rule
@@ -1622,18 +1646,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(93)
+ p.SetState(94)
p.Value()
}
case 7:
p.EnterOuterAlt(localctx, 7)
{
- p.SetState(95)
+ p.SetState(96)
p.Key()
}
{
- p.SetState(96)
+ p.SetState(97)
_la = p.GetTokenStream().LA(1)
if !(_la == FilterQueryParserLIKE || _la == FilterQueryParserILIKE) {
@@ -1644,18 +1668,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(97)
+ p.SetState(98)
p.Value()
}
case 8:
p.EnterOuterAlt(localctx, 8)
{
- p.SetState(99)
+ p.SetState(100)
p.Key()
}
{
- p.SetState(100)
+ p.SetState(101)
_la = p.GetTokenStream().LA(1)
if !(_la == FilterQueryParserNOT_LIKE || _la == FilterQueryParserNOT_ILIKE) {
@@ -1666,18 +1690,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(101)
+ p.SetState(102)
p.Value()
}
case 9:
p.EnterOuterAlt(localctx, 9)
{
- p.SetState(103)
+ p.SetState(104)
p.Key()
}
{
- p.SetState(104)
+ p.SetState(105)
p.Match(FilterQueryParserBETWEEN)
if p.HasError() {
// Recognition error - abort rule
@@ -1685,11 +1709,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(105)
+ p.SetState(106)
p.Value()
}
{
- p.SetState(106)
+ p.SetState(107)
p.Match(FilterQueryParserAND)
if p.HasError() {
// Recognition error - abort rule
@@ -1697,18 +1721,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(107)
+ p.SetState(108)
p.Value()
}
case 10:
p.EnterOuterAlt(localctx, 10)
{
- p.SetState(109)
+ p.SetState(110)
p.Key()
}
{
- p.SetState(110)
+ p.SetState(111)
p.Match(FilterQueryParserNOT)
if p.HasError() {
// Recognition error - abort rule
@@ -1716,7 +1740,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(111)
+ p.SetState(112)
p.Match(FilterQueryParserBETWEEN)
if p.HasError() {
// Recognition error - abort rule
@@ -1724,11 +1748,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(112)
+ p.SetState(113)
p.Value()
}
{
- p.SetState(113)
+ p.SetState(114)
p.Match(FilterQueryParserAND)
if p.HasError() {
// Recognition error - abort rule
@@ -1736,40 +1760,40 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(114)
+ p.SetState(115)
p.Value()
}
case 11:
p.EnterOuterAlt(localctx, 11)
{
- p.SetState(116)
+ p.SetState(117)
p.Key()
}
{
- p.SetState(117)
+ p.SetState(118)
p.InClause()
}
case 12:
p.EnterOuterAlt(localctx, 12)
{
- p.SetState(119)
+ p.SetState(120)
p.Key()
}
{
- p.SetState(120)
+ p.SetState(121)
p.NotInClause()
}
case 13:
p.EnterOuterAlt(localctx, 13)
{
- p.SetState(122)
+ p.SetState(123)
p.Key()
}
{
- p.SetState(123)
+ p.SetState(124)
p.Match(FilterQueryParserEXISTS)
if p.HasError() {
// Recognition error - abort rule
@@ -1780,11 +1804,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
case 14:
p.EnterOuterAlt(localctx, 14)
{
- p.SetState(125)
+ p.SetState(126)
p.Key()
}
{
- p.SetState(126)
+ p.SetState(127)
p.Match(FilterQueryParserNOT)
if p.HasError() {
// Recognition error - abort rule
@@ -1792,7 +1816,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(127)
+ p.SetState(128)
p.Match(FilterQueryParserEXISTS)
if p.HasError() {
// Recognition error - abort rule
@@ -1803,11 +1827,11 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
case 15:
p.EnterOuterAlt(localctx, 15)
{
- p.SetState(129)
+ p.SetState(130)
p.Key()
}
{
- p.SetState(130)
+ p.SetState(131)
p.Match(FilterQueryParserREGEXP)
if p.HasError() {
// Recognition error - abort rule
@@ -1815,27 +1839,19 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(131)
+ p.SetState(132)
p.Value()
}
case 16:
p.EnterOuterAlt(localctx, 16)
{
- p.SetState(133)
+ p.SetState(134)
p.Key()
}
- {
- p.SetState(134)
- p.Match(FilterQueryParserNOT)
- if p.HasError() {
- // Recognition error - abort rule
- goto errorExit
- }
- }
{
p.SetState(135)
- p.Match(FilterQueryParserREGEXP)
+ p.Match(FilterQueryParserNOT)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
@@ -1843,17 +1859,25 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
{
p.SetState(136)
+ p.Match(FilterQueryParserREGEXP)
+ if p.HasError() {
+ // Recognition error - abort rule
+ goto errorExit
+ }
+ }
+ {
+ p.SetState(137)
p.Value()
}
case 17:
p.EnterOuterAlt(localctx, 17)
{
- p.SetState(138)
+ p.SetState(139)
p.Key()
}
{
- p.SetState(139)
+ p.SetState(140)
p.Match(FilterQueryParserCONTAINS)
if p.HasError() {
// Recognition error - abort rule
@@ -1861,18 +1885,18 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(140)
+ p.SetState(141)
p.Value()
}
case 18:
p.EnterOuterAlt(localctx, 18)
{
- p.SetState(142)
+ p.SetState(143)
p.Key()
}
{
- p.SetState(143)
+ p.SetState(144)
p.Match(FilterQueryParserNOT)
if p.HasError() {
// Recognition error - abort rule
@@ -1880,7 +1904,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(144)
+ p.SetState(145)
p.Match(FilterQueryParserCONTAINS)
if p.HasError() {
// Recognition error - abort rule
@@ -1888,7 +1912,7 @@ func (p *FilterQueryParser) Comparison() (localctx IComparisonContext) {
}
}
{
- p.SetState(145)
+ p.SetState(146)
p.Value()
}
@@ -2029,7 +2053,7 @@ func (s *InClauseContext) Accept(visitor antlr.ParseTreeVisitor) interface{} {
func (p *FilterQueryParser) InClause() (localctx IInClauseContext) {
localctx = NewInClauseContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 14, FilterQueryParserRULE_inClause)
- p.SetState(159)
+ p.SetState(160)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -2039,7 +2063,7 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) {
case 1:
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(149)
+ p.SetState(150)
p.Match(FilterQueryParserIN)
if p.HasError() {
// Recognition error - abort rule
@@ -2047,7 +2071,7 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) {
}
}
{
- p.SetState(150)
+ p.SetState(151)
p.Match(FilterQueryParserLPAREN)
if p.HasError() {
// Recognition error - abort rule
@@ -2055,11 +2079,11 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) {
}
}
{
- p.SetState(151)
+ p.SetState(152)
p.ValueList()
}
{
- p.SetState(152)
+ p.SetState(153)
p.Match(FilterQueryParserRPAREN)
if p.HasError() {
// Recognition error - abort rule
@@ -2070,7 +2094,7 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) {
case 2:
p.EnterOuterAlt(localctx, 2)
{
- p.SetState(154)
+ p.SetState(155)
p.Match(FilterQueryParserIN)
if p.HasError() {
// Recognition error - abort rule
@@ -2078,7 +2102,7 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) {
}
}
{
- p.SetState(155)
+ p.SetState(156)
p.Match(FilterQueryParserLBRACK)
if p.HasError() {
// Recognition error - abort rule
@@ -2086,11 +2110,11 @@ func (p *FilterQueryParser) InClause() (localctx IInClauseContext) {
}
}
{
- p.SetState(156)
+ p.SetState(157)
p.ValueList()
}
{
- p.SetState(157)
+ p.SetState(158)
p.Match(FilterQueryParserRBRACK)
if p.HasError() {
// Recognition error - abort rule
@@ -2240,7 +2264,7 @@ func (s *NotInClauseContext) Accept(visitor antlr.ParseTreeVisitor) interface{}
func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
localctx = NewNotInClauseContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 16, FilterQueryParserRULE_notInClause)
- p.SetState(173)
+ p.SetState(174)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -2250,7 +2274,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
case 1:
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(161)
+ p.SetState(162)
p.Match(FilterQueryParserNOT)
if p.HasError() {
// Recognition error - abort rule
@@ -2258,7 +2282,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
}
}
{
- p.SetState(162)
+ p.SetState(163)
p.Match(FilterQueryParserIN)
if p.HasError() {
// Recognition error - abort rule
@@ -2266,7 +2290,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
}
}
{
- p.SetState(163)
+ p.SetState(164)
p.Match(FilterQueryParserLPAREN)
if p.HasError() {
// Recognition error - abort rule
@@ -2274,11 +2298,11 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
}
}
{
- p.SetState(164)
+ p.SetState(165)
p.ValueList()
}
{
- p.SetState(165)
+ p.SetState(166)
p.Match(FilterQueryParserRPAREN)
if p.HasError() {
// Recognition error - abort rule
@@ -2289,7 +2313,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
case 2:
p.EnterOuterAlt(localctx, 2)
{
- p.SetState(167)
+ p.SetState(168)
p.Match(FilterQueryParserNOT)
if p.HasError() {
// Recognition error - abort rule
@@ -2297,7 +2321,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
}
}
{
- p.SetState(168)
+ p.SetState(169)
p.Match(FilterQueryParserIN)
if p.HasError() {
// Recognition error - abort rule
@@ -2305,7 +2329,7 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
}
}
{
- p.SetState(169)
+ p.SetState(170)
p.Match(FilterQueryParserLBRACK)
if p.HasError() {
// Recognition error - abort rule
@@ -2313,11 +2337,11 @@ func (p *FilterQueryParser) NotInClause() (localctx INotInClauseContext) {
}
}
{
- p.SetState(170)
+ p.SetState(171)
p.ValueList()
}
{
- p.SetState(171)
+ p.SetState(172)
p.Match(FilterQueryParserRBRACK)
if p.HasError() {
// Recognition error - abort rule
@@ -2477,10 +2501,10 @@ func (p *FilterQueryParser) ValueList() (localctx IValueListContext) {
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(175)
+ p.SetState(176)
p.Value()
}
- p.SetState(180)
+ p.SetState(181)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -2489,7 +2513,7 @@ func (p *FilterQueryParser) ValueList() (localctx IValueListContext) {
for _la == FilterQueryParserCOMMA {
{
- p.SetState(176)
+ p.SetState(177)
p.Match(FilterQueryParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
@@ -2497,11 +2521,11 @@ func (p *FilterQueryParser) ValueList() (localctx IValueListContext) {
}
}
{
- p.SetState(177)
+ p.SetState(178)
p.Value()
}
- p.SetState(182)
+ p.SetState(183)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -2614,7 +2638,7 @@ func (p *FilterQueryParser) FullText() (localctx IFullTextContext) {
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(183)
+ p.SetState(184)
_la = p.GetTokenStream().LA(1)
if !(_la == FilterQueryParserQUOTED_TEXT || _la == FilterQueryParserFREETEXT) {
@@ -2762,7 +2786,7 @@ func (p *FilterQueryParser) FunctionCall() (localctx IFunctionCallContext) {
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(185)
+ p.SetState(186)
_la = p.GetTokenStream().LA(1)
if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&234881024) != 0) {
@@ -2773,7 +2797,7 @@ func (p *FilterQueryParser) FunctionCall() (localctx IFunctionCallContext) {
}
}
{
- p.SetState(186)
+ p.SetState(187)
p.Match(FilterQueryParserLPAREN)
if p.HasError() {
// Recognition error - abort rule
@@ -2781,11 +2805,11 @@ func (p *FilterQueryParser) FunctionCall() (localctx IFunctionCallContext) {
}
}
{
- p.SetState(187)
+ p.SetState(188)
p.FunctionParamList()
}
{
- p.SetState(188)
+ p.SetState(189)
p.Match(FilterQueryParserRPAREN)
if p.HasError() {
// Recognition error - abort rule
@@ -2941,10 +2965,10 @@ func (p *FilterQueryParser) FunctionParamList() (localctx IFunctionParamListCont
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(190)
+ p.SetState(191)
p.FunctionParam()
}
- p.SetState(195)
+ p.SetState(196)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -2953,7 +2977,7 @@ func (p *FilterQueryParser) FunctionParamList() (localctx IFunctionParamListCont
for _la == FilterQueryParserCOMMA {
{
- p.SetState(191)
+ p.SetState(192)
p.Match(FilterQueryParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
@@ -2961,11 +2985,11 @@ func (p *FilterQueryParser) FunctionParamList() (localctx IFunctionParamListCont
}
}
{
- p.SetState(192)
+ p.SetState(193)
p.FunctionParam()
}
- p.SetState(197)
+ p.SetState(198)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -3115,7 +3139,7 @@ func (s *FunctionParamContext) Accept(visitor antlr.ParseTreeVisitor) interface{
func (p *FilterQueryParser) FunctionParam() (localctx IFunctionParamContext) {
localctx = NewFunctionParamContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 26, FilterQueryParserRULE_functionParam)
- p.SetState(201)
+ p.SetState(202)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@@ -3125,21 +3149,21 @@ func (p *FilterQueryParser) FunctionParam() (localctx IFunctionParamContext) {
case 1:
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(198)
+ p.SetState(199)
p.Key()
}
case 2:
p.EnterOuterAlt(localctx, 2)
{
- p.SetState(199)
+ p.SetState(200)
p.Value()
}
case 3:
p.EnterOuterAlt(localctx, 3)
{
- p.SetState(200)
+ p.SetState(201)
p.Array()
}
@@ -3267,7 +3291,7 @@ func (p *FilterQueryParser) Array() (localctx IArrayContext) {
p.EnterRule(localctx, 28, FilterQueryParserRULE_array)
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(203)
+ p.SetState(204)
p.Match(FilterQueryParserLBRACK)
if p.HasError() {
// Recognition error - abort rule
@@ -3275,11 +3299,11 @@ func (p *FilterQueryParser) Array() (localctx IArrayContext) {
}
}
{
- p.SetState(204)
+ p.SetState(205)
p.ValueList()
}
{
- p.SetState(205)
+ p.SetState(206)
p.Match(FilterQueryParserRBRACK)
if p.HasError() {
// Recognition error - abort rule
@@ -3402,7 +3426,7 @@ func (p *FilterQueryParser) Value() (localctx IValueContext) {
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(207)
+ p.SetState(208)
_la = p.GetTokenStream().LA(1)
if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&4026531840) != 0) {
@@ -3511,7 +3535,7 @@ func (p *FilterQueryParser) Key() (localctx IKeyContext) {
p.EnterRule(localctx, 32, FilterQueryParserRULE_key)
p.EnterOuterAlt(localctx, 1)
{
- p.SetState(209)
+ p.SetState(210)
p.Match(FilterQueryParserKEY)
if p.HasError() {
// Recognition error - abort rule
diff --git a/pkg/querybuilder/agg_funcs.go b/pkg/querybuilder/agg_funcs.go
new file mode 100644
index 0000000000..80279c5a70
--- /dev/null
+++ b/pkg/querybuilder/agg_funcs.go
@@ -0,0 +1,281 @@
+package querybuilder
+
+import (
+ "github.com/SigNoz/signoz/pkg/valuer"
+)
+
+var (
+ AggreFuncMap = map[valuer.String]AggrFunc{}
+)
+
+type AggrFunc struct {
+ Name valuer.String
+ FuncName string
+ Aliases []valuer.String
+ RequireArgs bool
+ FuncCombinator bool
+ Rate bool
+ MinArgs int
+ MaxArgs int
+}
+
+var (
+ AggrFuncCount = AggrFunc{
+ Name: valuer.NewString("count"),
+ FuncName: "count",
+ RequireArgs: false, MinArgs: 0, MaxArgs: 1,
+ }
+ AggrFuncCountIf = AggrFunc{
+ Name: valuer.NewString("countif"),
+ FuncName: "countIf",
+ Aliases: []valuer.String{valuer.NewString("count_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncCountDistinct = AggrFunc{
+ Name: valuer.NewString("countdistinct"),
+ FuncName: "countDistinct",
+ Aliases: []valuer.String{valuer.NewString("count_distinct")},
+ RequireArgs: true, MinArgs: 1, MaxArgs: 10,
+ }
+ AggrFuncCountDistinctIf = AggrFunc{
+ Name: valuer.NewString("countdistinctif"),
+ FuncName: "countDistinctIf",
+ Aliases: []valuer.String{valuer.NewString("count_distinct_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncSum = AggrFunc{
+ Name: valuer.NewString("sum"),
+ FuncName: "sum",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncSumIf = AggrFunc{
+ Name: valuer.NewString("sumif"),
+ FuncName: "sumIf",
+ Aliases: []valuer.String{valuer.NewString("sum_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncAvg = AggrFunc{
+ Name: valuer.NewString("avg"),
+ FuncName: "avg",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncAvgIf = AggrFunc{
+ Name: valuer.NewString("avgif"),
+ FuncName: "avgIf",
+ Aliases: []valuer.String{valuer.NewString("avg_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncMin = AggrFunc{
+ Name: valuer.NewString("min"),
+ FuncName: "min",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncMinIf = AggrFunc{
+ Name: valuer.NewString("minif"),
+ FuncName: "minIf",
+ Aliases: []valuer.String{valuer.NewString("min_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncMax = AggrFunc{
+ Name: valuer.NewString("max"),
+ FuncName: "max",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncMaxIf = AggrFunc{
+ Name: valuer.NewString("maxif"),
+ FuncName: "maxIf",
+ Aliases: []valuer.String{valuer.NewString("max_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP05 = AggrFunc{
+ Name: valuer.NewString("p05"),
+ FuncName: "quantile(0.05)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP05IF = AggrFunc{
+ Name: valuer.NewString("p05if"),
+ FuncName: "quantileIf(0.05)",
+ Aliases: []valuer.String{valuer.NewString("p05_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP10 = AggrFunc{
+ Name: valuer.NewString("p10"),
+ FuncName: "quantile(0.10)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP10IF = AggrFunc{
+ Name: valuer.NewString("p10if"),
+ FuncName: "quantileIf(0.10)",
+ Aliases: []valuer.String{valuer.NewString("p10_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP20 = AggrFunc{
+ Name: valuer.NewString("p20"),
+ FuncName: "quantile(0.20)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP20IF = AggrFunc{
+ Name: valuer.NewString("p20if"),
+ FuncName: "quantileIf(0.20)",
+ Aliases: []valuer.String{valuer.NewString("p20_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP25 = AggrFunc{
+ Name: valuer.NewString("p25"),
+ FuncName: "quantile(0.25)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP25IF = AggrFunc{
+ Name: valuer.NewString("p25if"),
+ FuncName: "quantileIf(0.25)",
+ Aliases: []valuer.String{valuer.NewString("p25_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP50 = AggrFunc{
+ Name: valuer.NewString("p50"),
+ FuncName: "quantile(0.50)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP50IF = AggrFunc{
+ Name: valuer.NewString("p50if"),
+ FuncName: "quantileIf(0.50)",
+ Aliases: []valuer.String{valuer.NewString("p50_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP75 = AggrFunc{
+ Name: valuer.NewString("p75"),
+ FuncName: "quantile(0.75)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP75IF = AggrFunc{
+ Name: valuer.NewString("p75if"),
+ FuncName: "quantileIf(0.75)",
+ Aliases: []valuer.String{valuer.NewString("p75_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP90 = AggrFunc{
+ Name: valuer.NewString("p90"),
+ FuncName: "quantile(0.90)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP90IF = AggrFunc{
+ Name: valuer.NewString("p90if"),
+ FuncName: "quantileIf(0.90)",
+ Aliases: []valuer.String{valuer.NewString("p90_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP95 = AggrFunc{
+ Name: valuer.NewString("p95"),
+ FuncName: "quantile(0.95)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP95IF = AggrFunc{
+ Name: valuer.NewString("p95if"),
+ FuncName: "quantileIf(0.95)",
+ Aliases: []valuer.String{valuer.NewString("p95_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP99 = AggrFunc{
+ Name: valuer.NewString("p99"),
+ FuncName: "quantile(0.99)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP99IF = AggrFunc{
+ Name: valuer.NewString("p99if"),
+ FuncName: "quantileIf(0.99)",
+ Aliases: []valuer.String{valuer.NewString("p99_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncP999 = AggrFunc{
+ Name: valuer.NewString("p999"),
+ FuncName: "quantile(0.999)",
+ RequireArgs: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncP999IF = AggrFunc{
+ Name: valuer.NewString("p999if"),
+ FuncName: "quantileIf(0.999)",
+ Aliases: []valuer.String{valuer.NewString("p999_if")},
+ RequireArgs: true, FuncCombinator: true, MinArgs: 2, MaxArgs: 2,
+ }
+ AggrFuncRate = AggrFunc{
+ Name: valuer.NewString("rate"),
+ FuncName: "count",
+ RequireArgs: true, Rate: true, MinArgs: 0, MaxArgs: 1,
+ }
+ AggrFuncRateIf = AggrFunc{
+ Name: valuer.NewString("rateif"),
+ FuncName: "count",
+ Aliases: []valuer.String{valuer.NewString("rate_if")},
+ RequireArgs: true, Rate: true, FuncCombinator: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncRateSum = AggrFunc{
+ Name: valuer.NewString("rate_sum"),
+ FuncName: "sum",
+ RequireArgs: true, Rate: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncRateAvg = AggrFunc{
+ Name: valuer.NewString("rate_avg"),
+ FuncName: "avg",
+ RequireArgs: true, Rate: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncRateMin = AggrFunc{
+ Name: valuer.NewString("rate_min"),
+ FuncName: "min",
+ RequireArgs: true, Rate: true, MinArgs: 1, MaxArgs: 1,
+ }
+ AggrFuncRateMax = AggrFunc{
+ Name: valuer.NewString("rate_max"),
+ FuncName: "max",
+ RequireArgs: true, Rate: true, MinArgs: 1, MaxArgs: 1,
+ }
+)
+
+func init() {
+ var aggFuncs = []AggrFunc{
+ AggrFuncCount,
+ AggrFuncCountIf,
+ AggrFuncCountDistinct,
+ AggrFuncCountDistinctIf,
+ AggrFuncSum,
+ AggrFuncSumIf,
+ AggrFuncAvg,
+ AggrFuncAvgIf,
+ AggrFuncMin,
+ AggrFuncMinIf,
+ AggrFuncMax,
+ AggrFuncMaxIf,
+ AggrFuncP05,
+ AggrFuncP05IF,
+ AggrFuncP10,
+ AggrFuncP10IF,
+ AggrFuncP20,
+ AggrFuncP20IF,
+ AggrFuncP25,
+ AggrFuncP25IF,
+ AggrFuncP50,
+ AggrFuncP50IF,
+ AggrFuncP75,
+ AggrFuncP75IF,
+ AggrFuncP90,
+ AggrFuncP90IF,
+ AggrFuncP95,
+ AggrFuncP95IF,
+ AggrFuncP99,
+ AggrFuncP99IF,
+ AggrFuncP999,
+ AggrFuncP999IF,
+ AggrFuncRate,
+ AggrFuncRateIf,
+ AggrFuncRateSum,
+ AggrFuncRateAvg,
+ AggrFuncRateMin,
+ AggrFuncRateMax,
+ }
+
+ for _, aggFunc := range aggFuncs {
+ AggreFuncMap[aggFunc.Name] = aggFunc
+ for _, alias := range aggFunc.Aliases {
+ AggreFuncMap[alias] = aggFunc
+ }
+ }
+}
diff --git a/pkg/querybuilder/agg_rewrite.go b/pkg/querybuilder/agg_rewrite.go
new file mode 100644
index 0000000000..ccf0d99040
--- /dev/null
+++ b/pkg/querybuilder/agg_rewrite.go
@@ -0,0 +1,245 @@
+package querybuilder
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ chparser "github.com/AfterShip/clickhouse-sql-parser/parser"
+ "github.com/SigNoz/signoz/pkg/errors"
+ qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
+ "github.com/SigNoz/signoz/pkg/types/telemetrytypes"
+ "github.com/SigNoz/signoz/pkg/valuer"
+ "github.com/huandu/go-sqlbuilder"
+)
+
+type AggExprRewriterOptions struct {
+ FieldKeys map[string][]*telemetrytypes.TelemetryFieldKey
+ FullTextColumn *telemetrytypes.TelemetryFieldKey
+ FieldMapper qbtypes.FieldMapper
+ ConditionBuilder qbtypes.ConditionBuilder
+ JsonBodyPrefix string
+ JsonKeyToKey qbtypes.JsonKeyToFieldFunc
+ RateInterval uint64
+}
+
+type aggExprRewriter struct {
+ opts AggExprRewriterOptions
+}
+
+func NewAggExprRewriter(opts AggExprRewriterOptions) *aggExprRewriter {
+ return &aggExprRewriter{opts: opts}
+}
+
+// Rewrite parses the given aggregation expression, maps the column, and condition to
+// valid data source column and condition expression, and returns the rewritten expression
+// and the args if the parametric aggregation function is used.
+func (r *aggExprRewriter) Rewrite(expr string) (string, []any, error) {
+ wrapped := fmt.Sprintf("SELECT %s", expr)
+ p := chparser.NewParser(wrapped)
+ stmts, err := p.ParseStmts()
+
+ if err != nil {
+ return "", nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to parse aggregation expression %q", expr)
+ }
+
+ if len(stmts) == 0 {
+ return "", nil, errors.NewInternalf(errors.CodeInternal, "no statements found for %q", expr)
+ }
+
+ sel, ok := stmts[0].(*chparser.SelectQuery)
+ if !ok {
+ return "", nil, errors.NewInternalf(errors.CodeInternal, "expected SelectQuery, got %T", stmts[0])
+ }
+
+ if len(sel.SelectItems) == 0 {
+ return "", nil, errors.NewInternalf(errors.CodeInternal, "no SELECT items for %q", expr)
+ }
+
+ visitor := newExprVisitor(r.opts.FieldKeys,
+ r.opts.FullTextColumn,
+ r.opts.FieldMapper,
+ r.opts.ConditionBuilder,
+ r.opts.JsonBodyPrefix,
+ r.opts.JsonKeyToKey,
+ )
+ // Rewrite the first select item (our expression)
+ if err := sel.SelectItems[0].Accept(visitor); err != nil {
+ return "", nil, err
+ }
+ // If nothing changed, return original
+ if !visitor.Modified {
+ return expr, nil, nil
+ }
+
+ if visitor.isRate {
+ return fmt.Sprintf("%s/%d", sel.SelectItems[0].String(), r.opts.RateInterval), visitor.chArgs, nil
+ }
+ return sel.SelectItems[0].String(), visitor.chArgs, nil
+}
+
+// RewriteMultiple rewrites a slice of expressions.
+func (r *aggExprRewriter) RewriteMultiple(
+ exprs []string,
+) ([]string, [][]any, error) {
+ out := make([]string, len(exprs))
+ var errs []error
+ var chArgsList [][]any
+ for i, e := range exprs {
+ w, chArgs, err := r.Rewrite(e)
+ if err != nil {
+ errs = append(errs, err)
+ out[i] = e
+ } else {
+ out[i] = w
+ chArgsList = append(chArgsList, chArgs)
+ }
+ }
+ if len(errs) > 0 {
+ return out, nil, errors.Join(errs...)
+ }
+ return out, chArgsList, nil
+}
+
+// exprVisitor walks FunctionExpr nodes and applies the mappers.
+type exprVisitor struct {
+ chparser.DefaultASTVisitor
+ fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey
+ fullTextColumn *telemetrytypes.TelemetryFieldKey
+ fieldMapper qbtypes.FieldMapper
+ conditionBuilder qbtypes.ConditionBuilder
+ jsonBodyPrefix string
+ jsonKeyToKey qbtypes.JsonKeyToFieldFunc
+ Modified bool
+ chArgs []any
+ isRate bool
+}
+
+func newExprVisitor(
+ fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey,
+ fullTextColumn *telemetrytypes.TelemetryFieldKey,
+ fieldMapper qbtypes.FieldMapper,
+ conditionBuilder qbtypes.ConditionBuilder,
+ jsonBodyPrefix string,
+ jsonKeyToKey qbtypes.JsonKeyToFieldFunc,
+) *exprVisitor {
+ return &exprVisitor{
+ fieldKeys: fieldKeys,
+ fullTextColumn: fullTextColumn,
+ fieldMapper: fieldMapper,
+ conditionBuilder: conditionBuilder,
+ jsonBodyPrefix: jsonBodyPrefix,
+ jsonKeyToKey: jsonKeyToKey,
+ }
+}
+
+// VisitFunctionExpr is invoked for each function call in the AST.
+func (v *exprVisitor) VisitFunctionExpr(fn *chparser.FunctionExpr) error {
+ name := strings.ToLower(fn.Name.Name)
+
+ aggFunc, ok := AggreFuncMap[valuer.NewString(name)]
+ if !ok {
+ return nil
+ }
+
+ var args []chparser.Expr
+ if fn.Params != nil && fn.Params.Items != nil {
+ args = fn.Params.Items.Items
+ }
+
+ // if we know aggregation function, we must ensure that the number of arguments is correct
+ if aggFunc.RequireArgs {
+ if len(args) < aggFunc.MinArgs || len(args) > aggFunc.MaxArgs {
+ return errors.NewInternalf(errors.CodeInternal, "invalid number of arguments for %q: %d", name, len(args))
+ }
+ }
+ fn.Name.Name = aggFunc.FuncName
+ if aggFunc.Rate {
+ v.isRate = true
+ }
+
+ // Handle *If functions with predicate + values
+ if aggFunc.FuncCombinator {
+ // Map the predicate (last argument)
+ origPred := args[len(args)-1].String()
+ whereClause, _, err := PrepareWhereClause(
+ origPred,
+ FilterExprVisitorOpts{
+ FieldKeys: v.fieldKeys,
+ FieldMapper: v.fieldMapper,
+ ConditionBuilder: v.conditionBuilder,
+ FullTextColumn: v.fullTextColumn,
+ JsonBodyPrefix: v.jsonBodyPrefix,
+ JsonKeyToKey: v.jsonKeyToKey,
+ },
+ )
+ if err != nil {
+ return err
+ }
+
+ newPred, chArgs := whereClause.BuildWithFlavor(sqlbuilder.ClickHouse)
+ newPred = strings.TrimPrefix(newPred, "WHERE")
+ parsedPred, err := parseFragment(newPred)
+ if err != nil {
+ return err
+ }
+ args[len(args)-1] = parsedPred
+ v.Modified = true
+ v.chArgs = chArgs
+
+ // Map each value column argument
+ for i := 0; i < len(args)-1; i++ {
+ origVal := args[i].String()
+ colName, err := v.fieldMapper.ColumnExpressionFor(context.Background(), &telemetrytypes.TelemetryFieldKey{Name: origVal}, v.fieldKeys)
+ if err != nil {
+ return errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "failed to get table field name for %q", origVal)
+ }
+ newVal := colName
+ parsedVal, err := parseFragment(newVal)
+ if err != nil {
+ return err
+ }
+ args[i] = parsedVal
+ v.Modified = true
+ }
+ } else {
+ // Non-If functions: map every argument as a column/value
+ for i, arg := range args {
+ orig := arg.String()
+ colName, err := v.fieldMapper.ColumnExpressionFor(context.Background(), &telemetrytypes.TelemetryFieldKey{Name: orig}, v.fieldKeys)
+ if err != nil {
+ return errors.WrapInvalidInputf(err, errors.CodeInvalidInput, "failed to get table field name for %q", orig)
+ }
+ newCol := colName
+ parsed, err := parseFragment(newCol)
+ if err != nil {
+ return err
+ }
+ args[i] = parsed
+ v.Modified = true
+ }
+ if aggFunc.Rate {
+ v.Modified = true
+ }
+ }
+
+ return nil
+}
+
+// parseFragment parses a SQL expression fragment by wrapping in SELECT.
+func parseFragment(sql string) (chparser.Expr, error) {
+ wrapped := fmt.Sprintf("SELECT %s", sql)
+ p := chparser.NewParser(wrapped)
+ stmts, err := p.ParseStmts()
+ if err != nil {
+ return nil, errors.WrapInternalf(err, errors.CodeInternal, "failed to parse re-written expression %q", sql)
+ }
+ sel, ok := stmts[0].(*chparser.SelectQuery)
+ if !ok {
+ return nil, errors.NewInternalf(errors.CodeInternal, "unexpected statement type in re-written expression %q: %T", sql, stmts[0])
+ }
+ if len(sel.SelectItems) == 0 {
+ return nil, errors.NewInternalf(errors.CodeInternal, "no select items in re-written expression %q", sql)
+ }
+ return sel.SelectItems[0].Expr, nil
+}
diff --git a/pkg/querybuilder/filter_error_listener.go b/pkg/querybuilder/filter_error_listener.go
new file mode 100644
index 0000000000..ffa623a5f0
--- /dev/null
+++ b/pkg/querybuilder/filter_error_listener.go
@@ -0,0 +1,235 @@
+package querybuilder
+
+import (
+ "fmt"
+ "slices"
+ "sort"
+ "strings"
+
+ "github.com/antlr4-go/antlr/v4"
+)
+
+var skipTokens = []string{"WS", "COMMENT"}
+
+// friendly maps SYMBOLIC_NAME -> what the user should see.
+var friendly = map[string]string{
+ // punctuation & operators
+ "LPAREN": "(", "RPAREN": ")",
+ "LBRACK": "[", "RBRACK": "]",
+ "COMMA": ",",
+ "EQUALS": "=",
+ "NOT_EQUALS": "!=",
+ "NEQ": "<>",
+ "LT": "<", "LE": "<=",
+ "GT": ">", "GE": ">=",
+
+ // keywords / functions
+ "AND": "AND",
+ "OR": "OR",
+ "NOT": "NOT",
+ "LIKE": "LIKE", "ILIKE": "ILIKE",
+ "NOT_LIKE": "NOT LIKE", "NOT_ILIKE": "NOT ILIKE",
+ "BETWEEN": "BETWEEN", "IN": "IN", "EXISTS": "EXISTS",
+ "REGEXP": "REGEXP", "CONTAINS": "CONTAINS",
+ "HAS": "has()", "HASANY": "hasAny()", "HASALL": "hasAll()",
+
+ // literals / identifiers
+ "NUMBER": "number",
+ "BOOL": "boolean",
+ "QUOTED_TEXT": "quoted text",
+ "KEY": "field name (ex: service.name)",
+}
+
+// prettyToken returns the nicest human label for token type tType.
+//
+// Order of preference:
+// 1. hard-coded friendly table
+// 2. literal name from grammar e.g. "'('"
+// 3. symbolic name e.g. AND
+// 4. numeric fallback e.g. <34>
+func prettyToken(p antlr.Parser, tType int) (string, bool) {
+ if slices.Contains(skipTokens, tokenName(p, tType)) {
+ return "", false
+ }
+
+ // symbolic name -> friendly ?
+ syms := p.GetSymbolicNames()
+ if tType >= 0 && tType < len(syms) {
+ if nice, ok := friendly[syms[tType]]; ok {
+ return nice, true
+ }
+ }
+
+ // literal name (the quoted punctuation that ANTLR generates)
+ lits := p.GetLiteralNames()
+ if tType >= 0 && tType < len(lits) && lits[tType] != "" {
+ return lits[tType], true
+ }
+
+ // symbolic name as last resort (but hide WS, EOF, …)
+ if tType >= 0 && tType < len(syms) && syms[tType] != "" && syms[tType] != "WS" {
+ return syms[tType], true
+ }
+
+ return "", false // tell caller to skip this entry
+}
+
+type SyntaxErr struct {
+ Line, Col int
+ TokenTxt string // offending text (or EOF)
+ TokenType int // offending token type
+ Expected []string // token names the parser still expected
+ RuleStack []string
+ Msg string
+}
+
+func (e *SyntaxErr) Error() string {
+ exp := ""
+ if len(e.Expected) > 0 {
+ exp = "expecting one of {" + strings.Join(e.Expected, ", ") + "}" + " but got " + e.TokenTxt
+ }
+ return fmt.Sprintf("line %d:%d %s", e.Line, e.Col, exp)
+}
+
+type Ambiguity struct {
+ Text string // slice of raw input that was ambiguous
+ Alts string // e.g. "{1, 3}"
+ RStack []string
+}
+
+func (a *Ambiguity) Error() string {
+ return fmt.Sprintf("ambiguity: %s, alts: %s", a.Text, a.Alts)
+}
+
+type ErrorListener struct {
+ antlr.DefaultErrorListener
+
+ SyntaxErrors []*SyntaxErr
+ Ambigs []*Ambiguity
+}
+
+func NewErrorListener() *ErrorListener { return &ErrorListener{} }
+
+func (l *ErrorListener) SyntaxError(
+ rec antlr.Recognizer,
+ off any,
+ line, column int,
+ msg string,
+ e antlr.RecognitionException,
+) {
+ err := &SyntaxErr{Line: line, Col: column, Msg: msg}
+
+ if tok, ok := off.(antlr.Token); ok {
+ if tok.GetTokenType() == antlr.TokenEOF {
+ err.TokenTxt = "EOF"
+ err.TokenType = tok.GetTokenType()
+ } else {
+ err.TokenTxt = fmt.Sprintf("'%s'", tok.GetText())
+ err.TokenType = tok.GetTokenType()
+ }
+ }
+
+ if p, ok := rec.(antlr.Parser); ok {
+ set := p.GetExpectedTokens()
+
+ // Heuristic: if KEY appears in the expected set *alongside* any literal
+ // value tokens, we assume it stands for a bare value. Otherwise, it stands
+ // for a left‑hand identifier.
+ valueTokens := map[int]struct{}{
+ pGetTokenType(p, "QUOTED_TEXT"): {},
+ pGetTokenType(p, "NUMBER"): {},
+ pGetTokenType(p, "BOOL"): {},
+ }
+ hasValueLiterals := false
+ for _, iv := range set.GetIntervals() {
+ for t := iv.Start; t <= iv.Stop; t++ {
+ if _, ok := valueTokens[t]; ok {
+ hasValueLiterals = true
+ break
+ }
+ }
+ if hasValueLiterals {
+ break
+ }
+ }
+
+ uniq := map[string]struct{}{}
+ for _, iv := range set.GetIntervals() {
+ for t := iv.Start; t <= iv.Stop; t++ {
+ sym := tokenName(p, t)
+ if sym == "KEY" {
+ if !hasValueLiterals {
+ uniq["field name (ex: service.name)"] = struct{}{}
+ }
+ continue
+ }
+ if label, ok := prettyToken(p, t); ok {
+ uniq[label] = struct{}{}
+ }
+ }
+ }
+
+ err.Expected = make([]string, 0, len(uniq))
+ for k := range uniq {
+ err.Expected = append(err.Expected, k)
+ }
+ sort.Strings(err.Expected)
+ err.RuleStack = p.GetRuleInvocationStack(nil)
+ }
+
+ l.SyntaxErrors = append(l.SyntaxErrors, err)
+}
+
+func (l *ErrorListener) ReportAmbiguity(
+ rec antlr.Parser,
+ dfa *antlr.DFA,
+ startIdx, stopIdx int,
+ exact bool,
+ ambigAlts *antlr.BitSet,
+ configs *antlr.ATNConfigSet,
+) {
+ if !exact {
+ return
+ }
+ stream := rec.GetTokenStream()
+ txt := textSlice(stream, startIdx, stopIdx)
+ l.Ambigs = append(l.Ambigs, &Ambiguity{
+ Text: txt,
+ Alts: ambigAlts.String(),
+ RStack: rec.GetRuleInvocationStack(nil),
+ })
+}
+
+func pGetTokenType(p antlr.Parser, tName string) int {
+ syms := p.GetSymbolicNames()
+ for i, sym := range syms {
+ if sym == tName {
+ return i
+ }
+ }
+ return -1
+}
+
+// tokenName prefers literal > symbolic > numeric.
+func tokenName(p antlr.Parser, tType int) string {
+ lits := p.GetLiteralNames()
+ if tType >= 0 && tType < len(lits) && lits[tType] != "" {
+ return lits[tType]
+ }
+ syms := p.GetSymbolicNames()
+ if tType >= 0 && tType < len(syms) && syms[tType] != "" {
+ return syms[tType]
+ }
+ return fmt.Sprintf("<%d>", tType)
+}
+
+// textSlice pulls raw input text between two token indexes.
+func textSlice(ts antlr.TokenStream, start, stop int) string {
+ var b strings.Builder
+ for i := start; i <= stop && i >= 0; i++ {
+ if tok := ts.Get(i); tok != nil && tok.GetTokenType() != antlr.TokenEOF {
+ b.WriteString(tok.GetText())
+ }
+ }
+ return b.String()
+}
diff --git a/pkg/querybuilder/where_clause_visitor.go b/pkg/querybuilder/where_clause_visitor.go
index 2c8ba191d0..4e10dc605a 100644
--- a/pkg/querybuilder/where_clause_visitor.go
+++ b/pkg/querybuilder/where_clause_visitor.go
@@ -8,7 +8,6 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
grammar "github.com/SigNoz/signoz/pkg/parser/grammar"
- "github.com/SigNoz/signoz/pkg/telemetrylogs"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/antlr4-go/antlr/v4"
@@ -16,75 +15,58 @@ import (
sqlbuilder "github.com/huandu/go-sqlbuilder"
)
-// WhereClauseVisitor implements the FilterQueryVisitor interface
+// filterExpressionVisitor implements the FilterQueryVisitor interface
// to convert the parsed filter expressions into ClickHouse WHERE clause
-type WhereClauseVisitor struct {
- fieldMapper qbtypes.FieldMapper
- conditionBuilder qbtypes.ConditionBuilder
- warnings []error
- fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey
- errors []error
- builder *sqlbuilder.SelectBuilder
- fullTextColumn *telemetrytypes.TelemetryFieldKey
+type filterExpressionVisitor struct {
+ fieldMapper qbtypes.FieldMapper
+ conditionBuilder qbtypes.ConditionBuilder
+ warnings []string
+ fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey
+ errors []error
+ builder *sqlbuilder.SelectBuilder
+ fullTextColumn *telemetrytypes.TelemetryFieldKey
+ jsonBodyPrefix string
+ jsonKeyToKey qbtypes.JsonKeyToFieldFunc
+ skipResourceFilter bool
}
-// NewWhereClauseVisitor creates a new WhereClauseVisitor
-func NewWhereClauseVisitor(
- conditionBuilder qbtypes.ConditionBuilder,
- fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey,
- builder *sqlbuilder.SelectBuilder,
- fullTextColumn *telemetrytypes.TelemetryFieldKey,
-) *WhereClauseVisitor {
- return &WhereClauseVisitor{
- conditionBuilder: conditionBuilder,
- fieldKeys: fieldKeys,
- builder: builder,
- fullTextColumn: fullTextColumn,
+type FilterExprVisitorOpts struct {
+ FieldMapper qbtypes.FieldMapper
+ ConditionBuilder qbtypes.ConditionBuilder
+ FieldKeys map[string][]*telemetrytypes.TelemetryFieldKey
+ Builder *sqlbuilder.SelectBuilder
+ FullTextColumn *telemetrytypes.TelemetryFieldKey
+ JsonBodyPrefix string
+ JsonKeyToKey qbtypes.JsonKeyToFieldFunc
+ SkipResourceFilter bool
+}
+
+// newFilterExpressionVisitor creates a new filterExpressionVisitor
+func newFilterExpressionVisitor(opts FilterExprVisitorOpts) *filterExpressionVisitor {
+ return &filterExpressionVisitor{
+ fieldMapper: opts.FieldMapper,
+ conditionBuilder: opts.ConditionBuilder,
+ fieldKeys: opts.FieldKeys,
+ builder: opts.Builder,
+ fullTextColumn: opts.FullTextColumn,
+ jsonBodyPrefix: opts.JsonBodyPrefix,
+ jsonKeyToKey: opts.JsonKeyToKey,
+ skipResourceFilter: opts.SkipResourceFilter,
}
}
-type SyntaxError struct {
- line, column int
- msg string
-}
-
-func (e *SyntaxError) Error() string {
- return fmt.Sprintf("line %d:%d %s", e.line, e.column, e.msg)
-}
-
-// ErrorListener is a custom error listener to capture syntax errors
-type ErrorListener struct {
- *antlr.DefaultErrorListener
- Errors []error
-}
-
-// NewErrorListener creates a new error listener
-func NewErrorListener() *ErrorListener {
- return &ErrorListener{
- DefaultErrorListener: antlr.NewDefaultErrorListener(),
- Errors: []error{},
- }
-}
-
-// SyntaxError captures syntax errors during parsing
-func (l *ErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) {
- l.Errors = append(l.Errors, &SyntaxError{line: line, column: column, msg: msg})
-}
-
// PrepareWhereClause generates a ClickHouse compatible WHERE clause from the filter query
-func PrepareWhereClause(
- query string,
- fieldKeys map[string][]*telemetrytypes.TelemetryFieldKey,
- conditionBuilder qbtypes.ConditionBuilder,
- fullTextColumn *telemetrytypes.TelemetryFieldKey,
-) (*sqlbuilder.WhereClause, []error, error) {
+func PrepareWhereClause(query string, opts FilterExprVisitorOpts) (*sqlbuilder.WhereClause, []string, error) {
// Setup the ANTLR parsing pipeline
input := antlr.NewInputStream(query)
lexer := grammar.NewFilterQueryLexer(input)
- sb := sqlbuilder.NewSelectBuilder()
+ if opts.Builder == nil {
+ sb := sqlbuilder.NewSelectBuilder()
+ opts.Builder = sb
+ }
- visitor := NewWhereClauseVisitor(conditionBuilder, fieldKeys, sb, fullTextColumn)
+ visitor := newFilterExpressionVisitor(opts)
// Set up error handling
lexerErrorListener := NewErrorListener()
@@ -101,13 +83,13 @@ func PrepareWhereClause(
tree := parser.Query()
// Handle syntax errors
- if len(parserErrorListener.Errors) > 0 {
+ if len(parserErrorListener.SyntaxErrors) > 0 {
combinedErrors := errors.Newf(
errors.TypeInvalidInput,
errors.CodeInvalidInput,
- "found %d syntax errors while parsing the search expression: %v",
- len(parserErrorListener.Errors),
- parserErrorListener.Errors,
+ "found %d syntax errors while parsing the filter expression: %v",
+ len(parserErrorListener.SyntaxErrors),
+ parserErrorListener.SyntaxErrors,
)
return nil, nil, combinedErrors
}
@@ -133,7 +115,7 @@ func PrepareWhereClause(
}
// Visit dispatches to the specific visit method based on node type
-func (v *WhereClauseVisitor) Visit(tree antlr.ParseTree) any {
+func (v *filterExpressionVisitor) Visit(tree antlr.ParseTree) any {
// Handle nil nodes to prevent panic
if tree == nil {
return ""
@@ -179,18 +161,18 @@ func (v *WhereClauseVisitor) Visit(tree antlr.ParseTree) any {
}
}
-func (v *WhereClauseVisitor) VisitQuery(ctx *grammar.QueryContext) any {
+func (v *filterExpressionVisitor) VisitQuery(ctx *grammar.QueryContext) any {
return v.Visit(ctx.Expression())
}
// VisitExpression passes through to the orExpression
-func (v *WhereClauseVisitor) VisitExpression(ctx *grammar.ExpressionContext) any {
+func (v *filterExpressionVisitor) VisitExpression(ctx *grammar.ExpressionContext) any {
return v.Visit(ctx.OrExpression())
}
// VisitOrExpression handles OR expressions
-func (v *WhereClauseVisitor) VisitOrExpression(ctx *grammar.OrExpressionContext) any {
+func (v *filterExpressionVisitor) VisitOrExpression(ctx *grammar.OrExpressionContext) any {
andExpressions := ctx.AllAndExpression()
andExpressionConditions := make([]string, len(andExpressions))
@@ -206,7 +188,7 @@ func (v *WhereClauseVisitor) VisitOrExpression(ctx *grammar.OrExpressionContext)
}
// VisitAndExpression handles AND expressions
-func (v *WhereClauseVisitor) VisitAndExpression(ctx *grammar.AndExpressionContext) any {
+func (v *filterExpressionVisitor) VisitAndExpression(ctx *grammar.AndExpressionContext) any {
unaryExpressions := ctx.AllUnaryExpression()
unaryExpressionConditions := make([]string, len(unaryExpressions))
@@ -222,7 +204,7 @@ func (v *WhereClauseVisitor) VisitAndExpression(ctx *grammar.AndExpressionContex
}
// VisitUnaryExpression handles NOT expressions
-func (v *WhereClauseVisitor) VisitUnaryExpression(ctx *grammar.UnaryExpressionContext) any {
+func (v *filterExpressionVisitor) VisitUnaryExpression(ctx *grammar.UnaryExpressionContext) any {
result := v.Visit(ctx.Primary()).(string)
// Check if this is a NOT expression
@@ -234,7 +216,7 @@ func (v *WhereClauseVisitor) VisitUnaryExpression(ctx *grammar.UnaryExpressionCo
}
// VisitPrimary handles grouped expressions, comparisons, function calls, and full-text search
-func (v *WhereClauseVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any {
+func (v *filterExpressionVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any {
if ctx.OrExpression() != nil {
// This is a parenthesized expression
return fmt.Sprintf("(%s)", v.Visit(ctx.OrExpression()).(string))
@@ -246,14 +228,43 @@ func (v *WhereClauseVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any {
return v.Visit(ctx.FullText())
}
- // Handle standalone key as a full text search term
+ // Handle standalone key/value as a full text search term
if ctx.GetChildCount() == 1 {
+ if v.fullTextColumn == nil {
+ v.errors = append(v.errors, errors.Newf(
+ errors.TypeInvalidInput,
+ errors.CodeInvalidInput,
+ "full text search is not supported",
+ ))
+ return ""
+ }
child := ctx.GetChild(0)
if keyCtx, ok := child.(*grammar.KeyContext); ok {
// create a full text search condition on the body field
keyText := keyCtx.GetText()
cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, keyText, v.builder)
if err != nil {
+ v.errors = append(v.errors, errors.WrapInternalf(err, errors.CodeInternal, "failed to build full text search condition"))
+ return ""
+ }
+ return cond
+ } else if valCtx, ok := child.(*grammar.ValueContext); ok {
+ var text string
+ if valCtx.QUOTED_TEXT() != nil {
+ text = trimQuotes(valCtx.QUOTED_TEXT().GetText())
+ } else if valCtx.NUMBER() != nil {
+ text = valCtx.NUMBER().GetText()
+ } else if valCtx.BOOL() != nil {
+ text = valCtx.BOOL().GetText()
+ } else if valCtx.KEY() != nil {
+ text = valCtx.KEY().GetText()
+ } else {
+ v.errors = append(v.errors, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "unsupported value type: %s", valCtx.GetText()))
+ return ""
+ }
+ cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, text, v.builder)
+ if err != nil {
+ v.errors = append(v.errors, errors.WrapInternalf(err, errors.CodeInternal, "failed to build full text search condition"))
return ""
}
return cond
@@ -264,9 +275,21 @@ func (v *WhereClauseVisitor) VisitPrimary(ctx *grammar.PrimaryContext) any {
}
// VisitComparison handles all comparison operators
-func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any {
+func (v *filterExpressionVisitor) VisitComparison(ctx *grammar.ComparisonContext) any {
keys := v.Visit(ctx.Key()).([]*telemetrytypes.TelemetryFieldKey)
+ // this is used to skip the resource filtering on main table if
+ // the query may use the resources table sub-query filter
+ if v.skipResourceFilter {
+ filteredKeys := []*telemetrytypes.TelemetryFieldKey{}
+ for _, key := range keys {
+ if key.FieldContext != telemetrytypes.FieldContextResource {
+ filteredKeys = append(filteredKeys, key)
+ }
+ }
+ keys = filteredKeys
+ }
+
// Handle EXISTS specially
if ctx.EXISTS() != nil {
op := qbtypes.FilterOperatorExists
@@ -281,12 +304,23 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any
}
conds = append(conds, condition)
}
+ // if there is only one condition, return it directly, one less `()` wrapper
+ if len(conds) == 1 {
+ return conds[0]
+ }
return v.builder.Or(conds...)
}
// Handle IN clause
if ctx.InClause() != nil || ctx.NotInClause() != nil {
- values := v.Visit(ctx.InClause()).([]any)
+
+ var values []any
+ if ctx.InClause() != nil {
+ values = v.Visit(ctx.InClause()).([]any)
+ } else if ctx.NotInClause() != nil {
+ values = v.Visit(ctx.NotInClause()).([]any)
+ }
+
op := qbtypes.FilterOperatorIn
if ctx.NotInClause() != nil {
op = qbtypes.FilterOperatorNotIn
@@ -299,6 +333,9 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any
}
conds = append(conds, condition)
}
+ if len(conds) == 1 {
+ return conds[0]
+ }
return v.builder.Or(conds...)
}
@@ -325,6 +362,9 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any
}
conds = append(conds, condition)
}
+ if len(conds) == 1 {
+ return conds[0]
+ }
return v.builder.Or(conds...)
}
@@ -351,29 +391,35 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any
} else if ctx.LIKE() != nil {
op = qbtypes.FilterOperatorLike
} else if ctx.ILIKE() != nil {
- op = qbtypes.FilterOperatorLike
+ op = qbtypes.FilterOperatorILike
} else if ctx.NOT_LIKE() != nil {
op = qbtypes.FilterOperatorNotLike
} else if ctx.NOT_ILIKE() != nil {
- op = qbtypes.FilterOperatorNotLike
+ op = qbtypes.FilterOperatorNotILike
} else if ctx.REGEXP() != nil {
op = qbtypes.FilterOperatorRegexp
- } else if ctx.NOT() != nil && ctx.REGEXP() != nil {
- op = qbtypes.FilterOperatorNotRegexp
+ if ctx.NOT() != nil {
+ op = qbtypes.FilterOperatorNotRegexp
+ }
} else if ctx.CONTAINS() != nil {
op = qbtypes.FilterOperatorContains
- } else if ctx.NOT() != nil && ctx.CONTAINS() != nil {
- op = qbtypes.FilterOperatorNotContains
+ if ctx.NOT() != nil {
+ op = qbtypes.FilterOperatorNotContains
+ }
}
var conds []string
for _, key := range keys {
condition, err := v.conditionBuilder.ConditionFor(context.Background(), key, op, value, v.builder)
if err != nil {
+ v.errors = append(v.errors, errors.WrapInternalf(err, errors.CodeInternal, "failed to build condition"))
return ""
}
conds = append(conds, condition)
}
+ if len(conds) == 1 {
+ return conds[0]
+ }
return v.builder.Or(conds...)
}
@@ -381,17 +427,17 @@ func (v *WhereClauseVisitor) VisitComparison(ctx *grammar.ComparisonContext) any
}
// VisitInClause handles IN expressions
-func (v *WhereClauseVisitor) VisitInClause(ctx *grammar.InClauseContext) any {
+func (v *filterExpressionVisitor) VisitInClause(ctx *grammar.InClauseContext) any {
return v.Visit(ctx.ValueList())
}
// VisitNotInClause handles NOT IN expressions
-func (v *WhereClauseVisitor) VisitNotInClause(ctx *grammar.NotInClauseContext) any {
+func (v *filterExpressionVisitor) VisitNotInClause(ctx *grammar.NotInClauseContext) any {
return v.Visit(ctx.ValueList())
}
// VisitValueList handles comma-separated value lists
-func (v *WhereClauseVisitor) VisitValueList(ctx *grammar.ValueListContext) any {
+func (v *filterExpressionVisitor) VisitValueList(ctx *grammar.ValueListContext) any {
values := ctx.AllValue()
parts := []any{}
@@ -403,18 +449,34 @@ func (v *WhereClauseVisitor) VisitValueList(ctx *grammar.ValueListContext) any {
}
// VisitFullText handles standalone quoted strings for full-text search
-func (v *WhereClauseVisitor) VisitFullText(ctx *grammar.FullTextContext) any {
- // remove quotes from the quotedText
- quotedText := strings.Trim(ctx.QUOTED_TEXT().GetText(), "\"'")
- cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, quotedText, v.builder)
+func (v *filterExpressionVisitor) VisitFullText(ctx *grammar.FullTextContext) any {
+
+ var text string
+
+ if ctx.QUOTED_TEXT() != nil {
+ text = trimQuotes(ctx.QUOTED_TEXT().GetText())
+ } else if ctx.FREETEXT() != nil {
+ text = ctx.FREETEXT().GetText()
+ }
+
+ if v.fullTextColumn == nil {
+ v.errors = append(v.errors, errors.Newf(
+ errors.TypeInvalidInput,
+ errors.CodeInvalidInput,
+ "full text search is not supported",
+ ))
+ return ""
+ }
+ cond, err := v.conditionBuilder.ConditionFor(context.Background(), v.fullTextColumn, qbtypes.FilterOperatorRegexp, text, v.builder)
if err != nil {
+ v.errors = append(v.errors, errors.WrapInternalf(err, errors.CodeInternal, "failed to build full text search condition"))
return ""
}
return cond
}
// VisitFunctionCall handles function calls like has(), hasAny(), etc.
-func (v *WhereClauseVisitor) VisitFunctionCall(ctx *grammar.FunctionCallContext) any {
+func (v *filterExpressionVisitor) VisitFunctionCall(ctx *grammar.FunctionCallContext) any {
// Get function name based on which token is present
var functionName string
if ctx.HAS() != nil {
@@ -460,10 +522,16 @@ func (v *WhereClauseVisitor) VisitFunctionCall(ctx *grammar.FunctionCallContext)
for _, key := range keys {
var fieldName string
- if strings.HasPrefix(key.Name, telemetrylogs.BodyJSONStringSearchPrefix) {
- fieldName, _ = telemetrylogs.GetBodyJSONKey(context.Background(), key, qbtypes.FilterOperatorUnknown, value)
+ if strings.HasPrefix(key.Name, v.jsonBodyPrefix) {
+ fieldName, _ = v.jsonKeyToKey(context.Background(), key, qbtypes.FilterOperatorUnknown, value)
} else {
- fieldName, _ = v.fieldMapper.FieldFor(context.Background(), key)
+ v.errors = append(v.errors, errors.Newf(
+ errors.TypeInvalidInput,
+ errors.CodeInvalidInput,
+ "function `%s` supports only body JSON search",
+ functionName,
+ ))
+ return ""
}
var cond string
@@ -479,11 +547,14 @@ func (v *WhereClauseVisitor) VisitFunctionCall(ctx *grammar.FunctionCallContext)
conds = append(conds, cond)
}
+ if len(conds) == 1 {
+ return conds[0]
+ }
return v.builder.Or(conds...)
}
// VisitFunctionParamList handles the parameter list for function calls
-func (v *WhereClauseVisitor) VisitFunctionParamList(ctx *grammar.FunctionParamListContext) any {
+func (v *filterExpressionVisitor) VisitFunctionParamList(ctx *grammar.FunctionParamListContext) any {
params := ctx.AllFunctionParam()
parts := make([]any, len(params))
@@ -495,7 +566,7 @@ func (v *WhereClauseVisitor) VisitFunctionParamList(ctx *grammar.FunctionParamLi
}
// VisitFunctionParam handles individual parameters in function calls
-func (v *WhereClauseVisitor) VisitFunctionParam(ctx *grammar.FunctionParamContext) any {
+func (v *filterExpressionVisitor) VisitFunctionParam(ctx *grammar.FunctionParamContext) any {
if ctx.Key() != nil {
return v.Visit(ctx.Key())
} else if ctx.Value() != nil {
@@ -508,16 +579,16 @@ func (v *WhereClauseVisitor) VisitFunctionParam(ctx *grammar.FunctionParamContex
}
// VisitArray handles array literals
-func (v *WhereClauseVisitor) VisitArray(ctx *grammar.ArrayContext) any {
+func (v *filterExpressionVisitor) VisitArray(ctx *grammar.ArrayContext) any {
return v.Visit(ctx.ValueList())
}
// VisitValue handles literal values: strings, numbers, booleans
-func (v *WhereClauseVisitor) VisitValue(ctx *grammar.ValueContext) any {
+func (v *filterExpressionVisitor) VisitValue(ctx *grammar.ValueContext) any {
if ctx.QUOTED_TEXT() != nil {
txt := ctx.QUOTED_TEXT().GetText()
// trim quotes and return the value
- return strings.Trim(txt, "\"'")
+ return trimQuotes(txt)
} else if ctx.NUMBER() != nil {
number, err := strconv.ParseFloat(ctx.NUMBER().GetText(), 64)
if err != nil {
@@ -546,11 +617,11 @@ func (v *WhereClauseVisitor) VisitValue(ctx *grammar.ValueContext) any {
}
// VisitKey handles field/column references
-func (v *WhereClauseVisitor) VisitKey(ctx *grammar.KeyContext) any {
+func (v *filterExpressionVisitor) VisitKey(ctx *grammar.KeyContext) any {
- fieldKey := telemetrytypes.GetFieldKeyFromKeyText(ctx.KEY().GetText())
+ fieldKey := telemetrytypes.GetFieldKeyFromKeyText(ctx.GetText())
- keyName := strings.TrimPrefix(fieldKey.Name, telemetrylogs.BodyJSONStringSearchPrefix)
+ keyName := strings.TrimPrefix(fieldKey.Name, v.jsonBodyPrefix)
fieldKeysForName := v.fieldKeys[keyName]
@@ -558,26 +629,33 @@ func (v *WhereClauseVisitor) VisitKey(ctx *grammar.KeyContext) any {
// if there is a field with the same name as attribute/resource attribute
// Since it will ORed with the fieldKeysForName, it will not result empty
// when either of them have values
- if strings.HasPrefix(fieldKey.Name, telemetrylogs.BodyJSONStringSearchPrefix) {
- fieldKeysForName = append(fieldKeysForName, &fieldKey)
+ if strings.HasPrefix(fieldKey.Name, v.jsonBodyPrefix) && v.jsonBodyPrefix != "" {
+ if keyName != "" {
+ fieldKeysForName = append(fieldKeysForName, &fieldKey)
+ }
}
- // TODO(srikanthccv): do we want to return an error here?
- // should we infer the type and auto-magically build a key for expression?
if len(fieldKeysForName) == 0 {
- v.errors = append(v.errors, errors.Newf(
- errors.TypeInvalidInput,
- errors.CodeInvalidInput,
- "key `%s` not found",
- fieldKey.Name,
- ))
+ if strings.HasPrefix(fieldKey.Name, v.jsonBodyPrefix) && v.jsonBodyPrefix != "" && keyName == "" {
+ v.errors = append(v.errors, errors.NewInvalidInputf(
+ errors.CodeInvalidInput,
+ "missing key for body json search - expected key of the form `body.key` (ex: `body.status`)",
+ ))
+ } else {
+ // TODO(srikanthccv): do we want to return an error here?
+ // should we infer the type and auto-magically build a key for expression?
+ v.errors = append(v.errors, errors.Newf(
+ errors.TypeInvalidInput,
+ errors.CodeInvalidInput,
+ "key `%s` not found",
+ fieldKey.Name,
+ ))
+ }
}
if len(fieldKeysForName) > 1 {
// this is warning state, we must have a unambiguous key
- v.warnings = append(v.warnings, errors.Newf(
- errors.TypeInvalidInput,
- errors.CodeInvalidInput,
+ v.warnings = append(v.warnings, fmt.Sprintf(
"key `%s` is ambiguous, found %d different combinations of field context and data type: %v",
fieldKey.Name,
len(fieldKeysForName),
@@ -587,3 +665,13 @@ func (v *WhereClauseVisitor) VisitKey(ctx *grammar.KeyContext) any {
return fieldKeysForName
}
+
+func trimQuotes(txt string) string {
+ if len(txt) >= 2 {
+ if (txt[0] == '"' && txt[len(txt)-1] == '"') ||
+ (txt[0] == '\'' && txt[len(txt)-1] == '\'') {
+ return txt[1 : len(txt)-1]
+ }
+ }
+ return txt
+}
diff --git a/pkg/telemetrylogs/condition_builder.go b/pkg/telemetrylogs/condition_builder.go
index 4e432a5ec2..da171ca51f 100644
--- a/pkg/telemetrylogs/condition_builder.go
+++ b/pkg/telemetrylogs/condition_builder.go
@@ -3,6 +3,7 @@ package telemetrylogs
import (
"context"
"fmt"
+ "slices"
"strings"
schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
@@ -20,7 +21,7 @@ func NewConditionBuilder(fm qbtypes.FieldMapper) *conditionBuilder {
return &conditionBuilder{fm: fm}
}
-func (c *conditionBuilder) ConditionFor(
+func (c *conditionBuilder) conditionFor(
ctx context.Context,
key *telemetrytypes.TelemetryFieldKey,
operator qbtypes.FilterOperator,
@@ -44,6 +45,16 @@ func (c *conditionBuilder) ConditionFor(
tblFieldName, value = telemetrytypes.DataTypeCollisionHandledFieldName(key, value, tblFieldName)
+ // make use of case insensitive index for body
+ if tblFieldName == "body" {
+ switch operator {
+ case qbtypes.FilterOperatorLike:
+ return sb.ILike(tblFieldName, value), nil
+ case qbtypes.FilterOperatorNotLike:
+ return sb.NotILike(tblFieldName, value), nil
+ }
+ }
+
// regular operators
switch operator {
// regular operators
@@ -76,11 +87,9 @@ func (c *conditionBuilder) ConditionFor(
return sb.NotILike(tblFieldName, fmt.Sprintf("%%%s%%", value)), nil
case qbtypes.FilterOperatorRegexp:
- exp := fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value))
- return sb.And(exp), nil
+ return fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value)), nil
case qbtypes.FilterOperatorNotRegexp:
- exp := fmt.Sprintf(`not match(%s, %s)`, tblFieldName, sb.Var(value))
- return sb.And(exp), nil
+ return fmt.Sprintf(`NOT match(%s, %s)`, tblFieldName, sb.Var(value)), nil
// between and not between
case qbtypes.FilterOperatorBetween:
values, ok := value.([]any)
@@ -107,18 +116,37 @@ func (c *conditionBuilder) ConditionFor(
if !ok {
return "", qbtypes.ErrInValues
}
- return sb.In(tblFieldName, values...), nil
+ // instead of using IN, we use `=` + `OR` to make use of index
+ conditions := []string{}
+ for _, value := range values {
+ conditions = append(conditions, sb.E(tblFieldName, value))
+ }
+ return sb.Or(conditions...), nil
case qbtypes.FilterOperatorNotIn:
values, ok := value.([]any)
if !ok {
return "", qbtypes.ErrInValues
}
- return sb.NotIn(tblFieldName, values...), nil
+ // instead of using NOT IN, we use `!=` + `AND` to make use of index
+ conditions := []string{}
+ for _, value := range values {
+ conditions = append(conditions, sb.NE(tblFieldName, value))
+ }
+ return sb.And(conditions...), nil
// exists and not exists
// in the UI based query builder, `exists` and `not exists` are used for
// key membership checks, so depending on the column type, the condition changes
case qbtypes.FilterOperatorExists, qbtypes.FilterOperatorNotExists:
+
+ if strings.HasPrefix(key.Name, BodyJSONStringSearchPrefix) {
+ if operator == qbtypes.FilterOperatorExists {
+ return GetBodyJSONKeyForExists(ctx, key, operator, value), nil
+ } else {
+ return "NOT " + GetBodyJSONKeyForExists(ctx, key, operator, value), nil
+ }
+ }
+
var value any
switch column.Type {
case schema.ColumnTypeString, schema.LowCardinalityColumnType{ElementType: schema.ColumnTypeString}:
@@ -160,3 +188,32 @@ func (c *conditionBuilder) ConditionFor(
}
return "", fmt.Errorf("unsupported operator: %v", operator)
}
+
+func (c *conditionBuilder) ConditionFor(
+ ctx context.Context,
+ key *telemetrytypes.TelemetryFieldKey,
+ operator qbtypes.FilterOperator,
+ value any,
+ sb *sqlbuilder.SelectBuilder,
+) (string, error) {
+ condition, err := c.conditionFor(ctx, key, operator, value, sb)
+ if err != nil {
+ return "", err
+ }
+
+ if operator.AddDefaultExistsFilter() {
+ // skip adding exists filter for intrinsic fields
+ // with an exception for body json search
+ field, _ := c.fm.FieldFor(ctx, key)
+ if slices.Contains(IntrinsicFields, field) && !strings.HasPrefix(key.Name, BodyJSONStringSearchPrefix) {
+ return condition, nil
+ }
+
+ existsCondition, err := c.conditionFor(ctx, key, qbtypes.FilterOperatorExists, nil, sb)
+ if err != nil {
+ return "", err
+ }
+ return sb.And(condition, existsCondition), nil
+ }
+ return condition, nil
+}
diff --git a/pkg/telemetrylogs/condition_builder_test.go b/pkg/telemetrylogs/condition_builder_test.go
index 3adb4f64de..45f61b2d03 100644
--- a/pkg/telemetrylogs/condition_builder_test.go
+++ b/pkg/telemetrylogs/condition_builder_test.go
@@ -20,6 +20,7 @@ func TestConditionFor(t *testing.T) {
operator qbtypes.FilterOperator
value any
expectedSQL string
+ expectedArgs []any
expectedError error
}{
{
@@ -31,6 +32,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorEqual,
value: "error message",
expectedSQL: "body = ?",
+ expectedArgs: []any{"error message"},
expectedError: nil,
},
{
@@ -42,6 +44,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorNotEqual,
value: uint64(1617979338000000000),
expectedSQL: "timestamp <> ?",
+ expectedArgs: []any{uint64(1617979338000000000)},
expectedError: nil,
},
{
@@ -53,7 +56,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorGreaterThan,
value: float64(100),
- expectedSQL: "attributes_number['request.duration'] > ?",
+ expectedSQL: "(attributes_number['request.duration'] > ? AND mapContains(attributes_number, 'request.duration') = ?)",
+ expectedArgs: []any{float64(100), true},
expectedError: nil,
},
{
@@ -65,7 +69,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorLessThan,
value: float64(1024),
- expectedSQL: "attributes_number['request.size'] < ?",
+ expectedSQL: "(attributes_number['request.size'] < ? AND mapContains(attributes_number, 'request.size') = ?)",
+ expectedArgs: []any{float64(1024), true},
expectedError: nil,
},
{
@@ -77,6 +82,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorGreaterThanOrEq,
value: uint64(1617979338000000000),
expectedSQL: "timestamp >= ?",
+ expectedArgs: []any{uint64(1617979338000000000)},
expectedError: nil,
},
{
@@ -88,6 +94,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorLessThanOrEq,
value: uint64(1617979338000000000),
expectedSQL: "timestamp <= ?",
+ expectedArgs: []any{uint64(1617979338000000000)},
expectedError: nil,
},
{
@@ -98,7 +105,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorLike,
value: "%error%",
- expectedSQL: "body LIKE ?",
+ expectedSQL: "LOWER(body) LIKE LOWER(?)",
+ expectedArgs: []any{"%error%"},
expectedError: nil,
},
{
@@ -109,7 +117,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorNotLike,
value: "%error%",
- expectedSQL: "body NOT LIKE ?",
+ expectedSQL: "LOWER(body) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%error%"},
expectedError: nil,
},
{
@@ -121,7 +130,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorILike,
value: "%admin%",
- expectedSQL: "WHERE LOWER(attributes_string['user.id']) LIKE LOWER(?)",
+ expectedSQL: "(LOWER(attributes_string['user.id']) LIKE LOWER(?) AND mapContains(attributes_string, 'user.id') = ?)",
+ expectedArgs: []any{"%admin%", true},
expectedError: nil,
},
{
@@ -134,6 +144,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorNotILike,
value: "%admin%",
expectedSQL: "WHERE LOWER(attributes_string['user.id']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%admin%"},
expectedError: nil,
},
{
@@ -145,7 +156,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorContains,
value: "admin",
- expectedSQL: "WHERE LOWER(attributes_string['user.id']) LIKE LOWER(?)",
+ expectedSQL: "(LOWER(attributes_string['user.id']) LIKE LOWER(?) AND mapContains(attributes_string, 'user.id') = ?)",
+ expectedArgs: []any{"%admin%", true},
expectedError: nil,
},
{
@@ -157,6 +169,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorBetween,
value: []any{uint64(1617979338000000000), uint64(1617979348000000000)},
expectedSQL: "timestamp BETWEEN ? AND ?",
+ expectedArgs: []any{uint64(1617979338000000000), uint64(1617979348000000000)},
expectedError: nil,
},
{
@@ -190,6 +203,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorNotBetween,
value: []any{uint64(1617979338000000000), uint64(1617979348000000000)},
expectedSQL: "timestamp NOT BETWEEN ? AND ?",
+ expectedArgs: []any{uint64(1617979338000000000), uint64(1617979348000000000)},
expectedError: nil,
},
{
@@ -200,7 +214,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorIn,
value: []any{"error", "fatal", "critical"},
- expectedSQL: "severity_text IN (?, ?, ?)",
+ expectedSQL: "(severity_text = ? OR severity_text = ? OR severity_text = ?)",
+ expectedArgs: []any{"error", "fatal", "critical"},
expectedError: nil,
},
{
@@ -222,7 +237,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorNotIn,
value: []any{"debug", "info", "trace"},
- expectedSQL: "severity_text NOT IN (?, ?, ?)",
+ expectedSQL: "(severity_text <> ? AND severity_text <> ? AND severity_text <> ?)",
+ expectedArgs: []any{"debug", "info", "trace"},
expectedError: nil,
},
{
@@ -234,6 +250,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorExists,
value: nil,
expectedSQL: "body <> ?",
+ expectedArgs: []any{""},
expectedError: nil,
},
{
@@ -245,6 +262,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorNotExists,
value: nil,
expectedSQL: "body = ?",
+ expectedArgs: []any{""},
expectedError: nil,
},
{
@@ -256,6 +274,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorExists,
value: nil,
expectedSQL: "timestamp <> ?",
+ expectedArgs: []any{0},
expectedError: nil,
},
{
@@ -268,6 +287,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorExists,
value: nil,
expectedSQL: "mapContains(attributes_string, 'user.id') = ?",
+ expectedArgs: []any{true},
expectedError: nil,
},
{
@@ -280,6 +300,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorNotExists,
value: nil,
expectedSQL: "mapContains(attributes_string, 'user.id') <> ?",
+ expectedArgs: []any{true},
expectedError: nil,
},
{
@@ -308,8 +329,9 @@ func TestConditionFor(t *testing.T) {
assert.Equal(t, tc.expectedError, err)
} else {
require.NoError(t, err)
- sql, _ := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
+ sql, args := sb.BuildWithFlavor(sqlbuilder.ClickHouse)
assert.Contains(t, sql, tc.expectedSQL)
+ assert.Equal(t, tc.expectedArgs, args)
}
})
}
@@ -324,6 +346,7 @@ func TestConditionForMultipleKeys(t *testing.T) {
operator qbtypes.FilterOperator
value any
expectedSQL string
+ expectedArgs []any
expectedError error
}{
{
@@ -341,6 +364,7 @@ func TestConditionForMultipleKeys(t *testing.T) {
operator: qbtypes.FilterOperatorEqual,
value: "error message",
expectedSQL: "body = ? AND severity_text = ?",
+ expectedArgs: []any{"error message", "error message"},
expectedError: nil,
},
}
@@ -389,7 +413,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorEqual,
value: 200,
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') = ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ?`,
expectedError: nil,
},
{
@@ -399,7 +423,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorEqual,
value: 405.5,
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.duration_ms'), 'Float64') = ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."duration_ms"'), 'Float64') = ?`,
expectedError: nil,
},
{
@@ -409,7 +433,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorEqual,
value: "GET",
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.method'), 'String') = ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."method"'), 'String') = ?`,
expectedError: nil,
},
{
@@ -419,7 +443,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorEqual,
value: true,
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.success'), 'Bool') = ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."success"'), 'Bool') = ?`,
expectedError: nil,
},
{
@@ -429,7 +453,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorExists,
value: nil,
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'String') <> ?",
+ expectedSQL: `JSON_EXISTS(body, '$."http"."status_code"')`,
expectedError: nil,
},
{
@@ -439,7 +463,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorNotExists,
value: nil,
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'String') = ?",
+ expectedSQL: `NOT JSON_EXISTS(body, '$."http"."status_code"')`,
expectedError: nil,
},
{
@@ -449,7 +473,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorGreaterThan,
value: "200",
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') > ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') > ?`,
expectedError: nil,
},
{
@@ -459,7 +483,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorGreaterThan,
value: 200,
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') > ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') > ?`,
expectedError: nil,
},
{
@@ -469,7 +493,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorLessThan,
value: "300",
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') < ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') < ?`,
expectedError: nil,
},
{
@@ -479,7 +503,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorLessThan,
value: 300,
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') < ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') < ?`,
expectedError: nil,
},
{
@@ -489,7 +513,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorContains,
value: "200",
- expectedSQL: "LOWER(JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'String')) LIKE LOWER(?)",
+ expectedSQL: `LOWER(JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'String')) LIKE LOWER(?)`,
expectedError: nil,
},
{
@@ -499,7 +523,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorNotContains,
value: "200",
- expectedSQL: "LOWER(JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'String')) NOT LIKE LOWER(?)",
+ expectedSQL: `LOWER(JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'String')) NOT LIKE LOWER(?)`,
expectedError: nil,
},
{
@@ -509,7 +533,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorBetween,
value: []any{"200", "300"},
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') BETWEEN ? AND ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') BETWEEN ? AND ?`,
expectedError: nil,
},
{
@@ -519,7 +543,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorBetween,
value: []any{400, 500},
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') BETWEEN ? AND ?",
+ expectedSQL: `JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') BETWEEN ? AND ?`,
expectedError: nil,
},
{
@@ -529,7 +553,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorIn,
value: []any{"200", "300"},
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') IN (?, ?)",
+ expectedSQL: `(JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ? OR JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ?)`,
expectedError: nil,
},
{
@@ -539,7 +563,7 @@ func TestConditionForJSONBodySearch(t *testing.T) {
},
operator: qbtypes.FilterOperatorIn,
value: []any{401, 404, 500},
- expectedSQL: "JSONExtract(JSON_VALUE(body, '$.http.status_code'), 'Int64') IN (?, ?, ?)",
+ expectedSQL: `(JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ? OR JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ? OR JSONExtract(JSON_VALUE(body, '$."http"."status_code"'), 'Int64') = ?)`,
expectedError: nil,
},
}
diff --git a/pkg/telemetrylogs/const.go b/pkg/telemetrylogs/const.go
index 1acc1fcff9..ee3d92af0c 100644
--- a/pkg/telemetrylogs/const.go
+++ b/pkg/telemetrylogs/const.go
@@ -2,4 +2,5 @@ package telemetrylogs
var (
BodyJSONStringSearchPrefix = `body.`
+ IntrinsicFields = []string{"timestamp", "body", "trace_id", "span_id", "trace_flags", "severity_text", "severity_number"}
)
diff --git a/pkg/telemetrylogs/json.go b/pkg/telemetrylogs/json.go
index 987018a2c7..89b2f27626 100644
--- a/pkg/telemetrylogs/json.go
+++ b/pkg/telemetrylogs/json.go
@@ -68,22 +68,36 @@ func inferDataType(value any, operator qbtypes.FilterOperator, key *telemetrytyp
return valueType, value
}
+func getBodyJSONPath(key *telemetrytypes.TelemetryFieldKey) string {
+ parts := strings.Split(key.Name, ".")[1:]
+ newParts := []string{}
+ for _, part := range parts {
+ if strings.HasSuffix(part, "[*]") {
+ newParts = append(newParts, fmt.Sprintf(`"%s"[*]`, strings.TrimSuffix(part, "[*]")))
+ } else {
+ newParts = append(newParts, fmt.Sprintf(`"%s"`, part))
+ }
+ }
+ return strings.Join(newParts, ".")
+}
+
func GetBodyJSONKey(_ context.Context, key *telemetrytypes.TelemetryFieldKey, operator qbtypes.FilterOperator, value any) (string, any) {
dataType, value := inferDataType(value, operator, key)
- // all body json keys are of the form body.
- path := strings.Join(strings.Split(key.Name, ".")[1:], ".")
-
// for array types, we need to extract the value from the JSON_QUERY
if dataType == telemetrytypes.FieldDataTypeArrayInt64 ||
dataType == telemetrytypes.FieldDataTypeArrayFloat64 ||
dataType == telemetrytypes.FieldDataTypeArrayString ||
dataType == telemetrytypes.FieldDataTypeArrayBool ||
dataType == telemetrytypes.FieldDataTypeArrayNumber {
- return fmt.Sprintf("JSONExtract(JSON_QUERY(body, '$.%s'), '%s')", path, dataType.CHDataType()), value
+ return fmt.Sprintf("JSONExtract(JSON_QUERY(body, '$.%s'), '%s')", getBodyJSONPath(key), dataType.CHDataType()), value
}
// for all other types, we need to extract the value from the JSON_VALUE
- return fmt.Sprintf("JSONExtract(JSON_VALUE(body, '$.%s'), '%s')", path, dataType.CHDataType()), value
+ return fmt.Sprintf("JSONExtract(JSON_VALUE(body, '$.%s'), '%s')", getBodyJSONPath(key), dataType.CHDataType()), value
+}
+
+func GetBodyJSONKeyForExists(_ context.Context, key *telemetrytypes.TelemetryFieldKey, _ qbtypes.FilterOperator, _ any) string {
+ return fmt.Sprintf("JSON_EXISTS(body, '$.%s')", getBodyJSONPath(key))
}
diff --git a/pkg/telemetrymetadata/condition_builder.go b/pkg/telemetrymetadata/condition_builder.go
index 79044f53c2..a029930100 100644
--- a/pkg/telemetrymetadata/condition_builder.go
+++ b/pkg/telemetrymetadata/condition_builder.go
@@ -76,7 +76,7 @@ func (c *conditionBuilder) ConditionFor(
case qbtypes.FilterOperatorRegexp:
cond = fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value))
case qbtypes.FilterOperatorNotRegexp:
- cond = fmt.Sprintf(`not match(%s, %s)`, tblFieldName, sb.Var(value))
+ cond = fmt.Sprintf(`NOT match(%s, %s)`, tblFieldName, sb.Var(value))
// in and not in
case qbtypes.FilterOperatorIn:
@@ -84,13 +84,23 @@ func (c *conditionBuilder) ConditionFor(
if !ok {
return "", qbtypes.ErrInValues
}
- cond = sb.In(tblFieldName, values...)
+ // instead of using IN, we use `=` + `OR` to make use of index
+ conditions := []string{}
+ for _, value := range values {
+ conditions = append(conditions, sb.E(tblFieldName, value))
+ }
+ cond = sb.Or(conditions...)
case qbtypes.FilterOperatorNotIn:
values, ok := value.([]any)
if !ok {
return "", qbtypes.ErrInValues
}
- cond = sb.NotIn(tblFieldName, values...)
+ // instead of using NOT IN, we use `!=` + `AND` to make use of index
+ conditions := []string{}
+ for _, value := range values {
+ conditions = append(conditions, sb.NE(tblFieldName, value))
+ }
+ cond = sb.And(conditions...)
// exists and not exists
// in the query builder, `exists` and `not exists` are used for
diff --git a/pkg/telemetrytests/agg_rewrite_test.go b/pkg/telemetrytests/agg_rewrite_test.go
new file mode 100644
index 0000000000..f4d99281d8
--- /dev/null
+++ b/pkg/telemetrytests/agg_rewrite_test.go
@@ -0,0 +1,98 @@
+package telemetrytests
+
+import (
+ "testing"
+
+ "github.com/SigNoz/signoz/pkg/querybuilder"
+ "github.com/SigNoz/signoz/pkg/telemetrylogs"
+ "github.com/SigNoz/signoz/pkg/telemetrytraces"
+ "github.com/SigNoz/signoz/pkg/types/telemetrytypes"
+ "github.com/stretchr/testify/require"
+)
+
+// TestAggRewrite tests rewrite set of aggregation expressions
+func TestAggRewrite(t *testing.T) {
+ fm := telemetrytraces.NewFieldMapper()
+ cb := telemetrytraces.NewConditionBuilder(fm)
+
+ // Define a comprehensive set of field keys to support all test cases
+ keys := buildCompleteFieldKeyMap()
+
+ opts := querybuilder.AggExprRewriterOptions{
+ FieldMapper: fm,
+ ConditionBuilder: cb,
+ FieldKeys: keys,
+ FullTextColumn: &telemetrytypes.TelemetryFieldKey{
+ Name: "body",
+ },
+ JsonBodyPrefix: "body",
+ JsonKeyToKey: telemetrylogs.GetBodyJSONKey,
+ RateInterval: 60,
+ }
+
+ testCases := []struct {
+ expr string
+ shouldPass bool
+ expectedExpr string
+ expectedArgs []any
+ expectedErrorContains string
+ }{
+ {
+ expr: "count()",
+ shouldPass: true,
+ expectedExpr: "count()",
+ },
+ {
+ expr: `countIf(service.name = "redis")`,
+ shouldPass: true,
+ expectedExpr: "countIf((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{"redis", true},
+ },
+ {
+ expr: `countIf(service.name = "redis" AND status = 200)`,
+ shouldPass: true,
+ expectedExpr: "countIf(((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) AND (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)))",
+ expectedArgs: []any{"redis", true, float64(200), true},
+ },
+ {
+ expr: `p05(duration_nano)`,
+ shouldPass: true,
+ expectedExpr: "quantile(0.05)(duration_nano)",
+ },
+ {
+ expr: `rate()`,
+ shouldPass: true,
+ expectedExpr: "count()/60",
+ },
+ {
+ expr: `avg(duration_nano)`,
+ shouldPass: true,
+ expectedExpr: "avg(duration_nano)",
+ },
+ {
+ expr: `sum(total_orders)`,
+ shouldPass: true,
+ expectedExpr: "sum(attributes_number['total_orders'])",
+ },
+ }
+
+ rewriter := querybuilder.NewAggExprRewriter(opts)
+
+ for _, tc := range testCases {
+ t.Run(limitString(tc.expr, 50), func(t *testing.T) {
+ expr, args, err := rewriter.Rewrite(tc.expr)
+ if tc.shouldPass {
+ if err != nil {
+ t.Errorf("Failed to parse query: %s\nError: %v\n", tc.expr, err)
+ return
+ }
+ // Build the SQL and print it for debugging
+ require.Equal(t, tc.expectedExpr, expr)
+ require.Equal(t, tc.expectedArgs, args)
+ } else {
+ require.Error(t, err, "Expected error for query: %s", tc.expr)
+ require.Contains(t, err.Error(), tc.expectedErrorContains)
+ }
+ })
+ }
+}
diff --git a/pkg/telemetrytests/filter_expr_logs_body_json_test.go b/pkg/telemetrytests/filter_expr_logs_body_json_test.go
new file mode 100644
index 0000000000..1cc1b6de91
--- /dev/null
+++ b/pkg/telemetrytests/filter_expr_logs_body_json_test.go
@@ -0,0 +1,190 @@
+package telemetrytests
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/SigNoz/signoz/pkg/querybuilder"
+ "github.com/SigNoz/signoz/pkg/telemetrylogs"
+ "github.com/SigNoz/signoz/pkg/types/telemetrytypes"
+ "github.com/huandu/go-sqlbuilder"
+ "github.com/stretchr/testify/require"
+)
+
+// TestFilterExprLogsBodyJSON tests a comprehensive set of query patterns for body JSON search
+func TestFilterExprLogsBodyJSON(t *testing.T) {
+ fm := telemetrylogs.NewFieldMapper()
+ cb := telemetrylogs.NewConditionBuilder(fm)
+
+ // Define a comprehensive set of field keys to support all test cases
+ keys := buildCompleteFieldKeyMap()
+
+ opts := querybuilder.FilterExprVisitorOpts{
+ FieldMapper: fm,
+ ConditionBuilder: cb,
+ FieldKeys: keys,
+ FullTextColumn: &telemetrytypes.TelemetryFieldKey{
+ Name: "body",
+ },
+ JsonBodyPrefix: "body",
+ JsonKeyToKey: telemetrylogs.GetBodyJSONKey,
+ }
+
+ testCases := []struct {
+ category string
+ query string
+ shouldPass bool
+ expectedQuery string
+ expectedArgs []any
+ expectedErrorContains string
+ }{
+ {
+ category: "json",
+ query: "has(body.requestor_list[*], 'index_service')",
+ shouldPass: true,
+ expectedQuery: `WHERE has(JSONExtract(JSON_QUERY(body, '$."requestor_list"[*]'), 'Array(String)'), ?)`,
+ expectedArgs: []any{"index_service"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "has(body.int_numbers[*], 2)",
+ shouldPass: true,
+ expectedQuery: `WHERE has(JSONExtract(JSON_QUERY(body, '$."int_numbers"[*]'), 'Array(Float64)'), ?)`,
+ expectedArgs: []any{float64(2)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "has(body.bool[*], true)",
+ shouldPass: true,
+ expectedQuery: `WHERE has(JSONExtract(JSON_QUERY(body, '$."bool"[*]'), 'Array(Bool)'), ?)`,
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "NOT has(body.nested_num[*].float_nums[*], 2.2)",
+ shouldPass: true,
+ expectedQuery: `WHERE NOT (has(JSONExtract(JSON_QUERY(body, '$."nested_num"[*]."float_nums"[*]'), 'Array(Float64)'), ?))`,
+ expectedArgs: []any{float64(2.2)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "body.message = hello",
+ shouldPass: true,
+ expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."message"'), 'String') = ? AND JSON_EXISTS(body, '$."message"'))`,
+ expectedArgs: []any{"hello"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "body.status = 1",
+ shouldPass: true,
+ expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."status"'), 'Float64') = ? AND JSON_EXISTS(body, '$."status"'))`,
+ expectedArgs: []any{float64(1)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "body.status = 1.1",
+ shouldPass: true,
+ expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."status"'), 'Float64') = ? AND JSON_EXISTS(body, '$."status"'))`,
+ expectedArgs: []any{float64(1.1)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "body.boolkey = true",
+ shouldPass: true,
+ expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."boolkey"'), 'Bool') = ? AND JSON_EXISTS(body, '$."boolkey"'))`,
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "body.status > 200",
+ shouldPass: true,
+ expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."status"'), 'Float64') > ? AND JSON_EXISTS(body, '$."status"'))`,
+ expectedArgs: []any{float64(200)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "body.message REGEXP 'a*'",
+ shouldPass: true,
+ expectedQuery: `WHERE (match(JSONExtract(JSON_VALUE(body, '$."message"'), 'String'), ?) AND JSON_EXISTS(body, '$."message"'))`,
+ expectedArgs: []any{"a*"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: `body.message CONTAINS "hello 'world'"`,
+ shouldPass: true,
+ expectedQuery: `WHERE (LOWER(JSONExtract(JSON_VALUE(body, '$."message"'), 'String')) LIKE LOWER(?) AND JSON_EXISTS(body, '$."message"'))`,
+ expectedArgs: []any{"%hello 'world'%"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: `body.message EXISTS`,
+ shouldPass: true,
+ expectedQuery: `WHERE JSON_EXISTS(body, '$."message"')`,
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: `body.name IN ('hello', 'world')`,
+ shouldPass: true,
+ expectedQuery: `WHERE ((JSONExtract(JSON_VALUE(body, '$."name"'), 'String') = ? OR JSONExtract(JSON_VALUE(body, '$."name"'), 'String') = ?) AND JSON_EXISTS(body, '$."name"'))`,
+ expectedArgs: []any{"hello", "world"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: `body.value IN (200, 300)`,
+ shouldPass: true,
+ expectedQuery: `WHERE ((JSONExtract(JSON_VALUE(body, '$."value"'), 'Float64') = ? OR JSONExtract(JSON_VALUE(body, '$."value"'), 'Float64') = ?) AND JSON_EXISTS(body, '$."value"'))`,
+ expectedArgs: []any{float64(200), float64(300)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "json",
+ query: "body.key-with-hyphen = true",
+ shouldPass: true,
+ expectedQuery: `WHERE (JSONExtract(JSON_VALUE(body, '$."key-with-hyphen"'), 'Bool') = ? AND JSON_EXISTS(body, '$."key-with-hyphen"'))`,
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(fmt.Sprintf("%s: %s", tc.category, limitString(tc.query, 50)), func(t *testing.T) {
+
+ clause, _, err := querybuilder.PrepareWhereClause(tc.query, opts)
+
+ if tc.shouldPass {
+ if err != nil {
+ t.Errorf("Failed to parse query: %s\nError: %v\n", tc.query, err)
+ return
+ }
+
+ if clause == nil {
+ t.Errorf("Expected clause for query: %s\n", tc.query)
+ return
+ }
+
+ // Build the SQL and print it for debugging
+ sql, args := clause.BuildWithFlavor(sqlbuilder.ClickHouse)
+
+ require.Equal(t, tc.expectedQuery, sql)
+ require.Equal(t, tc.expectedArgs, args)
+
+ } else {
+ require.Error(t, err, "Expected error for query: %s", tc.query)
+ require.Contains(t, err.Error(), tc.expectedErrorContains)
+ }
+ })
+ }
+}
diff --git a/pkg/telemetrytests/filter_expr_logs_test.go b/pkg/telemetrytests/filter_expr_logs_test.go
new file mode 100644
index 0000000000..01e1d76c30
--- /dev/null
+++ b/pkg/telemetrytests/filter_expr_logs_test.go
@@ -0,0 +1,2323 @@
+package telemetrytests
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/SigNoz/signoz/pkg/querybuilder"
+ "github.com/SigNoz/signoz/pkg/telemetrylogs"
+ "github.com/SigNoz/signoz/pkg/types/telemetrytypes"
+ "github.com/huandu/go-sqlbuilder"
+ "github.com/stretchr/testify/require"
+)
+
+// TestFilterExprLogs tests a comprehensive set of query patterns for logs search
+func TestFilterExprLogs(t *testing.T) {
+ fm := telemetrylogs.NewFieldMapper()
+ cb := telemetrylogs.NewConditionBuilder(fm)
+
+ // Define a comprehensive set of field keys to support all test cases
+ keys := buildCompleteFieldKeyMap()
+
+ opts := querybuilder.FilterExprVisitorOpts{
+ FieldMapper: fm,
+ ConditionBuilder: cb,
+ FieldKeys: keys,
+ FullTextColumn: &telemetrytypes.TelemetryFieldKey{
+ Name: "body",
+ },
+ JsonBodyPrefix: "body",
+ JsonKeyToKey: telemetrylogs.GetBodyJSONKey,
+ }
+
+ testCases := []struct {
+ category string
+ query string
+ shouldPass bool
+ expectedQuery string
+ expectedArgs []any
+ expectedErrorContains string
+ }{
+ // Single word searches
+ {
+ category: "Single word",
+ query: "download",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"download"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Single word",
+ query: "LAMBDA",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"LAMBDA"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Single word",
+ query: "AccessDenied",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"AccessDenied"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Single word",
+ query: "42069",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"42069"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Single word",
+ query: "pulljob",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"pulljob"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Single word",
+ query: "",
+ shouldPass: false,
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got '<'",
+ },
+
+ // Single word searches with spaces
+ {
+ category: "Single word with spaces",
+ query: `" 504 "`,
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{" 504 "},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Single word with spaces",
+ query: `"Importing "`,
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"Importing "},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Single word with spaces",
+ query: `"Job ID"`,
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"Job ID"},
+ expectedErrorContains: "",
+ },
+
+ // Searches with special characters
+ {
+ category: "Special characters",
+ query: "[tracing]",
+ shouldPass: false,
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got '['",
+ },
+ {
+ category: "Special characters",
+ query: "srikanth@signoz.io",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"srikanth@signoz.io"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: "cancel_membership",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"cancel_membership"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: `"ERROR: cannot execute update() in a read-only context"`,
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"ERROR: cannot execute update() in a read-only context"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: "ERROR: cannot execute update() in a read-only context",
+ shouldPass: false,
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got ')'",
+ },
+ {
+ category: "Special characters",
+ query: "https://example.com/user/default/0196877a-f01f-785e-a937-5da0a3efbb75",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"https://example.com/user/default/0196877a-f01f-785e-a937-5da0a3efbb75"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: "\"STEPS_PER_DAY\"",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"STEPS_PER_DAY"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: "#bvn",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"#bvn"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: "question?mark",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"question?mark"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: "backslash\\\\escape",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"backslash\\\\escape"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: "underscore_separator",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"underscore_separator"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Special characters",
+ query: "\"Text with [brackets]\"",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"Text with [brackets]"},
+ expectedErrorContains: "",
+ },
+
+ // Multi word searches
+ {
+ category: "Multi word",
+ query: "Fail to parse",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND match(body, ?))",
+ expectedArgs: []any{"Fail", "to", "parse"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Multi word",
+ query: "Importing file",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND match(body, ?))",
+ expectedArgs: []any{"Importing", "file"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Multi word",
+ query: "sync account status",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND match(body, ?))",
+ expectedArgs: []any{"sync", "account", "status"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Multi word",
+ query: "Download CSV Reports",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND match(body, ?))",
+ expectedArgs: []any{"Download", "CSV", "Reports"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Multi word",
+ query: "Emitted event to the Kafka topic",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND match(body, ?) AND match(body, ?) AND match(body, ?) AND match(body, ?))",
+ expectedArgs: []any{"Emitted", "event", "to", "the", "Kafka", "topic"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Multi word",
+ query: "\"user authentication\" failed",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND match(body, ?))",
+ expectedArgs: []any{"user authentication", "failed"},
+ expectedErrorContains: "",
+ },
+
+ // Search for IDs
+ {
+ category: "ID search",
+ query: "250430165501118HIgesxlEb9",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"250430165501118HIgesxlEb9"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "ID search",
+ query: "d7b9d77aefa95aef19719775c10fda60c28342f23657d1e27304d6c59a3c3004",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"d7b9d77aefa95aef19719775c10fda60c28342f23657d1e27304d6c59a3c3004"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "ID search",
+ query: "51183870",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"51183870"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "ID search",
+ query: "79f82635-d014-4f99-adf5-41d31d291ae3",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"79f82635-d014-4f99-adf5-41d31d291ae3"},
+ expectedErrorContains: "",
+ },
+
+ // Unicode characters in full text
+ {
+ category: "Unicode",
+ query: "café",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"café"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Unicode",
+ query: "résumé",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"résumé"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Unicode",
+ query: "Россия",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"Россия"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Unicode",
+ query: "\"I do not like emojis ❤️\"",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"I do not like emojis ❤️"},
+ expectedErrorContains: "",
+ },
+
+ // Various number formats
+ {
+ category: "Number format",
+ query: "123",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"123"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Number format",
+ query: "3.14159",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"3.14159"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Number format",
+ query: "-42",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"-42"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Number format",
+ query: "1e6",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"1e6"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Number format",
+ query: "+100",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"+100"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Number format",
+ query: "0xFF",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"0xFF"},
+ expectedErrorContains: "",
+ },
+
+ // Typical queries that combine FREETEXT with other constructs
+ {
+ category: "FREETEXT with conditions",
+ query: "critical NOT resolved status=open",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND NOT (match(body, ?)) AND (toString(attributes_number['status']) = ? AND mapContains(attributes_number, 'status') = ?))",
+ expectedArgs: []any{"critical", "resolved", "open", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "FREETEXT with conditions",
+ query: "database error type=mysql",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND match(body, ?) AND (attributes_string['type'] = ? AND mapContains(attributes_string, 'type') = ?))",
+ expectedArgs: []any{"database", "error", "mysql", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "FREETEXT with conditions",
+ query: "\"connection timeout\" duration>30",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND (attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?))",
+ expectedArgs: []any{"connection timeout", float64(30), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "FREETEXT with conditions",
+ query: "warning level=critical",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?))",
+ expectedArgs: []any{"warning", "critical", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "FREETEXT with conditions",
+ query: "error service.name=authentication",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{"error", "authentication", true},
+ expectedErrorContains: "",
+ },
+
+ // fulltext with parenthesized expression
+ {
+ category: "FREETEXT with parentheses",
+ query: "error (status.code=500 OR status.code=503)",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND (((attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?) OR (attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?))))",
+ expectedArgs: []any{"error", float64(500), true, float64(503), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "FREETEXT with parentheses",
+ query: "(status.code=500 OR status.code=503) error",
+ shouldPass: true,
+ expectedQuery: "WHERE ((((attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?) OR (attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?))) AND match(body, ?))",
+ expectedArgs: []any{float64(500), true, float64(503), true, "error"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "FREETEXT with parentheses",
+ query: "error AND (status.code=500 OR status.code=503)",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND (((attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?) OR (attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?))))",
+ expectedArgs: []any{"error", float64(500), true, float64(503), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "FREETEXT with parentheses",
+ query: "(status.code=500 OR status.code=503) AND error",
+ shouldPass: true,
+ expectedQuery: "WHERE ((((attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?) OR (attributes_number['status.code'] = ? AND mapContains(attributes_number, 'status.code') = ?))) AND match(body, ?))",
+ expectedArgs: []any{float64(500), true, float64(503), true, "error"},
+ expectedErrorContains: "",
+ },
+
+ // Whitespace with Fulltext
+ {
+ category: "Whitespace with FREETEXT",
+ query: "term1 term2",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(body, ?) AND match(body, ?))",
+ expectedArgs: []any{"term1", "term2"},
+ expectedErrorContains: "",
+ },
+
+ // Conflicts with the key token, are valid and without additonal tokens, they are searched as FREETEXT
+ {
+ category: "Key token conflict",
+ query: "status.code",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"status.code"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Key token conflict",
+ query: "array_field",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"array_field"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Key token conflict",
+ query: "user_id.value",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"user_id.value"},
+ expectedErrorContains: "",
+ }, // Could be a key with dot notation or FREETEXT
+
+ // Random set of cases
+ {
+ category: "Random cases",
+ query: "true",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"true"},
+ expectedErrorContains: "",
+ }, // Could be interpreted as boolean or FREETEXT
+ {
+ category: "Random cases",
+ query: "false",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"false"},
+ expectedErrorContains: "",
+ }, // Could be interpreted as boolean or FREETEXT
+ {
+ category: "Random cases",
+ query: "null",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"null"},
+ expectedErrorContains: "",
+ }, // Special value or FREETEXT
+ {
+ category: "Random cases",
+ query: "123abc",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"123abc"},
+ expectedErrorContains: "",
+ }, // Starts with number but contains letters
+ {
+ category: "Random cases",
+ query: "0x123F",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"0x123F"},
+ expectedErrorContains: "",
+ }, // Hex number format
+ {
+ category: "Random cases",
+ query: "1.2.3",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"1.2.3"},
+ expectedErrorContains: "",
+ }, // Version number format
+ {
+ category: "Random cases",
+ query: "a+b-c*d/e",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"a+b-c*d/e"},
+ expectedErrorContains: "",
+ }, // Mathematical expression as FREETEXT
+ {
+ category: "Random cases",
+ query: "http://example.com/path",
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"http://example.com/path"},
+ expectedErrorContains: "",
+ }, // URL as FREETEXT
+
+ // Keyword conflicting
+ {
+ category: "Keyword conflict",
+ query: "and",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'and'",
+ },
+ {
+ category: "Keyword conflict",
+ query: "or",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'or'",
+ },
+ {
+ category: "Keyword conflict",
+ query: "not",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), FREETEXT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got EOF",
+ },
+ {
+ category: "Keyword conflict",
+ query: "like",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'like'",
+ },
+ {
+ category: "Keyword conflict",
+ query: "between",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'between'",
+ },
+ {
+ category: "Keyword conflict",
+ query: "in",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'in'",
+ },
+ {
+ category: "Keyword conflict",
+ query: "exists",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'exists'",
+ },
+ {
+ category: "Keyword conflict",
+ query: "regexp",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'regexp'",
+ },
+ {
+ category: "Keyword conflict",
+ query: "contains",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'contains'",
+ },
+ {
+ category: "Keyword conflict",
+ query: "has",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, )} but got EOF",
+ },
+ {
+ category: "Keyword conflict",
+ query: "hasany",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, )} but got EOF",
+ },
+ {
+ category: "Keyword conflict",
+ query: "hasall",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "expecting one of {(, )} but got EOF",
+ },
+
+ // Boundary of key-operator-value AND full text
+ {
+ category: "Key-operator-value boundary",
+ query: `"not!equal"`,
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"not!equal"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Key-operator-value boundary",
+ query: "greater>than",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: []any{},
+ expectedErrorContains: "key `greater` not found",
+ },
+ {
+ category: "Key-operator-value boundary",
+ query: `"greater>than"`,
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{"greater>than"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Key-operator-value boundary",
+ query: "less ?",
+ expectedArgs: []any{float64(200)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Not equals",
+ query: "status<>200",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_number['status'] <> ?",
+ expectedArgs: []any{float64(200)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Not equals",
+ query: "code!=400",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_number['code'] <> ?",
+ expectedArgs: []any{float64(400)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Not equals",
+ query: "service.name!=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE resources_string['service.name'] <> ?",
+ expectedArgs: []any{"api"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Not equals",
+ query: "user.email!=\"user@example.com\"",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_string['user.email'] <> ?",
+ expectedArgs: []any{"user@example.com"},
+ expectedErrorContains: "",
+ },
+
+ // Less than
+ {
+ category: "Less than",
+ query: "count<10",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['count'] < ? AND mapContains(attributes_number, 'count') = ?)",
+ expectedArgs: []any{float64(10), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Less than",
+ query: "duration<1000",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?)",
+ expectedArgs: []any{float64(1000), true},
+ expectedErrorContains: "",
+ },
+
+ // Less than or equal
+ {
+ category: "Less than or equal",
+ query: "count<=10",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['count'] <= ? AND mapContains(attributes_number, 'count') = ?)",
+ expectedArgs: []any{float64(10), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Less than or equal",
+ query: "duration<=1000",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['duration'] <= ? AND mapContains(attributes_number, 'duration') = ?)",
+ expectedArgs: []any{float64(1000), true},
+ expectedErrorContains: "",
+ },
+
+ // Greater than
+ {
+ category: "Greater than",
+ query: "count>10",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?)",
+ expectedArgs: []any{float64(10), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Greater than",
+ query: "duration>1000",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?)",
+ expectedArgs: []any{float64(1000), true},
+ expectedErrorContains: "",
+ },
+
+ // Greater than or equal
+ {
+ category: "Greater than or equal",
+ query: "count>=10",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['count'] >= ? AND mapContains(attributes_number, 'count') = ?)",
+ expectedArgs: []any{float64(10), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Greater than or equal",
+ query: "duration>=1000",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['duration'] >= ? AND mapContains(attributes_number, 'duration') = ?)",
+ expectedArgs: []any{float64(1000), true},
+ expectedErrorContains: "",
+ },
+
+ // Basic LIKE
+ {
+ category: "LIKE operator",
+ query: "message LIKE \"%error%\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['message'] LIKE ? AND mapContains(attributes_string, 'message') = ?)",
+ expectedArgs: []any{"%error%", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "LIKE operator",
+ query: "path LIKE \"/api/%\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['path'] LIKE ? AND mapContains(attributes_string, 'path') = ?)",
+ expectedArgs: []any{"/api/%", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "LIKE operator",
+ query: "email LIKE \"%@example.com\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['email'] LIKE ? AND mapContains(attributes_string, 'email') = ?)",
+ expectedArgs: []any{"%@example.com", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "LIKE operator",
+ query: "filename LIKE \"%.pdf\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['filename'] LIKE ? AND mapContains(attributes_string, 'filename') = ?)",
+ expectedArgs: []any{"%.pdf", true},
+ expectedErrorContains: "",
+ },
+
+ // Case-insensitive LIKE (ILIKE)
+ {
+ category: "ILIKE operator",
+ query: "message ILIKE \"%error%\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)",
+ expectedArgs: []any{"%error%", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "ILIKE operator",
+ query: "path ILIKE \"/api/%\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (LOWER(attributes_string['path']) LIKE LOWER(?) AND mapContains(attributes_string, 'path') = ?)",
+ expectedArgs: []any{"/api/%", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "ILIKE operator",
+ query: "email ILIKE \"%@EXAMPLE.com\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (LOWER(attributes_string['email']) LIKE LOWER(?) AND mapContains(attributes_string, 'email') = ?)",
+ expectedArgs: []any{"%@EXAMPLE.com", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "ILIKE operator",
+ query: "filename ILIKE \"%.PDF\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (LOWER(attributes_string['filename']) LIKE LOWER(?) AND mapContains(attributes_string, 'filename') = ?)",
+ expectedArgs: []any{"%.PDF", true},
+ expectedErrorContains: "",
+ },
+
+ // NOT LIKE
+ {
+ category: "NOT LIKE operator",
+ query: "message NOT LIKE \"%error%\"",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_string['message'] NOT LIKE ?",
+ expectedArgs: []any{"%error%"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT LIKE operator",
+ query: "path NOT LIKE \"/api/%\"",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_string['path'] NOT LIKE ?",
+ expectedArgs: []any{"/api/%"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT LIKE operator",
+ query: "email NOT LIKE \"%@example.com\"",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_string['email'] NOT LIKE ?",
+ expectedArgs: []any{"%@example.com"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT LIKE operator",
+ query: "filename NOT LIKE \"%.pdf\"",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_string['filename'] NOT LIKE ?",
+ expectedArgs: []any{"%.pdf"},
+ expectedErrorContains: "",
+ },
+
+ // NOT ILIKE
+ {
+ category: "NOT ILIKE operator",
+ query: "message NOT ILIKE \"%error%\"",
+ shouldPass: true,
+ expectedQuery: "WHERE LOWER(attributes_string['message']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%error%"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT ILIKE operator",
+ query: "path NOT ILIKE \"/api/%\"",
+ shouldPass: true,
+ expectedQuery: "WHERE LOWER(attributes_string['path']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"/api/%"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT ILIKE operator",
+ query: "email NOT ILIKE \"%@EXAMPLE.com\"",
+ shouldPass: true,
+ expectedQuery: "WHERE LOWER(attributes_string['email']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%@EXAMPLE.com"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT ILIKE operator",
+ query: "filename NOT ILIKE \"%.PDF\"",
+ shouldPass: true,
+ expectedQuery: "WHERE LOWER(attributes_string['filename']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%.PDF"},
+ expectedErrorContains: "",
+ },
+
+ // Basic BETWEEN
+ {
+ category: "BETWEEN operator",
+ query: "count BETWEEN 1 AND 10",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['count'] BETWEEN ? AND ? AND mapContains(attributes_number, 'count') = ?)",
+ expectedArgs: []any{float64(1), float64(10), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "BETWEEN operator",
+ query: "duration BETWEEN 100 AND 1000",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['duration'] BETWEEN ? AND ? AND mapContains(attributes_number, 'duration') = ?)",
+ expectedArgs: []any{float64(100), float64(1000), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "BETWEEN operator",
+ query: "amount BETWEEN 0.1 AND 9.9",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['amount'] BETWEEN ? AND ? AND mapContains(attributes_number, 'amount') = ?)",
+ expectedArgs: []any{0.1, 9.9, true},
+ expectedErrorContains: "",
+ },
+
+ // NOT BETWEEN
+ {
+ category: "NOT BETWEEN operator",
+ query: "count NOT BETWEEN 1 AND 10",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_number['count'] NOT BETWEEN ? AND ?",
+ expectedArgs: []any{float64(1), float64(10)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT BETWEEN operator",
+ query: "duration NOT BETWEEN 100 AND 1000",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_number['duration'] NOT BETWEEN ? AND ?",
+ expectedArgs: []any{float64(100), float64(1000)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT BETWEEN operator",
+ query: "amount NOT BETWEEN 0.1 AND 9.9",
+ shouldPass: true,
+ expectedQuery: "WHERE attributes_number['amount'] NOT BETWEEN ? AND ?",
+ expectedArgs: []any{0.1, 9.9},
+ expectedErrorContains: "",
+ },
+
+ // IN with parentheses
+ {
+ category: "IN operator (parentheses)",
+ query: "status IN (200, 201, 202)",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? OR attributes_number['status'] = ? OR attributes_number['status'] = ?) AND mapContains(attributes_number, 'status') = ?)",
+ expectedArgs: []any{float64(200), float64(201), float64(202), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "IN operator (parentheses)",
+ query: "error.code IN (404, 500, 503)",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['error.code'] = ? OR attributes_number['error.code'] = ? OR attributes_number['error.code'] = ?) AND mapContains(attributes_number, 'error.code') = ?)",
+ expectedArgs: []any{float64(404), float64(500), float64(503), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "IN operator (parentheses)",
+ query: "service.name IN (\"api\", \"web\", \"auth\")",
+ shouldPass: true,
+ expectedQuery: "WHERE ((resources_string['service.name'] = ? OR resources_string['service.name'] = ? OR resources_string['service.name'] = ?) AND mapContains(resources_string, 'service.name') = ?)",
+ expectedArgs: []any{"api", "web", "auth", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "IN operator (parentheses)",
+ query: "environment IN (\"dev\", \"test\", \"staging\", \"prod\")",
+ shouldPass: true,
+ expectedQuery: "WHERE ((resources_string['environment'] = ? OR resources_string['environment'] = ? OR resources_string['environment'] = ? OR resources_string['environment'] = ?) AND mapContains(resources_string, 'environment') = ?)",
+ expectedArgs: []any{"dev", "test", "staging", "prod", true},
+ expectedErrorContains: "",
+ },
+
+ // IN with brackets
+ {
+ category: "IN operator (brackets)",
+ query: "status IN [200, 201, 202]",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? OR attributes_number['status'] = ? OR attributes_number['status'] = ?) AND mapContains(attributes_number, 'status') = ?)",
+ expectedArgs: []any{float64(200), float64(201), float64(202), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "IN operator (brackets)",
+ query: "error.code IN [404, 500, 503]",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['error.code'] = ? OR attributes_number['error.code'] = ? OR attributes_number['error.code'] = ?) AND mapContains(attributes_number, 'error.code') = ?)",
+ expectedArgs: []any{float64(404), float64(500), float64(503), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "IN operator (brackets)",
+ query: "service.name IN [\"api\", \"web\", \"auth\"]",
+ shouldPass: true,
+ expectedQuery: "WHERE ((resources_string['service.name'] = ? OR resources_string['service.name'] = ? OR resources_string['service.name'] = ?) AND mapContains(resources_string, 'service.name') = ?)",
+ expectedArgs: []any{"api", "web", "auth", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "IN operator (brackets)",
+ query: "environment IN [\"dev\", \"test\", \"staging\", \"prod\"]",
+ shouldPass: true,
+ expectedQuery: "WHERE ((resources_string['environment'] = ? OR resources_string['environment'] = ? OR resources_string['environment'] = ? OR resources_string['environment'] = ?) AND mapContains(resources_string, 'environment') = ?)",
+ expectedArgs: []any{"dev", "test", "staging", "prod", true},
+ expectedErrorContains: "",
+ },
+
+ // NOT IN with parentheses
+ {
+ category: "NOT IN operator (parentheses)",
+ query: "status NOT IN (400, 500)",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['status'] <> ? AND attributes_number['status'] <> ?)",
+ expectedArgs: []any{float64(400), float64(500)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT IN operator (parentheses)",
+ query: "error.code NOT IN (401, 403)",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['error.code'] <> ? AND attributes_number['error.code'] <> ?)",
+ expectedArgs: []any{float64(401), float64(403)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT IN operator (parentheses)",
+ query: "service.name NOT IN (\"database\", \"cache\")",
+ shouldPass: true,
+ expectedQuery: "WHERE (resources_string['service.name'] <> ? AND resources_string['service.name'] <> ?)",
+ expectedArgs: []any{"database", "cache"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT IN operator (parentheses)",
+ query: "environment NOT IN (\"prod\")",
+ shouldPass: true,
+ expectedQuery: "WHERE (resources_string['environment'] <> ?)",
+ expectedArgs: []any{"prod"},
+ expectedErrorContains: "",
+ },
+
+ // NOT IN with brackets
+ {
+ category: "NOT IN operator (brackets)",
+ query: "status NOT IN [400, 500]",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['status'] <> ? AND attributes_number['status'] <> ?)",
+ expectedArgs: []any{float64(400), float64(500)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT IN operator (brackets)",
+ query: "error.code NOT IN [401, 403]",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['error.code'] <> ? AND attributes_number['error.code'] <> ?)",
+ expectedArgs: []any{float64(401), float64(403)},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT IN operator (brackets)",
+ query: "service.name NOT IN [\"database\", \"cache\"]",
+ shouldPass: true,
+ expectedQuery: "WHERE (resources_string['service.name'] <> ? AND resources_string['service.name'] <> ?)",
+ expectedArgs: []any{"database", "cache"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT IN operator (brackets)",
+ query: "environment NOT IN [\"prod\"]",
+ shouldPass: true,
+ expectedQuery: "WHERE (resources_string['environment'] <> ?)",
+ expectedArgs: []any{"prod"},
+ expectedErrorContains: "",
+ },
+
+ // Basic EXISTS
+ {
+ category: "EXISTS operator",
+ query: "user.id EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'user.id') = ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "EXISTS operator",
+ query: "metadata.version EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'metadata.version') = ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "EXISTS operator",
+ query: "request.headers.authorization EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'request.headers.authorization') = ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "EXISTS operator",
+ query: "response.body.data EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'response.body.data') = ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+
+ // NOT EXISTS
+ {
+ category: "NOT EXISTS operator",
+ query: "user.id NOT EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'user.id') <> ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT EXISTS operator",
+ query: "user.id not exists",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'user.id') <> ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT EXISTS operator",
+ query: "metadata.version NOT EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'metadata.version') <> ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT EXISTS operator",
+ query: "request.headers.authorization NOT EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'request.headers.authorization') <> ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT EXISTS operator",
+ query: "response.body.data NOT EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'response.body.data') <> ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+
+ // Basic REGEXP
+ {
+ category: "REGEXP operator",
+ query: "message REGEXP \"^ERROR:\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(attributes_string['message'], ?) AND mapContains(attributes_string, 'message') = ?)",
+ expectedArgs: []any{"^ERROR:", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "REGEXP operator",
+ query: "email REGEXP \"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(attributes_string['email'], ?) AND mapContains(attributes_string, 'email') = ?)",
+ expectedArgs: []any{"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "REGEXP operator",
+ query: "version REGEXP \"^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(attributes_string['version'], ?) AND mapContains(attributes_string, 'version') = ?)",
+ expectedArgs: []any{"^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "REGEXP operator",
+ query: "path REGEXP \"^/api/v\\\\d+/users/\\\\d+$\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (match(attributes_string['path'], ?) AND mapContains(attributes_string, 'path') = ?)",
+ expectedArgs: []any{"^/api/v\\\\d+/users/\\\\d+$", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "REGEXP operator",
+ query: `"^\[(INFO|WARN|ERROR|DEBUG)\] .+$"`,
+ shouldPass: true,
+ expectedQuery: "WHERE match(body, ?)",
+ expectedArgs: []any{`^\[(INFO|WARN|ERROR|DEBUG)\] .+$`},
+ expectedErrorContains: "",
+ },
+
+ // NOT REGEXP
+ {
+ category: "NOT REGEXP operator",
+ query: "message NOT REGEXP \"^ERROR:\"",
+ shouldPass: true,
+ expectedQuery: "WHERE NOT match(attributes_string['message'], ?)",
+ expectedArgs: []any{"^ERROR:"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT REGEXP operator",
+ query: "email NOT REGEXP \"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$\"",
+ shouldPass: true,
+ expectedQuery: "WHERE NOT match(attributes_string['email'], ?)",
+ expectedArgs: []any{"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT REGEXP operator",
+ query: "version NOT REGEXP \"^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$\"",
+ shouldPass: true,
+ expectedQuery: "WHERE NOT match(attributes_string['version'], ?)",
+ expectedArgs: []any{"^v\\\\d+\\\\.\\\\d+\\\\.\\\\d+$"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT REGEXP operator",
+ query: "path NOT REGEXP \"^/api/v\\\\d+/users/\\\\d+$\"",
+ shouldPass: true,
+ expectedQuery: "WHERE NOT match(attributes_string['path'], ?)",
+ expectedArgs: []any{"^/api/v\\\\d+/users/\\\\d+$"},
+ expectedErrorContains: "",
+ },
+
+ // Basic CONTAINS
+ {
+ category: "CONTAINS operator",
+ query: "message CONTAINS \"error\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)",
+ expectedArgs: []any{"%error%", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "CONTAINS operator",
+ query: "level CONTAINS \"critical\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (LOWER(attributes_string['level']) LIKE LOWER(?) AND mapContains(attributes_string, 'level') = ?)",
+ expectedArgs: []any{"%critical%", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "CONTAINS operator",
+ query: "path CONTAINS \"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (LOWER(attributes_string['path']) LIKE LOWER(?) AND mapContains(attributes_string, 'path') = ?)",
+ expectedArgs: []any{"%api%", true},
+ expectedErrorContains: "",
+ },
+
+ // NOT CONTAINS
+ {
+ category: "NOT CONTAINS operator",
+ query: "message NOT CONTAINS \"error\"",
+ shouldPass: true,
+ expectedQuery: "WHERE LOWER(attributes_string['message']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%error%"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT CONTAINS operator",
+ query: "level NOT CONTAINS \"critical\"",
+ shouldPass: true,
+ expectedQuery: "WHERE LOWER(attributes_string['level']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%critical%"},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT CONTAINS operator",
+ query: "path NOT CONTAINS \"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE LOWER(attributes_string['path']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%api%"},
+ expectedErrorContains: "",
+ },
+
+ // Basic materialized key
+ {
+ category: "Materialized key",
+ query: "materialized.key.name=\"test\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (`attribute_string_materialized$$key$$name` = ? AND `attribute_string_materialized$$key$$name_exists` = ?)",
+ expectedArgs: []any{"test", true},
+ expectedErrorContains: "",
+ },
+
+ // Explicit AND
+ {
+ category: "Explicit AND",
+ query: "status=200 AND service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{float64(200), true, "api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Explicit AND",
+ query: "count>0 AND duration<1000",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))",
+ expectedArgs: []any{float64(0), true, float64(1000), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Explicit AND",
+ query: "message CONTAINS \"error\" AND level=\"ERROR\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?))",
+ expectedArgs: []any{"%error%", true, "ERROR", true},
+ expectedErrorContains: "",
+ },
+
+ // Explicit OR
+ {
+ category: "Explicit OR",
+ query: "status=200 OR status=201",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) OR (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))",
+ expectedArgs: []any{float64(200), true, float64(201), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Explicit OR",
+ query: "service.name=\"api\" OR service.name=\"web\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) OR (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{"api", true, "web", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Explicit OR",
+ query: "count<10 OR count>100",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['count'] < ? AND mapContains(attributes_number, 'count') = ?) OR (attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?))",
+ expectedArgs: []any{float64(10), true, float64(100), true},
+ expectedErrorContains: "",
+ },
+
+ // NOT with various expressions
+ {
+ category: "NOT with expressions",
+ query: "NOT status=200",
+ shouldPass: true,
+ expectedQuery: "WHERE NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))",
+ expectedArgs: []any{float64(200), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT with expressions",
+ query: "NOT service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE NOT ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{"api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "NOT with expressions",
+ query: "NOT count>10",
+ shouldPass: true,
+ expectedQuery: "WHERE NOT ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?))",
+ expectedArgs: []any{float64(10), true},
+ expectedErrorContains: "",
+ },
+
+ // AND + OR combinations
+ {
+ category: "AND + OR combinations",
+ query: "status=200 AND (service.name=\"api\" OR service.name=\"web\")",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) OR (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))))",
+ expectedArgs: []any{float64(200), true, "api", true, "web", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "AND + OR combinations",
+ query: "(count>10 AND count<100) OR (duration>1000 AND duration<5000)",
+ shouldPass: true,
+ expectedQuery: "WHERE ((((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_number['count'] < ? AND mapContains(attributes_number, 'count') = ?))) OR (((attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))))",
+ expectedArgs: []any{float64(10), true, float64(100), true, float64(1000), true, float64(5000), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "AND + OR combinations",
+ query: "level=\"ERROR\" OR (level=\"WARN\" AND message CONTAINS \"timeout\")",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) OR (((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) AND (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?))))",
+ expectedArgs: []any{"ERROR", true, "WARN", true, "%timeout%", true},
+ expectedErrorContains: "",
+ },
+
+ // AND + NOT combinations
+ {
+ category: "AND + NOT combinations",
+ query: "status=200 AND NOT service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND NOT ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))",
+ expectedArgs: []any{float64(200), true, "api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "AND + NOT combinations",
+ query: "count>0 AND NOT error.code EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND NOT (mapContains(attributes_number, 'error.code') = ?))",
+ expectedArgs: []any{float64(0), true, true},
+ expectedErrorContains: "",
+ },
+
+ // OR + NOT combinations
+ {
+ category: "OR + NOT combinations",
+ query: "NOT status=200 OR NOT service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)) OR NOT ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))",
+ expectedArgs: []any{float64(200), true, "api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "OR + NOT combinations",
+ query: "NOT count>0 OR NOT error.code EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE (NOT ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?)) OR NOT (mapContains(attributes_number, 'error.code') = ?))",
+ expectedArgs: []any{float64(0), true, true},
+ expectedErrorContains: "",
+ },
+
+ // AND + OR + NOT combinations
+ {
+ category: "AND + OR + NOT combinations",
+ query: "status=200 AND (service.name=\"api\" OR NOT duration>1000)",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) OR NOT ((attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?)))))",
+ expectedArgs: []any{float64(200), true, "api", true, float64(1000), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "AND + OR + NOT combinations",
+ query: "(level=\"ERROR\" OR level=\"FATAL\") AND NOT message CONTAINS \"expected\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) OR (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?))) AND NOT ((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)))",
+ expectedArgs: []any{"ERROR", true, "FATAL", true, "%expected%", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "AND + OR + NOT combinations",
+ query: "NOT (status=200 AND service.name=\"api\") OR count>0",
+ shouldPass: true,
+ expectedQuery: "WHERE (NOT ((((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))) OR (attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?))",
+ expectedArgs: []any{float64(200), true, "api", true, float64(0), true},
+ expectedErrorContains: "",
+ },
+
+ // Multiple expressions without explicit AND
+ {
+ category: "Implicit AND",
+ query: "status=200 service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{float64(200), true, "api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Implicit AND",
+ query: "count>0 duration<1000",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))",
+ expectedArgs: []any{float64(0), true, float64(1000), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Implicit AND",
+ query: "message CONTAINS \"error\" level=\"ERROR\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?))",
+ expectedArgs: []any{"%error%", true, "ERROR", true},
+ expectedErrorContains: "",
+ },
+
+ // Mixed implicit and explicit AND
+ {
+ category: "Mixed implicit/explicit AND",
+ query: "status=200 AND service.name=\"api\" duration<1000",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))",
+ expectedArgs: []any{float64(200), true, "api", true, float64(1000), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Mixed implicit/explicit AND",
+ query: "count>0 level=\"ERROR\" AND message CONTAINS \"error\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) AND (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?))",
+ expectedArgs: []any{float64(0), true, "ERROR", true, "%error%", true},
+ expectedErrorContains: "",
+ },
+
+ // Simple grouping
+ {
+ category: "Simple grouping",
+ query: "(status=200)",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))",
+ expectedArgs: []any{float64(200), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Simple grouping",
+ query: "service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)",
+ expectedArgs: []any{"api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Simple grouping",
+ query: "(count>0)",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?))",
+ expectedArgs: []any{float64(0), true},
+ expectedErrorContains: "",
+ },
+
+ // Nested grouping
+ {
+ category: "Nested grouping",
+ query: "((status=200))",
+ shouldPass: true,
+ expectedQuery: "WHERE (((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)))",
+ expectedArgs: []any{float64(200), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Nested grouping",
+ query: "(((service.name=\"api\")))",
+ shouldPass: true,
+ expectedQuery: "WHERE ((((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))))",
+ expectedArgs: []any{"api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Nested grouping",
+ query: "((count>0) AND (duration<1000))",
+ shouldPass: true,
+ expectedQuery: "WHERE ((((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?)) AND ((attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?))))",
+ expectedArgs: []any{float64(0), true, float64(1000), true},
+ expectedErrorContains: "",
+ },
+
+ // Complex nested grouping
+ {
+ category: "Complex nested grouping",
+ query: "(status=200 AND (service.name=\"api\" OR service.name=\"web\"))",
+ shouldPass: true,
+ expectedQuery: "WHERE (((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) OR (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))))",
+ expectedArgs: []any{float64(200), true, "api", true, "web", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Complex nested grouping",
+ query: "((count>0 AND count<100) OR (duration>1000 AND duration<5000))",
+ shouldPass: true,
+ expectedQuery: "WHERE (((((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (attributes_number['count'] < ? AND mapContains(attributes_number, 'count') = ?))) OR (((attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?) AND (attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?)))))",
+ expectedArgs: []any{float64(0), true, float64(100), true, float64(1000), true, float64(5000), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Complex nested grouping",
+ query: "(level=\"ERROR\" OR (level=\"WARN\" AND (message CONTAINS \"timeout\" OR message CONTAINS \"connection\")))",
+ shouldPass: true,
+ expectedQuery: "WHERE (((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) OR (((attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?) AND (((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?) OR (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)))))))",
+ expectedArgs: []any{"ERROR", true, "WARN", true, "%timeout%", true, "%connection%", true},
+ expectedErrorContains: "",
+ },
+
+ // Deep nesting with mixed operators
+ {
+ category: "Deep nesting",
+ query: "(((status=200 OR status=201) AND service.name=\"api\") OR ((status=202 OR status=203) AND service.name=\"web\"))",
+ shouldPass: true,
+ expectedQuery: "WHERE (((((((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) OR (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))) OR (((((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) OR (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?))) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))))",
+ expectedArgs: []any{float64(200), true, float64(201), true, "api", true, float64(202), true, float64(203), true, "web", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Deep nesting",
+ query: "(count>0 AND ((duration<1000 AND service.name=\"api\") OR (duration<500 AND service.name=\"web\")))",
+ shouldPass: true,
+ expectedQuery: "WHERE (((attributes_number['count'] > ? AND mapContains(attributes_number, 'count') = ?) AND (((((attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))) OR (((attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))))))",
+ expectedArgs: []any{float64(0), true, float64(1000), true, "api", true, float64(500), true, "web", true},
+ expectedErrorContains: "",
+ },
+
+ // String values with different quote styles
+ {
+ category: "String quote styles",
+ query: "service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)",
+ expectedArgs: []any{"api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "String quote styles",
+ query: "service.name='api'",
+ shouldPass: true,
+ expectedQuery: "WHERE (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)",
+ expectedArgs: []any{"api", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "String quote styles",
+ query: "message=\"This is a \\\"quoted\\\" message\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['message'] = ? AND mapContains(attributes_string, 'message') = ?)",
+ expectedArgs: []any{"This is a \\\"quoted\\\" message", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "String quote styles",
+ query: "message='This is a \\'quoted\\' message'",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['message'] = ? AND mapContains(attributes_string, 'message') = ?)",
+ expectedArgs: []any{"This is a \\'quoted\\' message", true},
+ expectedErrorContains: "",
+ },
+
+ // Numeric values
+ {
+ category: "Numeric values",
+ query: "status=200",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)",
+ expectedArgs: []any{float64(200), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Numeric values",
+ query: "count=0",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['count'] = ? AND mapContains(attributes_number, 'count') = ?)",
+ expectedArgs: []any{float64(0), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Numeric values",
+ query: "duration=1000.5",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['duration'] = ? AND mapContains(attributes_number, 'duration') = ?)",
+ expectedArgs: []any{float64(1000.5), true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Numeric values",
+ query: "amount=-10.25",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['amount'] = ? AND mapContains(attributes_number, 'amount') = ?)",
+ expectedArgs: []any{float64(-10.25), true},
+ expectedErrorContains: "",
+ },
+
+ // Boolean values
+ {
+ category: "Boolean values",
+ query: "isEnabled=true",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_bool['isEnabled'] = ? AND mapContains(attributes_bool, 'isEnabled') = ?)",
+ expectedArgs: []any{true, true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Boolean values",
+ query: "isDisabled=false",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_bool['isDisabled'] = ? AND mapContains(attributes_bool, 'isDisabled') = ?)",
+ expectedArgs: []any{false, true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Boolean values",
+ query: "is_valid=TRUE",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_bool['is_valid'] = ? AND mapContains(attributes_bool, 'is_valid') = ?)",
+ expectedArgs: []any{true, true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Boolean values",
+ query: "is_invalid=FALSE",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_bool['is_invalid'] = ? AND mapContains(attributes_bool, 'is_invalid') = ?)",
+ expectedArgs: []any{false, true},
+ expectedErrorContains: "",
+ },
+
+ // Null and undefined values, there is no such type as "null" or "undefined"
+ // they are just strings
+ {
+ category: "Null values",
+ query: "value=null",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['value'] = ? AND mapContains(attributes_string, 'value') = ?)",
+ expectedArgs: []any{"null", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Null values",
+ query: "value=undefined",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['value'] = ? AND mapContains(attributes_string, 'value') = ?)",
+ expectedArgs: []any{"undefined", true},
+ expectedErrorContains: "",
+ },
+
+ // Nested object paths
+ {
+ category: "Nested object paths",
+ query: "user.profile.name=\"John\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['user.profile.name'] = ? AND mapContains(attributes_string, 'user.profile.name') = ?)",
+ expectedArgs: []any{"John", true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Nested object paths",
+ query: "request.headers.authorization EXISTS",
+ shouldPass: true,
+ expectedQuery: "WHERE mapContains(attributes_string, 'request.headers.authorization') = ?",
+ expectedArgs: []any{true},
+ expectedErrorContains: "",
+ },
+ {
+ category: "Nested object paths",
+ query: "response.body.data.items[].id=123",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "key `response.body.data.items[].id` not found",
+ },
+ {
+ category: "Nested object paths",
+ query: "metadata.dimensions.width>1000",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_number['metadata.dimensions.width'] > ? AND mapContains(attributes_number, 'metadata.dimensions.width') = ?)",
+ expectedArgs: []any{float64(1000), true},
+ expectedErrorContains: "",
+ },
+
+ {category: "Only keywords", query: "AND", shouldPass: false, expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'AND'"},
+ {category: "Only keywords", query: "OR", shouldPass: false, expectedErrorContains: "expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'OR'"},
+ {category: "Only keywords", query: "NOT", shouldPass: false, expectedErrorContains: "expecting one of {(, ), FREETEXT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got EOF"},
+
+ {category: "Only functions", query: "has", shouldPass: false, expectedErrorContains: "expecting one of {(, )} but got EOF"},
+ {category: "Only functions", query: "hasAny", shouldPass: false, expectedErrorContains: "expecting one of {(, )} but got EOF"},
+ {category: "Only functions", query: "hasAll", shouldPass: false, expectedErrorContains: "expecting one of {(, )} but got EOF"},
+
+ {category: "Values without keys", query: "=200", shouldPass: false, expectedErrorContains: "but got '='"},
+ {category: "Values without keys", query: "=\"api\"", shouldPass: false, expectedErrorContains: "but got '='"},
+ {category: "Values without keys", query: ">10", shouldPass: false, expectedErrorContains: "but got '>'"},
+ {category: "Values without keys", query: "BETWEEN 1 AND 10", shouldPass: false, expectedErrorContains: "but got 'BETWEEN'"},
+
+ // Testing NOT > AND > OR precedence
+ {
+ category: "Operator precedence",
+ query: "NOT status=200 AND service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{float64(200), true, "api", true}, // Should be (NOT status=200) AND service.name="api"
+ },
+ {
+ category: "Operator precedence",
+ query: "status=200 AND service.name=\"api\" OR service.name=\"web\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)) OR (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{float64(200), true, "api", true, "web", true}, // Should be (status=200 AND service.name="api") OR service.name="web"
+ },
+ {
+ category: "Operator precedence",
+ query: "NOT status=200 OR NOT service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)) OR NOT ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?)))",
+ expectedArgs: []any{float64(200), true, "api", true}, // Should be (NOT status=200) OR (NOT service.name="api")
+ },
+ {
+ category: "Operator precedence",
+ query: "status=200 OR service.name=\"api\" AND level=\"ERROR\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) OR ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) AND (attributes_string['level'] = ? AND mapContains(attributes_string, 'level') = ?)))",
+ expectedArgs: []any{float64(200), true, "api", true, "ERROR", true}, // Should be status=200 OR (service.name="api" AND level="ERROR")
+ },
+
+ // Different whitespace patterns
+ {
+ category: "Whitespace patterns",
+ query: "status=200AND service.name=\"api\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {boolean, number, quoted text} but got '200AND'",
+ },
+ {
+ category: "Whitespace patterns",
+ query: "status=200ANDservice.name=\"api\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {boolean, number, quoted text} but got '200ANDservice.name'",
+ },
+ {
+ category: "Whitespace patterns",
+ query: "status=200 AND service.name=\"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{float64(200), true, "api", true}, // Multiple spaces
+ },
+
+ // More Unicode characters
+ {
+ category: "Unicode",
+ query: "message=\"café\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['message'] = ? AND mapContains(attributes_string, 'message') = ?)",
+ expectedArgs: []any{"café", true},
+ },
+ {
+ category: "Unicode",
+ query: "user.name=\"Jörg Müller\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['user.name'] = ? AND mapContains(attributes_string, 'user.name') = ?)",
+ expectedArgs: []any{"Jörg Müller", true},
+ },
+ {
+ category: "Unicode",
+ query: "location=\"東京\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['location'] = ? AND mapContains(attributes_string, 'location') = ?)",
+ expectedArgs: []any{"東京", true},
+ },
+ {
+ category: "Unicode",
+ query: "description=\"Это тестовое сообщение\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['description'] = ? AND mapContains(attributes_string, 'description') = ?)",
+ expectedArgs: []any{"Это тестовое сообщение", true},
+ },
+
+ // Special characters
+ {
+ category: "Special characters",
+ query: "path=\"/special/!@#$%^&*()\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['path'] = ? AND mapContains(attributes_string, 'path') = ?)",
+ expectedArgs: []any{"/special/!@#$%^&*()", true},
+ },
+ {
+ category: "Special characters",
+ query: "query=\"?param1=value1¶m2=value2\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['query'] = ? AND mapContains(attributes_string, 'query') = ?)",
+ expectedArgs: []any{"?param1=value1¶m2=value2", true},
+ },
+
+ // Special characters in keys
+ {
+ category: "Special characters in keys",
+ query: "special-key=\"value\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['special-key'] = ? AND mapContains(attributes_string, 'special-key') = ?)",
+ expectedArgs: []any{"value", true}, // With hyphen
+ },
+ {
+ category: "Special characters in keys",
+ query: "special.key=\"value\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['special.key'] = ? AND mapContains(attributes_string, 'special.key') = ?)",
+ expectedArgs: []any{"value", true}, // With dot
+ },
+ {
+ category: "Special characters in keys",
+ query: "special_key=\"value\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['special_key'] = ? AND mapContains(attributes_string, 'special_key') = ?)",
+ expectedArgs: []any{"value", true}, // With underscore
+ },
+
+ // Using operator keywords as keys
+ {
+ category: "Operator keywords as keys",
+ query: "and=true",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'and'",
+ },
+ {
+ category: "Operator keywords as keys",
+ query: "or=false",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'or'",
+ },
+ {
+ category: "Operator keywords as keys",
+ query: "not=\"value\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "line 1:3 expecting one of {(, ), FREETEXT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got '='",
+ },
+ {
+ category: "Operator keywords as keys",
+ query: "between=10",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'between'",
+ },
+ {
+ category: "Operator keywords as keys",
+ query: "in=\"list\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "line 1:0 expecting one of {(, ), AND, FREETEXT, NOT, boolean, has(), hasAll(), hasAny(), number, quoted text} but got 'in'",
+ },
+
+ // Using function keywords as keys
+ {
+ category: "Function keywords as keys",
+ query: "has=\"function\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {(, )} but got '='",
+ },
+ {
+ category: "Function keywords as keys",
+ query: "hasAny=\"function\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {(, )} but got '='",
+ },
+ {
+ category: "Function keywords as keys",
+ query: "hasAll=\"function\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {(, )} but got '='",
+ },
+
+ // Common filter patterns
+ {
+ category: "Common filters",
+ query: "status IN (\"pending\", \"processing\", \"completed\") AND NOT is_deleted=true",
+ shouldPass: true,
+ expectedQuery: "WHERE (((attributes_number['status'] = ? OR attributes_number['status'] = ? OR attributes_number['status'] = ?) AND mapContains(attributes_number, 'status') = ?) AND NOT ((attributes_bool['is_deleted'] = ? AND mapContains(attributes_bool, 'is_deleted') = ?)))",
+ expectedArgs: []any{"pending", "processing", "completed", true, true, true},
+ },
+ {
+ category: "Common filters",
+ query: "(first_name LIKE \"John%\" OR last_name LIKE \"Smith%\") AND age>=18",
+ shouldPass: true,
+ expectedQuery: "WHERE ((((attributes_string['first_name'] LIKE ? AND mapContains(attributes_string, 'first_name') = ?) OR (attributes_string['last_name'] LIKE ? AND mapContains(attributes_string, 'last_name') = ?))) AND (attributes_number['age'] >= ? AND mapContains(attributes_number, 'age') = ?))",
+ expectedArgs: []any{"John%", true, "Smith%", true, float64(18), true},
+ },
+ {
+ category: "Common filters",
+ query: "user_id=12345 AND (subscription_type=\"premium\" OR has(features, \"advanced\"))",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "key `user_id` not found",
+ },
+
+ // More common filter patterns
+ {
+ category: "More common filters",
+ query: "service.name=\"api\" AND (status>=500 OR duration>1000) AND NOT message CONTAINS \"expected\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?) AND (((attributes_number['status'] >= ? AND mapContains(attributes_number, 'status') = ?) OR (attributes_number['duration'] > ? AND mapContains(attributes_number, 'duration') = ?))) AND NOT ((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?)))",
+ expectedArgs: []any{"api", true, float64(500), true, float64(1000), true, "%expected%", true},
+ },
+
+ // Edge cases
+ {
+ category: "empty array",
+ query: "key IN ()",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {boolean, number, quoted text}",
+ },
+ {
+ category: "empty array",
+ query: "key IN []",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {boolean, number, quoted text}",
+ },
+ {
+ category: "empty array",
+ query: "key NOT IN ()",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {boolean, number, quoted text}",
+ },
+ {
+ category: "empty array",
+ query: "key NOT IN []",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {boolean, number, quoted text}",
+ },
+
+ // Values with escaping
+ {
+ category: "Escaped values",
+ query: "message=\"Line 1\\nLine 2\\tTabbed\\rCarriage return\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['message'] = ? AND mapContains(attributes_string, 'message') = ?)",
+ expectedArgs: []any{"Line 1\\nLine 2\\tTabbed\\rCarriage return", true},
+ },
+ {
+ category: "Escaped values",
+ query: "path=\"C:\\\\Program Files\\\\Application\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['path'] = ? AND mapContains(attributes_string, 'path') = ?)",
+ expectedArgs: []any{"C:\\\\Program Files\\\\Application", true},
+ },
+ {
+ category: "Escaped values",
+ query: "path=\"^prefix\\\\.suffix$\\\\d+\\\\w+\"",
+ shouldPass: true,
+ expectedQuery: "WHERE (attributes_string['path'] = ? AND mapContains(attributes_string, 'path') = ?)",
+ expectedArgs: []any{"^prefix\\\\.suffix$\\\\d+\\\\w+", true},
+ },
+
+ // Inconsistent/unusual whitespace
+ {
+ category: "Unusual whitespace",
+ query: "status = 200 AND service.name = \"api\"",
+ shouldPass: true,
+ expectedQuery: "WHERE ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?) AND (resources_string['service.name'] = ? AND mapContains(resources_string, 'service.name') = ?))",
+ expectedArgs: []any{float64(200), true, "api", true},
+ },
+ {
+ category: "Unusual whitespace",
+ query: "status=200AND service.name=\"api\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {boolean, number, quoted text} but got '200AND'",
+ },
+ {
+ category: "Unusual whitespace",
+ query: "status=200ANDservice.name=\"api\"",
+ shouldPass: false,
+ expectedQuery: "",
+ expectedArgs: nil,
+ expectedErrorContains: "expecting one of {boolean, number, quoted text} but got '200ANDservice.name'",
+ },
+
+ // Very complex query example
+ {
+ category: "Complex query example",
+ query: `
+ (
+ (
+ (status>=200 AND status<300) OR
+ (status>=400 AND status<500 AND NOT status=404)
+ ) AND
+ (
+ service.name IN ("api", "web", "auth") OR
+ (
+ service.type="internal" AND
+ NOT service.deprecated=true
+ )
+ )
+ ) AND
+ (
+ (
+ duration<1000 OR
+ (
+ duration BETWEEN 1000 AND 5000
+ )
+ ) AND
+ (
+ environment!="test" OR
+ (
+ environment="test" AND
+ is_automated_test=true
+ )
+ )
+ ) AND
+ NOT (
+ (
+ message CONTAINS "warning" OR
+ message CONTAINS "deprecated"
+ ) AND
+ severity="low"
+ )
+ `,
+ shouldPass: true,
+ expectedQuery: "WHERE ((((((((attributes_number['status'] >= ? AND mapContains(attributes_number, 'status') = ?) AND (attributes_number['status'] < ? AND mapContains(attributes_number, 'status') = ?))) OR (((attributes_number['status'] >= ? AND mapContains(attributes_number, 'status') = ?) AND (attributes_number['status'] < ? AND mapContains(attributes_number, 'status') = ?) AND NOT ((attributes_number['status'] = ? AND mapContains(attributes_number, 'status') = ?)))))) AND ((((resources_string['service.name'] = ? OR resources_string['service.name'] = ? OR resources_string['service.name'] = ?) AND mapContains(resources_string, 'service.name') = ?) OR (((resources_string['service.type'] = ? AND mapContains(resources_string, 'service.type') = ?) AND NOT ((resources_string['service.deprecated'] = ? AND mapContains(resources_string, 'service.deprecated') = ?)))))))) AND (((((attributes_number['duration'] < ? AND mapContains(attributes_number, 'duration') = ?) OR ((attributes_number['duration'] BETWEEN ? AND ? AND mapContains(attributes_number, 'duration') = ?)))) AND ((resources_string['environment'] <> ? OR (((resources_string['environment'] = ? AND mapContains(resources_string, 'environment') = ?) AND (attributes_bool['is_automated_test'] = ? AND mapContains(attributes_bool, 'is_automated_test') = ?))))))) AND NOT ((((((LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?) OR (LOWER(attributes_string['message']) LIKE LOWER(?) AND mapContains(attributes_string, 'message') = ?))) AND (attributes_string['severity'] = ? AND mapContains(attributes_string, 'severity') = ?)))))",
+ expectedArgs: []any{
+ float64(200), true, float64(300), true, float64(400), true, float64(500), true, float64(404), true,
+ "api", "web", "auth", true,
+ "internal", true, true, true,
+ float64(1000), true, float64(1000), float64(5000), true,
+ "test", "test", true, true, true,
+ "%warning%", true, "%deprecated%", true,
+ "low", true,
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(fmt.Sprintf("%s: %s", tc.category, limitString(tc.query, 50)), func(t *testing.T) {
+
+ clause, _, err := querybuilder.PrepareWhereClause(tc.query, opts)
+
+ if tc.shouldPass {
+ if err != nil {
+ t.Errorf("Failed to parse query: %s\nError: %v\n", tc.query, err)
+ return
+ }
+
+ if clause == nil {
+ t.Errorf("Expected clause for query: %s\n", tc.query)
+ return
+ }
+
+ // Build the SQL and print it for debugging
+ sql, args := clause.BuildWithFlavor(sqlbuilder.ClickHouse)
+
+ require.Equal(t, tc.expectedQuery, sql)
+ require.Equal(t, tc.expectedArgs, args)
+ } else {
+ require.Error(t, err, "Expected error for query: %s", tc.query)
+ require.Contains(t, err.Error(), tc.expectedErrorContains)
+ }
+ })
+ }
+}
diff --git a/pkg/telemetrytests/test_data.go b/pkg/telemetrytests/test_data.go
new file mode 100644
index 0000000000..039ed6effb
--- /dev/null
+++ b/pkg/telemetrytests/test_data.go
@@ -0,0 +1,859 @@
+package telemetrytests
+
+import (
+ "strings"
+
+ "github.com/SigNoz/signoz/pkg/types/telemetrytypes"
+)
+
+// Helper function to limit string length for display
+func limitString(s string, maxLen int) string {
+ s = strings.ReplaceAll(s, "\n", " ")
+ s = strings.ReplaceAll(s, "\t", " ")
+
+ if len(s) <= maxLen {
+ return s
+ }
+ return s[:maxLen-3] + "..."
+}
+
+// Function to build a complete field key map for testing all scenarios
+func buildCompleteFieldKeyMap() map[string][]*telemetrytypes.TelemetryFieldKey {
+ return map[string][]*telemetrytypes.TelemetryFieldKey{
+ "service.name": {
+ {
+ Name: "service.name",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "http.status_code": {
+ {
+ Name: "http.status_code",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "status": {
+ {
+ Name: "status",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "code": {
+ {
+ Name: "code",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "count": {
+ {
+ Name: "count",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "duration": {
+ {
+ Name: "duration",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "message": {
+ {
+ Name: "message",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "path": {
+ {
+ Name: "path",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "email": {
+ {
+ Name: "email",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "filename": {
+ {
+ Name: "filename",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "amount": {
+ {
+ Name: "amount",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "error.code": {
+ {
+ Name: "error.code",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "environment": {
+ {
+ Name: "environment",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "user.id": {
+ {
+ Name: "user.id",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "metadata.version": {
+ {
+ Name: "metadata.version",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "request.headers.authorization": {
+ {
+ Name: "request.headers.authorization",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "response.body.data": {
+ {
+ Name: "response.body.data",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "version": {
+ {
+ Name: "version",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "response.headers": {
+ {
+ Name: "response.headers",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "request.query_params": {
+ {
+ Name: "request.query_params",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "level": {
+ {
+ Name: "level",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "user.status": {
+ {
+ Name: "user.status",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "isEnabled": {
+ {
+ Name: "isEnabled",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "isDisabled": {
+ {
+ Name: "isDisabled",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "is_valid": {
+ {
+ Name: "is_valid",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "is_invalid": {
+ {
+ Name: "is_invalid",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "key": {
+ {
+ Name: "key",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "status.code": {
+ {
+ Name: "status.code",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ // Special fields for tests
+ "value": {
+ {
+ Name: "value",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "threshold": {
+ {
+ Name: "threshold",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "warning_threshold": {
+ {
+ Name: "warning_threshold",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "critical_threshold": {
+ {
+ Name: "critical_threshold",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "type": {
+ {
+ Name: "type",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "age": {
+ {
+ Name: "age",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "user.email": {
+ {
+ Name: "user.email",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "user.name": {
+ {
+ Name: "user.name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "user.profile.name": {
+ {
+ Name: "user.profile.name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "region": {
+ {
+ Name: "region",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "service.type": {
+ {
+ Name: "service.type",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "service.deprecated": {
+ {
+ Name: "service.deprecated",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "is_automated_test": {
+ {
+ Name: "is_automated_test",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "severity": {
+ {
+ Name: "severity",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "created_at": {
+ {
+ Name: "created_at",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeNumber,
+ },
+ },
+ "is_deleted": {
+ {
+ Name: "is_deleted",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "customer.type": {
+ {
+ Name: "customer.type",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "total_orders": {
+ {
+ Name: "total_orders",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "total_spent": {
+ {
+ Name: "total_spent",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "items[].product.category": {
+ {
+ Name: "items[].product.category",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "items[].license_type": {
+ {
+ Name: "items[].license_type",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "first_name": {
+ {
+ Name: "first_name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "last_name": {
+ {
+ Name: "last_name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "address.country": {
+ {
+ Name: "address.country",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "address.state": {
+ {
+ Name: "address.state",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "address.city": {
+ {
+ Name: "address.city",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "subscription.plan": {
+ {
+ Name: "subscription.plan",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "subscription.status": {
+ {
+ Name: "subscription.status",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "is_expected": {
+ {
+ Name: "is_expected",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "http.method": {
+ {
+ Name: "http.method",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "http.path": {
+ {
+ Name: "http.path",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "http.status": {
+ {
+ Name: "http.status",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "response.body.error": {
+ {
+ Name: "response.body.error",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "metric.name": {
+ {
+ Name: "metric.name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "metric.value": {
+ {
+ Name: "metric.value",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "metric.rate_of_change": {
+ {
+ Name: "metric.rate_of_change",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "resource.type": {
+ {
+ Name: "resource.type",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "resource.environment": {
+ {
+ Name: "resource.environment",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "resource.is_critical": {
+ {
+ Name: "resource.is_critical",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "metric.is_monitored": {
+ {
+ Name: "metric.is_monitored",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "aggregation.window": {
+ {
+ Name: "aggregation.window",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "aggregation.function": {
+ {
+ Name: "aggregation.function",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "action": {
+ {
+ Name: "action",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "host": {
+ {
+ Name: "host",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "metric.type": {
+ {
+ Name: "metric.type",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "rate": {
+ {
+ Name: "rate",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "delta": {
+ {
+ Name: "delta",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "alerting": {
+ {
+ Name: "alerting",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "aggregation": {
+ {
+ Name: "aggregation",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "user.created_at": {
+ {
+ Name: "user.created_at",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeNumber,
+ },
+ },
+ "user.type": {
+ {
+ Name: "user.type",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "user.last_login": {
+ {
+ Name: "user.last_login",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeNumber,
+ },
+ },
+ "user.failed_logins": {
+ {
+ Name: "user.failed_logins",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "user.department": {
+ {
+ Name: "user.department",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "user.region": {
+ {
+ Name: "user.region",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "user.quota": {
+ {
+ Name: "user.quota",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "users[].role": {
+ {
+ Name: "users[].role",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "users[].status": {
+ {
+ Name: "users[].status",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "orders[].items[].product.id": {
+ {
+ Name: "orders[].items[].product.id",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "data.metrics[].value": {
+ {
+ Name: "data.metrics[].value",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeFloat64,
+ },
+ },
+ "data.metrics[].name": {
+ {
+ Name: "data.metrics[].name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "requests[].response.status": {
+ {
+ Name: "requests[].response.status",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ // Unicode characters in keys
+ "école.name": {
+ {
+ Name: "école.name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "straße.name": {
+ {
+ Name: "straße.name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "日本語.text": {
+ {
+ Name: "日本語.text",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "россия.capital": {
+ {
+ Name: "россия.capital",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ // Special characters in keys
+ "special-key": {
+ {
+ Name: "special-key",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "special.key": {
+ {
+ Name: "special.key",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "special_key": {
+ {
+ Name: "special_key",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "special:key": {
+ {
+ Name: "special:key",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "key-with-dashes": {
+ {
+ Name: "key-with-dashes",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "key_with_underscore": {
+ {
+ Name: "key_with_underscore",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ // Nested paths with dots
+ "and.value": {
+ {
+ Name: "and.value",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "or.status": {
+ {
+ Name: "or.status",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "not.enabled": {
+ {
+ Name: "not.enabled",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "like.pattern": {
+ {
+ Name: "like.pattern",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "between.min": {
+ {
+ Name: "between.min",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "between.max": {
+ {
+ Name: "between.max",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "exists.flag": {
+ {
+ Name: "exists.flag",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeBool,
+ },
+ },
+ "contains.text": {
+ {
+ Name: "contains.text",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "items[0].name": {
+ {
+ Name: "items[0].name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "errors[].code": {
+ {
+ Name: "errors[].code",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "metadata.dimensions": {
+ {
+ Name: "metadata.dimensions",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "metadata.dimensions.width": {
+ {
+ Name: "metadata.dimensions.width",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeInt64,
+ },
+ },
+ "subscription_type": {
+ {
+ Name: "subscription_type",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ // Resource-related fields from original example
+ "resource.k8s.namespace.name": {
+ {
+ Name: "resource.k8s.namespace.name",
+ FieldContext: telemetrytypes.FieldContextResource,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ // Add location field for Unicode test
+ "location": {
+ {
+ Name: "location",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ // Add description field for Unicode test
+ "description": {
+ {
+ Name: "description",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ // Add query field for special characters test
+ "query": {
+ {
+ Name: "query",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ },
+ "materialized.key.name": {
+ {
+ Name: "materialized.key.name",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ Materialized: true,
+ },
+ },
+ }
+}
diff --git a/pkg/telemetrytraces/condition_builder.go b/pkg/telemetrytraces/condition_builder.go
index 0f18a3b56e..eacb3d24ae 100644
--- a/pkg/telemetrytraces/condition_builder.go
+++ b/pkg/telemetrytraces/condition_builder.go
@@ -3,6 +3,7 @@ package telemetrytraces
import (
"context"
"fmt"
+ "slices"
schema "github.com/SigNoz/signoz-otel-collector/cmd/signozschemamigrator/schema_migrator"
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
@@ -20,7 +21,7 @@ func NewConditionBuilder(fm qbtypes.FieldMapper) *conditionBuilder {
return &conditionBuilder{fm: fm}
}
-func (c *conditionBuilder) ConditionFor(
+func (c *conditionBuilder) conditionFor(
ctx context.Context,
key *telemetrytypes.TelemetryFieldKey,
operator qbtypes.FilterOperator,
@@ -73,11 +74,9 @@ func (c *conditionBuilder) ConditionFor(
return sb.NotILike(tblFieldName, fmt.Sprintf("%%%s%%", value)), nil
case qbtypes.FilterOperatorRegexp:
- exp := fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value))
- return sb.And(exp), nil
+ return fmt.Sprintf(`match(%s, %s)`, tblFieldName, sb.Var(value)), nil
case qbtypes.FilterOperatorNotRegexp:
- exp := fmt.Sprintf(`not match(%s, %s)`, tblFieldName, sb.Var(value))
- return sb.And(exp), nil
+ return fmt.Sprintf(`NOT match(%s, %s)`, tblFieldName, sb.Var(value)), nil
// between and not between
case qbtypes.FilterOperatorBetween:
@@ -105,13 +104,23 @@ func (c *conditionBuilder) ConditionFor(
if !ok {
return "", qbtypes.ErrInValues
}
- return sb.In(tblFieldName, values...), nil
+ // instead of using IN, we use `=` + `OR` to make use of index
+ conditions := []string{}
+ for _, value := range values {
+ conditions = append(conditions, sb.E(tblFieldName, value))
+ }
+ return sb.Or(conditions...), nil
case qbtypes.FilterOperatorNotIn:
values, ok := value.([]any)
if !ok {
return "", qbtypes.ErrInValues
}
- return sb.NotIn(tblFieldName, values...), nil
+ // instead of using NOT IN, we use `!=` + `AND` to make use of index
+ conditions := []string{}
+ for _, value := range values {
+ conditions = append(conditions, sb.NE(tblFieldName, value))
+ }
+ return sb.And(conditions...), nil
// exists and not exists
// in the query builder, `exists` and `not exists` are used for
@@ -166,3 +175,31 @@ func (c *conditionBuilder) ConditionFor(
}
return "", nil
}
+
+func (c *conditionBuilder) ConditionFor(
+ ctx context.Context,
+ key *telemetrytypes.TelemetryFieldKey,
+ operator qbtypes.FilterOperator,
+ value any,
+ sb *sqlbuilder.SelectBuilder,
+) (string, error) {
+ condition, err := c.conditionFor(ctx, key, operator, value, sb)
+ if err != nil {
+ return "", err
+ }
+
+ if operator.AddDefaultExistsFilter() {
+ // skip adding exists filter for intrinsic fields
+ field, _ := c.fm.FieldFor(ctx, key)
+ if slices.Contains(IntrinsicFields, field) || slices.Contains(CalculatedFields, field) {
+ return condition, nil
+ }
+
+ existsCondition, err := c.conditionFor(ctx, key, qbtypes.FilterOperatorExists, nil, sb)
+ if err != nil {
+ return "", err
+ }
+ return sb.And(condition, existsCondition), nil
+ }
+ return condition, nil
+}
diff --git a/pkg/telemetrytraces/condition_builder_test.go b/pkg/telemetrytraces/condition_builder_test.go
index 1e7f8df264..a990648639 100644
--- a/pkg/telemetrytraces/condition_builder_test.go
+++ b/pkg/telemetrytraces/condition_builder_test.go
@@ -20,6 +20,7 @@ func TestConditionFor(t *testing.T) {
operator qbtypes.FilterOperator
value any
expectedSQL string
+ expectedArgs []any
expectedError error
}{
{
@@ -31,6 +32,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorNotEqual,
value: uint64(1617979338000000000),
expectedSQL: "timestamp <> ?",
+ expectedArgs: []any{uint64(1617979338000000000)},
expectedError: nil,
},
{
@@ -42,7 +44,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorGreaterThan,
value: float64(100),
- expectedSQL: "attributes_number['request.duration'] > ?",
+ expectedSQL: "(attributes_number['request.duration'] > ? AND mapContains(attributes_number, 'request.duration') = ?)",
+ expectedArgs: []any{float64(100), true},
expectedError: nil,
},
{
@@ -54,7 +57,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorLessThan,
value: float64(1024),
- expectedSQL: "attributes_number['request.size'] < ?",
+ expectedSQL: "(attributes_number['request.size'] < ? AND mapContains(attributes_number, 'request.size') = ?)",
+ expectedArgs: []any{float64(1024), true},
expectedError: nil,
},
{
@@ -66,6 +70,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorGreaterThanOrEq,
value: uint64(1617979338000000000),
expectedSQL: "timestamp >= ?",
+ expectedArgs: []any{uint64(1617979338000000000)},
expectedError: nil,
},
{
@@ -77,6 +82,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorLessThanOrEq,
value: uint64(1617979338000000000),
expectedSQL: "timestamp <= ?",
+ expectedArgs: []any{uint64(1617979338000000000)},
expectedError: nil,
},
{
@@ -88,7 +94,8 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorILike,
value: "%admin%",
- expectedSQL: "WHERE LOWER(attributes_string['user.id']) LIKE LOWER(?)",
+ expectedSQL: "(LOWER(attributes_string['user.id']) LIKE LOWER(?) AND mapContains(attributes_string, 'user.id') = ?)",
+ expectedArgs: []any{"%admin%", true},
expectedError: nil,
},
{
@@ -101,6 +108,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorNotILike,
value: "%admin%",
expectedSQL: "WHERE LOWER(attributes_string['user.id']) NOT LIKE LOWER(?)",
+ expectedArgs: []any{"%admin%", true},
expectedError: nil,
},
{
@@ -112,6 +120,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorBetween,
value: []any{uint64(1617979338000000000), uint64(1617979348000000000)},
expectedSQL: "timestamp BETWEEN ? AND ?",
+ expectedArgs: []any{uint64(1617979338000000000), uint64(1617979348000000000)},
expectedError: nil,
},
{
@@ -145,6 +154,7 @@ func TestConditionFor(t *testing.T) {
operator: qbtypes.FilterOperatorNotBetween,
value: []any{uint64(1617979338000000000), uint64(1617979348000000000)},
expectedSQL: "timestamp NOT BETWEEN ? AND ?",
+ expectedArgs: []any{uint64(1617979338000000000), uint64(1617979348000000000)},
expectedError: nil,
},
{
@@ -180,7 +190,34 @@ func TestConditionFor(t *testing.T) {
},
operator: qbtypes.FilterOperatorContains,
value: "admin",
- expectedSQL: "WHERE LOWER(attributes_string['user.id']) LIKE LOWER(?)",
+ expectedSQL: "(LOWER(attributes_string['user.id']) LIKE LOWER(?) AND mapContains(attributes_string, 'user.id') = ?)",
+ expectedArgs: []any{"%admin%", true},
+ expectedError: nil,
+ },
+ {
+ name: "In operator - map field",
+ key: telemetrytypes.TelemetryFieldKey{
+ Name: "user.id",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ operator: qbtypes.FilterOperatorIn,
+ value: []any{"admin", "user"},
+ expectedSQL: "((attributes_string['user.id'] = ? OR attributes_string['user.id'] = ?) AND mapContains(attributes_string, 'user.id') = ?)",
+ expectedArgs: []any{"admin", "user", true},
+ expectedError: nil,
+ },
+ {
+ name: "Not In operator - map field",
+ key: telemetrytypes.TelemetryFieldKey{
+ Name: "user.id",
+ FieldContext: telemetrytypes.FieldContextAttribute,
+ FieldDataType: telemetrytypes.FieldDataTypeString,
+ },
+ operator: qbtypes.FilterOperatorNotIn,
+ value: []any{"admin", "user"},
+ expectedSQL: "(attributes_string['user.id'] <> ? AND attributes_string['user.id'] <> ?)",
+ expectedArgs: []any{"admin", "user", true},
expectedError: nil,
},
{
diff --git a/pkg/telemetrytraces/const.go b/pkg/telemetrytraces/const.go
new file mode 100644
index 0000000000..45fcdb2965
--- /dev/null
+++ b/pkg/telemetrytraces/const.go
@@ -0,0 +1,51 @@
+package telemetrytraces
+
+var (
+ IntrinsicFields = []string{
+ "timestamp",
+ "trace_id",
+ "span_id",
+ "trace_state",
+ "parent_span_id",
+ "flags",
+ "name",
+ "kind",
+ "kind_string",
+ "duration_nano",
+ "status_code",
+ "status_message",
+ "status_code_string",
+ "traceID",
+ "spanID",
+ "parentSpanID",
+ "spanKind",
+ "durationNano",
+ "statusCode",
+ "statusMessage",
+ "statusCodeString",
+ }
+
+ CalculatedFields = []string{
+ "response_status_code",
+ "external_http_url",
+ "http_url",
+ "external_http_method",
+ "http_method",
+ "http_host",
+ "db_name",
+ "db_operation",
+ "has_error",
+ "is_remote",
+
+ "responseStatusCode",
+ "externalHttpUrl",
+ "httpUrl",
+ "externalHttpMethod",
+ "httpMethod",
+ "httpHost",
+ "dbName",
+ "dbOperation",
+ "hasError",
+ "isRemote",
+ }
+)
diff --git a/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go b/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go
index 856cd7a15b..9f282bb5dd 100644
--- a/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go
+++ b/pkg/types/querybuildertypes/querybuildertypesv5/builder_elements.go
@@ -82,6 +82,35 @@ const (
FilterOperatorNotContains
)
+// AddDefaultExistsFilter returns true if addl exists filter should be added to the query
+// For the negative predicates, we don't want to add the exists filter. Why?
+// Say for example, user adds a filter `service.name != "redis"`, we can't interpret it
+// unambiguously i.e do they mean to fetch logs that satisfy
+// service.name != "redis" or do they mean to fetch logs that have `service.name` field and
+// doesn't have value "redis"
+// Since we don't know the intent, we don't add the exists filter. They are expected
+// to add exists filter themselves if exclusion is desired.
+//
+// For the positive predicates, the key existence is implied.
+func (f FilterOperator) AddDefaultExistsFilter() bool {
+ switch f {
+ case
+ FilterOperatorEqual,
+ FilterOperatorGreaterThan,
+ FilterOperatorGreaterThanOrEq,
+ FilterOperatorLessThan,
+ FilterOperatorLessThanOrEq,
+ FilterOperatorLike,
+ FilterOperatorILike,
+ FilterOperatorBetween,
+ FilterOperatorIn,
+ FilterOperatorRegexp,
+ FilterOperatorContains:
+ return true
+ }
+ return false
+}
+
type OrderDirection struct {
valuer.String
}
diff --git a/pkg/types/telemetrytypes/field.go b/pkg/types/telemetrytypes/field.go
index db31a510d0..cb8a08883c 100644
--- a/pkg/types/telemetrytypes/field.go
+++ b/pkg/types/telemetrytypes/field.go
@@ -99,11 +99,11 @@ func GetFieldKeyFromKeyText(key string) TelemetryFieldKey {
}
func FieldKeyToMaterializedColumnName(key *TelemetryFieldKey) string {
- return fmt.Sprintf("%s_%s_%s", key.FieldContext.String, fieldDataTypes[key.FieldDataType.StringValue()].StringValue(), strings.ReplaceAll(key.Name, ".", "$$"))
+ return fmt.Sprintf("`%s_%s_%s`", key.FieldContext.String, fieldDataTypes[key.FieldDataType.StringValue()].StringValue(), strings.ReplaceAll(key.Name, ".", "$$"))
}
func FieldKeyToMaterializedColumnNameForExists(key *TelemetryFieldKey) string {
- return fmt.Sprintf("%s_%s_%s_exists", key.FieldContext.String, fieldDataTypes[key.FieldDataType.StringValue()].StringValue(), strings.ReplaceAll(key.Name, ".", "$$"))
+ return fmt.Sprintf("`%s_%s_%s_exists`", key.FieldContext.String, fieldDataTypes[key.FieldDataType.StringValue()].StringValue(), strings.ReplaceAll(key.Name, ".", "$$"))
}
type TelemetryFieldValues struct {