diff --git a/pkg/kql/dictionary.peg b/pkg/kql/dictionary.peg index c21f46f4c4..ad3c44977c 100644 --- a/pkg/kql/dictionary.peg +++ b/pkg/kql/dictionary.peg @@ -7,10 +7,13 @@ //////////////////////////////////////////////////////// AST <- - n:Nodes { + _ n:Nodes _ EOF { return buildAST(n, c.text, c.pos) } +EOF <- + !. + //////////////////////////////////////////////////////// // nodes //////////////////////////////////////////////////////// diff --git a/pkg/kql/dictionary_gen.go b/pkg/kql/dictionary_gen.go index 9777ece435..d24cbbfeba 100644 --- a/pkg/kql/dictionary_gen.go +++ b/pkg/kql/dictionary_gen.go @@ -24,31 +24,70 @@ var g = &grammar{ expr: &actionExpr{ pos: position{line: 10, col: 5, offset: 154}, run: (*parser).callonAST1, - expr: &labeledExpr{ - pos: position{line: 10, col: 5, offset: 154}, - label: "n", - expr: &ruleRefExpr{ - pos: position{line: 10, col: 7, offset: 156}, - name: "Nodes", + expr: &seqExpr{ + pos: position{line: 10, col: 5, offset: 154}, + exprs: []any{ + &actionExpr{ + pos: position{line: 260, col: 5, offset: 5359}, + run: (*parser).callonAST3, + expr: &zeroOrMoreExpr{ + pos: position{line: 260, col: 5, offset: 5359}, + expr: &charClassMatcher{ + pos: position{line: 260, col: 5, offset: 5359}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + &labeledExpr{ + pos: position{line: 10, col: 7, offset: 156}, + label: "n", + expr: &ruleRefExpr{ + pos: position{line: 10, col: 9, offset: 158}, + name: "Nodes", + }, + }, + &actionExpr{ + pos: position{line: 260, col: 5, offset: 5359}, + run: (*parser).callonAST8, + expr: &zeroOrMoreExpr{ + pos: position{line: 260, col: 5, offset: 5359}, + expr: &charClassMatcher{ + pos: position{line: 260, col: 5, offset: 5359}, + val: "[ \\t]", + chars: []rune{' ', '\t'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + ¬Expr{ + pos: position{line: 15, col: 5, offset: 232}, + expr: &anyMatcher{ + line: 15, col: 6, offset: 233, + }, + }, }, }, }, }, { name: "Nodes", - pos: position{line: 18, col: 1, offset: 337}, + pos: position{line: 21, col: 1, offset: 360}, expr: &oneOrMoreExpr{ - pos: position{line: 19, col: 5, offset: 350}, + pos: position{line: 22, col: 5, offset: 373}, expr: &seqExpr{ - pos: position{line: 19, col: 6, offset: 351}, + pos: position{line: 22, col: 6, offset: 374}, exprs: []any{ &actionExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, run: (*parser).callonNodes3, expr: &zeroOrMoreExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, expr: &charClassMatcher{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -57,7 +96,7 @@ var g = &grammar{ }, }, &ruleRefExpr{ - pos: position{line: 19, col: 8, offset: 353}, + pos: position{line: 22, col: 8, offset: 376}, name: "Node", }, }, @@ -66,36 +105,36 @@ var g = &grammar{ }, { name: "Node", - pos: position{line: 21, col: 1, offset: 361}, + pos: position{line: 24, col: 1, offset: 384}, expr: &choiceExpr{ - pos: position{line: 22, col: 5, offset: 373}, + pos: position{line: 25, col: 5, offset: 396}, alternatives: []any{ &ruleRefExpr{ - pos: position{line: 22, col: 5, offset: 373}, + pos: position{line: 25, col: 5, offset: 396}, name: "GroupNode", }, &actionExpr{ - pos: position{line: 47, col: 5, offset: 1066}, + pos: position{line: 50, col: 5, offset: 1089}, run: (*parser).callonNode3, expr: &seqExpr{ - pos: position{line: 47, col: 5, offset: 1066}, + pos: position{line: 50, col: 5, offset: 1089}, exprs: []any{ &labeledExpr{ - pos: position{line: 47, col: 5, offset: 1066}, + pos: position{line: 50, col: 5, offset: 1089}, label: "k", expr: &actionExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, run: (*parser).callonNode6, expr: &seqExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode9, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -104,23 +143,23 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 237, col: 11, offset: 5083}, + pos: position{line: 240, col: 11, offset: 5106}, expr: &seqExpr{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, exprs: []any{ &litMatcher{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 237, col: 16, offset: 5088}, + pos: position{line: 240, col: 16, offset: 5111}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode15, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -136,23 +175,23 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 47, col: 12, offset: 1073}, + pos: position{line: 50, col: 12, offset: 1096}, alternatives: []any{ &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonNode18, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, run: (*parser).callonNode20, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, val: "=", ignoreCase: false, want: "\"=\"", @@ -161,19 +200,19 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 47, col: 51, offset: 1112}, + pos: position{line: 50, col: 51, offset: 1135}, label: "v", expr: &choiceExpr{ - pos: position{line: 47, col: 54, offset: 1115}, + pos: position{line: 50, col: 54, offset: 1138}, alternatives: []any{ &litMatcher{ - pos: position{line: 47, col: 54, offset: 1115}, + pos: position{line: 50, col: 54, offset: 1138}, val: "true", ignoreCase: false, want: "\"true\"", }, &litMatcher{ - pos: position{line: 47, col: 63, offset: 1124}, + pos: position{line: 50, col: 63, offset: 1147}, val: "false", ignoreCase: false, want: "\"false\"", @@ -185,27 +224,27 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 52, col: 5, offset: 1225}, + pos: position{line: 55, col: 5, offset: 1248}, run: (*parser).callonNode26, expr: &seqExpr{ - pos: position{line: 52, col: 5, offset: 1225}, + pos: position{line: 55, col: 5, offset: 1248}, exprs: []any{ &labeledExpr{ - pos: position{line: 52, col: 5, offset: 1225}, + pos: position{line: 55, col: 5, offset: 1248}, label: "k", expr: &actionExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, run: (*parser).callonNode29, expr: &seqExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode32, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -214,23 +253,23 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 237, col: 11, offset: 5083}, + pos: position{line: 240, col: 11, offset: 5106}, expr: &seqExpr{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, exprs: []any{ &litMatcher{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 237, col: 16, offset: 5088}, + pos: position{line: 240, col: 16, offset: 5111}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode38, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -246,66 +285,66 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 52, col: 11, offset: 1231}, + pos: position{line: 55, col: 11, offset: 1254}, label: "o", expr: &choiceExpr{ - pos: position{line: 53, col: 9, offset: 1243}, + pos: position{line: 56, col: 9, offset: 1266}, alternatives: []any{ &actionExpr{ - pos: position{line: 159, col: 5, offset: 3745}, + pos: position{line: 162, col: 5, offset: 3768}, run: (*parser).callonNode42, expr: &litMatcher{ - pos: position{line: 159, col: 5, offset: 3745}, + pos: position{line: 162, col: 5, offset: 3768}, val: ">=", ignoreCase: false, want: "\">=\"", }, }, &actionExpr{ - pos: position{line: 149, col: 5, offset: 3561}, + pos: position{line: 152, col: 5, offset: 3584}, run: (*parser).callonNode44, expr: &litMatcher{ - pos: position{line: 149, col: 5, offset: 3561}, + pos: position{line: 152, col: 5, offset: 3584}, val: "<=", ignoreCase: false, want: "\"<=\"", }, }, &actionExpr{ - pos: position{line: 154, col: 5, offset: 3650}, + pos: position{line: 157, col: 5, offset: 3673}, run: (*parser).callonNode46, expr: &litMatcher{ - pos: position{line: 154, col: 5, offset: 3650}, + pos: position{line: 157, col: 5, offset: 3673}, val: ">", ignoreCase: false, want: "\">\"", }, }, &actionExpr{ - pos: position{line: 144, col: 5, offset: 3469}, + pos: position{line: 147, col: 5, offset: 3492}, run: (*parser).callonNode48, expr: &litMatcher{ - pos: position{line: 144, col: 5, offset: 3469}, + pos: position{line: 147, col: 5, offset: 3492}, val: "<", ignoreCase: false, want: "\"<\"", }, }, &actionExpr{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, run: (*parser).callonNode50, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, val: "=", ignoreCase: false, want: "\"=\"", }, }, &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonNode52, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", @@ -315,43 +354,43 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 59, col: 7, offset: 1423}, + pos: position{line: 62, col: 7, offset: 1446}, expr: &litMatcher{ - pos: position{line: 59, col: 7, offset: 1423}, + pos: position{line: 62, col: 7, offset: 1446}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, }, &labeledExpr{ - pos: position{line: 59, col: 12, offset: 1428}, + pos: position{line: 62, col: 12, offset: 1451}, label: "v", expr: &choiceExpr{ - pos: position{line: 60, col: 9, offset: 1440}, + pos: position{line: 63, col: 9, offset: 1463}, alternatives: []any{ &actionExpr{ - pos: position{line: 209, col: 5, offset: 4584}, + pos: position{line: 212, col: 5, offset: 4607}, run: (*parser).callonNode58, expr: &seqExpr{ - pos: position{line: 209, col: 5, offset: 4584}, + pos: position{line: 212, col: 5, offset: 4607}, exprs: []any{ &actionExpr{ - pos: position{line: 199, col: 5, offset: 4347}, + pos: position{line: 202, col: 5, offset: 4370}, run: (*parser).callonNode60, expr: &seqExpr{ - pos: position{line: 199, col: 5, offset: 4347}, + pos: position{line: 202, col: 5, offset: 4370}, exprs: []any{ &actionExpr{ - pos: position{line: 169, col: 5, offset: 3947}, + pos: position{line: 172, col: 5, offset: 3970}, run: (*parser).callonNode62, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3947}, + pos: position{line: 172, col: 5, offset: 3970}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode64, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -359,10 +398,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode66, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -370,10 +409,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode68, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -381,10 +420,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode70, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -395,22 +434,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 199, col: 14, offset: 4356}, + pos: position{line: 202, col: 14, offset: 4379}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 174, col: 5, offset: 4024}, + pos: position{line: 177, col: 5, offset: 4047}, run: (*parser).callonNode73, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 4024}, + pos: position{line: 177, col: 5, offset: 4047}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode75, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -418,10 +457,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode77, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -432,22 +471,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 199, col: 28, offset: 4370}, + pos: position{line: 202, col: 28, offset: 4393}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 179, col: 5, offset: 4087}, + pos: position{line: 182, col: 5, offset: 4110}, run: (*parser).callonNode80, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 4087}, + pos: position{line: 182, col: 5, offset: 4110}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode82, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -455,10 +494,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode84, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -472,28 +511,28 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 209, col: 14, offset: 4593}, + pos: position{line: 212, col: 14, offset: 4616}, val: "T", ignoreCase: false, want: "\"T\"", }, &actionExpr{ - pos: position{line: 204, col: 5, offset: 4434}, + pos: position{line: 207, col: 5, offset: 4457}, run: (*parser).callonNode87, expr: &seqExpr{ - pos: position{line: 204, col: 5, offset: 4434}, + pos: position{line: 207, col: 5, offset: 4457}, exprs: []any{ &actionExpr{ - pos: position{line: 184, col: 5, offset: 4151}, + pos: position{line: 187, col: 5, offset: 4174}, run: (*parser).callonNode89, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 4151}, + pos: position{line: 187, col: 5, offset: 4174}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode91, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -501,10 +540,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode93, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -515,22 +554,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 204, col: 14, offset: 4443}, + pos: position{line: 207, col: 14, offset: 4466}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 189, col: 5, offset: 4217}, + pos: position{line: 192, col: 5, offset: 4240}, run: (*parser).callonNode96, expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4217}, + pos: position{line: 192, col: 5, offset: 4240}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode98, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -538,10 +577,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode100, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -552,22 +591,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 204, col: 29, offset: 4458}, + pos: position{line: 207, col: 29, offset: 4481}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 194, col: 5, offset: 4283}, + pos: position{line: 197, col: 5, offset: 4306}, run: (*parser).callonNode103, expr: &seqExpr{ - pos: position{line: 194, col: 5, offset: 4283}, + pos: position{line: 197, col: 5, offset: 4306}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode105, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -575,10 +614,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode107, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -589,23 +628,23 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 204, col: 44, offset: 4473}, + pos: position{line: 207, col: 44, offset: 4496}, expr: &seqExpr{ - pos: position{line: 204, col: 45, offset: 4474}, + pos: position{line: 207, col: 45, offset: 4497}, exprs: []any{ &litMatcher{ - pos: position{line: 204, col: 45, offset: 4474}, + pos: position{line: 207, col: 45, offset: 4497}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 204, col: 49, offset: 4478}, + pos: position{line: 207, col: 49, offset: 4501}, expr: &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode113, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -617,35 +656,35 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 204, col: 59, offset: 4488}, + pos: position{line: 207, col: 59, offset: 4511}, alternatives: []any{ &litMatcher{ - pos: position{line: 204, col: 59, offset: 4488}, + pos: position{line: 207, col: 59, offset: 4511}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 204, col: 65, offset: 4494}, + pos: position{line: 207, col: 65, offset: 4517}, exprs: []any{ &charClassMatcher{ - pos: position{line: 204, col: 66, offset: 4495}, + pos: position{line: 207, col: 66, offset: 4518}, val: "[+-]", chars: []rune{'+', '-'}, ignoreCase: false, inverted: false, }, &actionExpr{ - pos: position{line: 184, col: 5, offset: 4151}, + pos: position{line: 187, col: 5, offset: 4174}, run: (*parser).callonNode119, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 4151}, + pos: position{line: 187, col: 5, offset: 4174}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode121, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -653,10 +692,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode123, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -667,22 +706,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 204, col: 86, offset: 4515}, + pos: position{line: 207, col: 86, offset: 4538}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 189, col: 5, offset: 4217}, + pos: position{line: 192, col: 5, offset: 4240}, run: (*parser).callonNode126, expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4217}, + pos: position{line: 192, col: 5, offset: 4240}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode128, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -690,10 +729,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode130, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -714,22 +753,22 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 199, col: 5, offset: 4347}, + pos: position{line: 202, col: 5, offset: 4370}, run: (*parser).callonNode132, expr: &seqExpr{ - pos: position{line: 199, col: 5, offset: 4347}, + pos: position{line: 202, col: 5, offset: 4370}, exprs: []any{ &actionExpr{ - pos: position{line: 169, col: 5, offset: 3947}, + pos: position{line: 172, col: 5, offset: 3970}, run: (*parser).callonNode134, expr: &seqExpr{ - pos: position{line: 169, col: 5, offset: 3947}, + pos: position{line: 172, col: 5, offset: 3970}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode136, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -737,10 +776,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode138, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -748,10 +787,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode140, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -759,10 +798,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode142, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -773,22 +812,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 199, col: 14, offset: 4356}, + pos: position{line: 202, col: 14, offset: 4379}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 174, col: 5, offset: 4024}, + pos: position{line: 177, col: 5, offset: 4047}, run: (*parser).callonNode145, expr: &seqExpr{ - pos: position{line: 174, col: 5, offset: 4024}, + pos: position{line: 177, col: 5, offset: 4047}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode147, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -796,10 +835,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode149, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -810,22 +849,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 199, col: 28, offset: 4370}, + pos: position{line: 202, col: 28, offset: 4393}, val: "-", ignoreCase: false, want: "\"-\"", }, &actionExpr{ - pos: position{line: 179, col: 5, offset: 4087}, + pos: position{line: 182, col: 5, offset: 4110}, run: (*parser).callonNode152, expr: &seqExpr{ - pos: position{line: 179, col: 5, offset: 4087}, + pos: position{line: 182, col: 5, offset: 4110}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode154, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -833,10 +872,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode156, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -850,22 +889,22 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 204, col: 5, offset: 4434}, + pos: position{line: 207, col: 5, offset: 4457}, run: (*parser).callonNode158, expr: &seqExpr{ - pos: position{line: 204, col: 5, offset: 4434}, + pos: position{line: 207, col: 5, offset: 4457}, exprs: []any{ &actionExpr{ - pos: position{line: 184, col: 5, offset: 4151}, + pos: position{line: 187, col: 5, offset: 4174}, run: (*parser).callonNode160, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 4151}, + pos: position{line: 187, col: 5, offset: 4174}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode162, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -873,10 +912,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode164, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -887,22 +926,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 204, col: 14, offset: 4443}, + pos: position{line: 207, col: 14, offset: 4466}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 189, col: 5, offset: 4217}, + pos: position{line: 192, col: 5, offset: 4240}, run: (*parser).callonNode167, expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4217}, + pos: position{line: 192, col: 5, offset: 4240}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode169, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -910,10 +949,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode171, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -924,22 +963,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 204, col: 29, offset: 4458}, + pos: position{line: 207, col: 29, offset: 4481}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 194, col: 5, offset: 4283}, + pos: position{line: 197, col: 5, offset: 4306}, run: (*parser).callonNode174, expr: &seqExpr{ - pos: position{line: 194, col: 5, offset: 4283}, + pos: position{line: 197, col: 5, offset: 4306}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode176, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -947,10 +986,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode178, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -961,23 +1000,23 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 204, col: 44, offset: 4473}, + pos: position{line: 207, col: 44, offset: 4496}, expr: &seqExpr{ - pos: position{line: 204, col: 45, offset: 4474}, + pos: position{line: 207, col: 45, offset: 4497}, exprs: []any{ &litMatcher{ - pos: position{line: 204, col: 45, offset: 4474}, + pos: position{line: 207, col: 45, offset: 4497}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 204, col: 49, offset: 4478}, + pos: position{line: 207, col: 49, offset: 4501}, expr: &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode184, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -989,35 +1028,35 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 204, col: 59, offset: 4488}, + pos: position{line: 207, col: 59, offset: 4511}, alternatives: []any{ &litMatcher{ - pos: position{line: 204, col: 59, offset: 4488}, + pos: position{line: 207, col: 59, offset: 4511}, val: "Z", ignoreCase: false, want: "\"Z\"", }, &seqExpr{ - pos: position{line: 204, col: 65, offset: 4494}, + pos: position{line: 207, col: 65, offset: 4517}, exprs: []any{ &charClassMatcher{ - pos: position{line: 204, col: 66, offset: 4495}, + pos: position{line: 207, col: 66, offset: 4518}, val: "[+-]", chars: []rune{'+', '-'}, ignoreCase: false, inverted: false, }, &actionExpr{ - pos: position{line: 184, col: 5, offset: 4151}, + pos: position{line: 187, col: 5, offset: 4174}, run: (*parser).callonNode190, expr: &seqExpr{ - pos: position{line: 184, col: 5, offset: 4151}, + pos: position{line: 187, col: 5, offset: 4174}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode192, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1025,10 +1064,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode194, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1039,22 +1078,22 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 204, col: 86, offset: 4515}, + pos: position{line: 207, col: 86, offset: 4538}, val: ":", ignoreCase: false, want: "\":\"", }, &actionExpr{ - pos: position{line: 189, col: 5, offset: 4217}, + pos: position{line: 192, col: 5, offset: 4240}, run: (*parser).callonNode197, expr: &seqExpr{ - pos: position{line: 189, col: 5, offset: 4217}, + pos: position{line: 192, col: 5, offset: 4240}, exprs: []any{ &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode199, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1062,10 +1101,10 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, run: (*parser).callonNode201, expr: &charClassMatcher{ - pos: position{line: 252, col: 5, offset: 5285}, + pos: position{line: 255, col: 5, offset: 5308}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1086,9 +1125,9 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 63, col: 7, offset: 1493}, + pos: position{line: 66, col: 7, offset: 1516}, expr: &litMatcher{ - pos: position{line: 63, col: 7, offset: 1493}, + pos: position{line: 66, col: 7, offset: 1516}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1098,27 +1137,27 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 66, col: 5, offset: 1569}, + pos: position{line: 69, col: 5, offset: 1592}, run: (*parser).callonNode205, expr: &seqExpr{ - pos: position{line: 66, col: 5, offset: 1569}, + pos: position{line: 69, col: 5, offset: 1592}, exprs: []any{ &labeledExpr{ - pos: position{line: 66, col: 5, offset: 1569}, + pos: position{line: 69, col: 5, offset: 1592}, label: "k", expr: &actionExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, run: (*parser).callonNode208, expr: &seqExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode211, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1127,23 +1166,23 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 237, col: 11, offset: 5083}, + pos: position{line: 240, col: 11, offset: 5106}, expr: &seqExpr{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, exprs: []any{ &litMatcher{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 237, col: 16, offset: 5088}, + pos: position{line: 240, col: 16, offset: 5111}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode217, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1159,23 +1198,23 @@ var g = &grammar{ }, }, &choiceExpr{ - pos: position{line: 67, col: 9, offset: 1585}, + pos: position{line: 70, col: 9, offset: 1608}, alternatives: []any{ &actionExpr{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, run: (*parser).callonNode220, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, val: "=", ignoreCase: false, want: "\"=\"", }, }, &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonNode222, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", @@ -1184,79 +1223,79 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 69, col: 7, offset: 1637}, + pos: position{line: 72, col: 7, offset: 1660}, expr: &litMatcher{ - pos: position{line: 69, col: 7, offset: 1637}, + pos: position{line: 72, col: 7, offset: 1660}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, }, &labeledExpr{ - pos: position{line: 69, col: 12, offset: 1642}, + pos: position{line: 72, col: 12, offset: 1665}, label: "v", expr: &choiceExpr{ - pos: position{line: 214, col: 5, offset: 4672}, + pos: position{line: 217, col: 5, offset: 4695}, alternatives: []any{ &litMatcher{ - pos: position{line: 214, col: 5, offset: 4672}, + pos: position{line: 217, col: 5, offset: 4695}, val: "today", ignoreCase: false, want: "\"today\"", }, &litMatcher{ - pos: position{line: 215, col: 5, offset: 4686}, + pos: position{line: 218, col: 5, offset: 4709}, val: "yesterday", ignoreCase: false, want: "\"yesterday\"", }, &litMatcher{ - pos: position{line: 216, col: 5, offset: 4704}, + pos: position{line: 219, col: 5, offset: 4727}, val: "this week", ignoreCase: false, want: "\"this week\"", }, &litMatcher{ - pos: position{line: 217, col: 5, offset: 4722}, + pos: position{line: 220, col: 5, offset: 4745}, val: "last week", ignoreCase: false, want: "\"last week\"", }, &litMatcher{ - pos: position{line: 218, col: 5, offset: 4740}, + pos: position{line: 221, col: 5, offset: 4763}, val: "last 7 days", ignoreCase: false, want: "\"last 7 days\"", }, &litMatcher{ - pos: position{line: 219, col: 5, offset: 4760}, + pos: position{line: 222, col: 5, offset: 4783}, val: "this month", ignoreCase: false, want: "\"this month\"", }, &litMatcher{ - pos: position{line: 220, col: 5, offset: 4779}, + pos: position{line: 223, col: 5, offset: 4802}, val: "last month", ignoreCase: false, want: "\"last month\"", }, &litMatcher{ - pos: position{line: 221, col: 5, offset: 4798}, + pos: position{line: 224, col: 5, offset: 4821}, val: "last 30 days", ignoreCase: false, want: "\"last 30 days\"", }, &litMatcher{ - pos: position{line: 222, col: 5, offset: 4819}, + pos: position{line: 225, col: 5, offset: 4842}, val: "this year", ignoreCase: false, want: "\"this year\"", }, &actionExpr{ - pos: position{line: 223, col: 5, offset: 4837}, + pos: position{line: 226, col: 5, offset: 4860}, run: (*parser).callonNode237, expr: &litMatcher{ - pos: position{line: 223, col: 5, offset: 4837}, + pos: position{line: 226, col: 5, offset: 4860}, val: "last year", ignoreCase: false, want: "\"last year\"", @@ -1266,9 +1305,9 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 69, col: 38, offset: 1668}, + pos: position{line: 72, col: 38, offset: 1691}, expr: &litMatcher{ - pos: position{line: 69, col: 38, offset: 1668}, + pos: position{line: 72, col: 38, offset: 1691}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1278,27 +1317,27 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 74, col: 5, offset: 1781}, + pos: position{line: 77, col: 5, offset: 1804}, run: (*parser).callonNode241, expr: &seqExpr{ - pos: position{line: 74, col: 5, offset: 1781}, + pos: position{line: 77, col: 5, offset: 1804}, exprs: []any{ &labeledExpr{ - pos: position{line: 74, col: 5, offset: 1781}, + pos: position{line: 77, col: 5, offset: 1804}, label: "k", expr: &actionExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, run: (*parser).callonNode244, expr: &seqExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode247, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1307,23 +1346,23 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 237, col: 11, offset: 5083}, + pos: position{line: 240, col: 11, offset: 5106}, expr: &seqExpr{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, exprs: []any{ &litMatcher{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 237, col: 16, offset: 5088}, + pos: position{line: 240, col: 16, offset: 5111}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode253, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1339,46 +1378,46 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 74, col: 11, offset: 1787}, + pos: position{line: 77, col: 11, offset: 1810}, label: "o", expr: &choiceExpr{ - pos: position{line: 75, col: 9, offset: 1799}, + pos: position{line: 78, col: 9, offset: 1822}, alternatives: []any{ &actionExpr{ - pos: position{line: 159, col: 5, offset: 3745}, + pos: position{line: 162, col: 5, offset: 3768}, run: (*parser).callonNode257, expr: &litMatcher{ - pos: position{line: 159, col: 5, offset: 3745}, + pos: position{line: 162, col: 5, offset: 3768}, val: ">=", ignoreCase: false, want: "\">=\"", }, }, &actionExpr{ - pos: position{line: 149, col: 5, offset: 3561}, + pos: position{line: 152, col: 5, offset: 3584}, run: (*parser).callonNode259, expr: &litMatcher{ - pos: position{line: 149, col: 5, offset: 3561}, + pos: position{line: 152, col: 5, offset: 3584}, val: "<=", ignoreCase: false, want: "\"<=\"", }, }, &actionExpr{ - pos: position{line: 154, col: 5, offset: 3650}, + pos: position{line: 157, col: 5, offset: 3673}, run: (*parser).callonNode261, expr: &litMatcher{ - pos: position{line: 154, col: 5, offset: 3650}, + pos: position{line: 157, col: 5, offset: 3673}, val: ">", ignoreCase: false, want: "\">\"", }, }, &actionExpr{ - pos: position{line: 144, col: 5, offset: 3469}, + pos: position{line: 147, col: 5, offset: 3492}, run: (*parser).callonNode263, expr: &litMatcher{ - pos: position{line: 144, col: 5, offset: 3469}, + pos: position{line: 147, col: 5, offset: 3492}, val: "<", ignoreCase: false, want: "\"<\"", @@ -1388,27 +1427,27 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 79, col: 7, offset: 1923}, + pos: position{line: 82, col: 7, offset: 1946}, expr: &litMatcher{ - pos: position{line: 79, col: 7, offset: 1923}, + pos: position{line: 82, col: 7, offset: 1946}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, }, &labeledExpr{ - pos: position{line: 79, col: 12, offset: 1928}, + pos: position{line: 82, col: 12, offset: 1951}, label: "v", expr: &actionExpr{ - pos: position{line: 247, col: 5, offset: 5207}, + pos: position{line: 250, col: 5, offset: 5230}, run: (*parser).callonNode268, expr: &seqExpr{ - pos: position{line: 247, col: 5, offset: 5207}, + pos: position{line: 250, col: 5, offset: 5230}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 247, col: 5, offset: 5207}, + pos: position{line: 250, col: 5, offset: 5230}, expr: &charClassMatcher{ - pos: position{line: 247, col: 5, offset: 5207}, + pos: position{line: 250, col: 5, offset: 5230}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1416,20 +1455,20 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 247, col: 12, offset: 5214}, + pos: position{line: 250, col: 12, offset: 5237}, expr: &seqExpr{ - pos: position{line: 247, col: 13, offset: 5215}, + pos: position{line: 250, col: 13, offset: 5238}, exprs: []any{ &litMatcher{ - pos: position{line: 247, col: 13, offset: 5215}, + pos: position{line: 250, col: 13, offset: 5238}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 247, col: 17, offset: 5219}, + pos: position{line: 250, col: 17, offset: 5242}, expr: &charClassMatcher{ - pos: position{line: 247, col: 17, offset: 5219}, + pos: position{line: 250, col: 17, offset: 5242}, val: "[0-9]", ranges: []rune{'0', '9'}, ignoreCase: false, @@ -1444,9 +1483,9 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 79, col: 21, offset: 1937}, + pos: position{line: 82, col: 21, offset: 1960}, expr: &litMatcher{ - pos: position{line: 79, col: 21, offset: 1937}, + pos: position{line: 82, col: 21, offset: 1960}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1456,27 +1495,27 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 84, col: 5, offset: 2041}, + pos: position{line: 87, col: 5, offset: 2064}, run: (*parser).callonNode279, expr: &seqExpr{ - pos: position{line: 84, col: 5, offset: 2041}, + pos: position{line: 87, col: 5, offset: 2064}, exprs: []any{ &labeledExpr{ - pos: position{line: 84, col: 5, offset: 2041}, + pos: position{line: 87, col: 5, offset: 2064}, label: "k", expr: &actionExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, run: (*parser).callonNode282, expr: &seqExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode285, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1485,23 +1524,23 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 237, col: 11, offset: 5083}, + pos: position{line: 240, col: 11, offset: 5106}, expr: &seqExpr{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, exprs: []any{ &litMatcher{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 237, col: 16, offset: 5088}, + pos: position{line: 240, col: 16, offset: 5111}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode291, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1517,40 +1556,40 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, run: (*parser).callonNode293, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, val: "=", ignoreCase: false, want: "\"=\"", }, }, &labeledExpr{ - pos: position{line: 84, col: 29, offset: 2065}, + pos: position{line: 87, col: 29, offset: 2088}, label: "v", expr: &choiceExpr{ - pos: position{line: 84, col: 32, offset: 2068}, + pos: position{line: 87, col: 32, offset: 2091}, alternatives: []any{ &actionExpr{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, run: (*parser).callonNode297, expr: &seqExpr{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, exprs: []any{ &litMatcher{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 242, col: 9, offset: 5150}, + pos: position{line: 245, col: 9, offset: 5173}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 242, col: 11, offset: 5152}, + pos: position{line: 245, col: 11, offset: 5175}, expr: &charClassMatcher{ - pos: position{line: 242, col: 11, offset: 5152}, + pos: position{line: 245, col: 11, offset: 5175}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1559,7 +1598,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 242, col: 17, offset: 5158}, + pos: position{line: 245, col: 17, offset: 5181}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1568,9 +1607,9 @@ var g = &grammar{ }, }, &oneOrMoreExpr{ - pos: position{line: 84, col: 41, offset: 2077}, + pos: position{line: 87, col: 41, offset: 2100}, expr: &charClassMatcher{ - pos: position{line: 84, col: 41, offset: 2077}, + pos: position{line: 87, col: 41, offset: 2100}, val: "[^ ()]", chars: []rune{' ', '(', ')'}, ignoreCase: false, @@ -1584,27 +1623,27 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 87, col: 5, offset: 2158}, + pos: position{line: 90, col: 5, offset: 2181}, run: (*parser).callonNode306, expr: &seqExpr{ - pos: position{line: 87, col: 5, offset: 2158}, + pos: position{line: 90, col: 5, offset: 2181}, exprs: []any{ &labeledExpr{ - pos: position{line: 87, col: 5, offset: 2158}, + pos: position{line: 90, col: 5, offset: 2181}, label: "k", expr: &actionExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, run: (*parser).callonNode309, expr: &seqExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode312, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1613,23 +1652,23 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 237, col: 11, offset: 5083}, + pos: position{line: 240, col: 11, offset: 5106}, expr: &seqExpr{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, exprs: []any{ &litMatcher{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 237, col: 16, offset: 5088}, + pos: position{line: 240, col: 16, offset: 5111}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonNode318, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1645,40 +1684,40 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonNode320, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", }, }, &labeledExpr{ - pos: position{line: 87, col: 29, offset: 2182}, + pos: position{line: 90, col: 29, offset: 2205}, label: "v", expr: &choiceExpr{ - pos: position{line: 87, col: 32, offset: 2185}, + pos: position{line: 90, col: 32, offset: 2208}, alternatives: []any{ &actionExpr{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, run: (*parser).callonNode324, expr: &seqExpr{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, exprs: []any{ &litMatcher{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 242, col: 9, offset: 5150}, + pos: position{line: 245, col: 9, offset: 5173}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 242, col: 11, offset: 5152}, + pos: position{line: 245, col: 11, offset: 5175}, expr: &charClassMatcher{ - pos: position{line: 242, col: 11, offset: 5152}, + pos: position{line: 245, col: 11, offset: 5175}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1687,7 +1726,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 242, col: 17, offset: 5158}, + pos: position{line: 245, col: 17, offset: 5181}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1696,9 +1735,9 @@ var g = &grammar{ }, }, &oneOrMoreExpr{ - pos: position{line: 87, col: 41, offset: 2194}, + pos: position{line: 90, col: 41, offset: 2217}, expr: &charClassMatcher{ - pos: position{line: 87, col: 41, offset: 2194}, + pos: position{line: 90, col: 41, offset: 2217}, val: "[^ ()]", chars: []rune{' ', '(', ')'}, ignoreCase: false, @@ -1712,19 +1751,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 119, col: 5, offset: 3008}, + pos: position{line: 122, col: 5, offset: 3031}, run: (*parser).callonNode333, expr: &choiceExpr{ - pos: position{line: 119, col: 6, offset: 3009}, + pos: position{line: 122, col: 6, offset: 3032}, alternatives: []any{ &litMatcher{ - pos: position{line: 119, col: 6, offset: 3009}, + pos: position{line: 122, col: 6, offset: 3032}, val: "AND", ignoreCase: false, want: "\"AND\"", }, &litMatcher{ - pos: position{line: 119, col: 14, offset: 3017}, + pos: position{line: 122, col: 14, offset: 3040}, val: "+", ignoreCase: false, want: "\"+\"", @@ -1733,19 +1772,19 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 124, col: 5, offset: 3109}, + pos: position{line: 127, col: 5, offset: 3132}, run: (*parser).callonNode337, expr: &choiceExpr{ - pos: position{line: 124, col: 6, offset: 3110}, + pos: position{line: 127, col: 6, offset: 3133}, alternatives: []any{ &litMatcher{ - pos: position{line: 124, col: 6, offset: 3110}, + pos: position{line: 127, col: 6, offset: 3133}, val: "NOT", ignoreCase: false, want: "\"NOT\"", }, &litMatcher{ - pos: position{line: 124, col: 14, offset: 3118}, + pos: position{line: 127, col: 14, offset: 3141}, val: "-", ignoreCase: false, want: "\"-\"", @@ -1754,28 +1793,28 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 129, col: 5, offset: 3209}, + pos: position{line: 132, col: 5, offset: 3232}, run: (*parser).callonNode341, expr: &litMatcher{ - pos: position{line: 129, col: 6, offset: 3210}, + pos: position{line: 132, col: 6, offset: 3233}, val: "OR", ignoreCase: false, want: "\"OR\"", }, }, &actionExpr{ - pos: position{line: 100, col: 6, offset: 2482}, + pos: position{line: 103, col: 6, offset: 2505}, run: (*parser).callonNode343, expr: &seqExpr{ - pos: position{line: 100, col: 6, offset: 2482}, + pos: position{line: 103, col: 6, offset: 2505}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 100, col: 6, offset: 2482}, + pos: position{line: 103, col: 6, offset: 2505}, expr: &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonNode346, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", @@ -1783,12 +1822,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, run: (*parser).callonNode348, expr: &zeroOrMoreExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, expr: &charClassMatcher{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1797,27 +1836,27 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 100, col: 27, offset: 2503}, + pos: position{line: 103, col: 27, offset: 2526}, label: "v", expr: &actionExpr{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, run: (*parser).callonNode352, expr: &seqExpr{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, exprs: []any{ &litMatcher{ - pos: position{line: 242, col: 5, offset: 5146}, + pos: position{line: 245, col: 5, offset: 5169}, val: "\"", ignoreCase: false, want: "\"\\\"\"", }, &labeledExpr{ - pos: position{line: 242, col: 9, offset: 5150}, + pos: position{line: 245, col: 9, offset: 5173}, label: "v", expr: &zeroOrMoreExpr{ - pos: position{line: 242, col: 11, offset: 5152}, + pos: position{line: 245, col: 11, offset: 5175}, expr: &charClassMatcher{ - pos: position{line: 242, col: 11, offset: 5152}, + pos: position{line: 245, col: 11, offset: 5175}, val: "[^\"]", chars: []rune{'"'}, ignoreCase: false, @@ -1826,7 +1865,7 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 242, col: 17, offset: 5158}, + pos: position{line: 245, col: 17, offset: 5181}, val: "\"", ignoreCase: false, want: "\"\\\"\"", @@ -1836,12 +1875,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, run: (*parser).callonNode359, expr: &zeroOrMoreExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, expr: &charClassMatcher{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1850,12 +1889,12 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 100, col: 38, offset: 2514}, + pos: position{line: 103, col: 38, offset: 2537}, expr: &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonNode363, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", @@ -1866,18 +1905,18 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 105, col: 6, offset: 2619}, + pos: position{line: 108, col: 6, offset: 2642}, run: (*parser).callonNode365, expr: &seqExpr{ - pos: position{line: 105, col: 6, offset: 2619}, + pos: position{line: 108, col: 6, offset: 2642}, exprs: []any{ &zeroOrOneExpr{ - pos: position{line: 105, col: 6, offset: 2619}, + pos: position{line: 108, col: 6, offset: 2642}, expr: &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonNode368, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", @@ -1885,12 +1924,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, run: (*parser).callonNode370, expr: &zeroOrMoreExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, expr: &charClassMatcher{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1899,12 +1938,12 @@ var g = &grammar{ }, }, &labeledExpr{ - pos: position{line: 105, col: 27, offset: 2640}, + pos: position{line: 108, col: 27, offset: 2663}, label: "v", expr: &oneOrMoreExpr{ - pos: position{line: 105, col: 29, offset: 2642}, + pos: position{line: 108, col: 29, offset: 2665}, expr: &charClassMatcher{ - pos: position{line: 105, col: 29, offset: 2642}, + pos: position{line: 108, col: 29, offset: 2665}, val: "[^ :()]", chars: []rune{' ', ':', '(', ')'}, ignoreCase: false, @@ -1913,12 +1952,12 @@ var g = &grammar{ }, }, &actionExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, run: (*parser).callonNode376, expr: &zeroOrMoreExpr{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, expr: &charClassMatcher{ - pos: position{line: 257, col: 5, offset: 5336}, + pos: position{line: 260, col: 5, offset: 5359}, val: "[ \\t]", chars: []rune{' ', '\t'}, ignoreCase: false, @@ -1927,12 +1966,12 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 105, col: 40, offset: 2653}, + pos: position{line: 108, col: 40, offset: 2676}, expr: &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonNode380, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", @@ -1947,31 +1986,31 @@ var g = &grammar{ }, { name: "GroupNode", - pos: position{line: 31, col: 1, offset: 595}, + pos: position{line: 34, col: 1, offset: 618}, expr: &actionExpr{ - pos: position{line: 32, col: 5, offset: 612}, + pos: position{line: 35, col: 5, offset: 635}, run: (*parser).callonGroupNode1, expr: &seqExpr{ - pos: position{line: 32, col: 5, offset: 612}, + pos: position{line: 35, col: 5, offset: 635}, exprs: []any{ &labeledExpr{ - pos: position{line: 32, col: 5, offset: 612}, + pos: position{line: 35, col: 5, offset: 635}, label: "k", expr: &zeroOrOneExpr{ - pos: position{line: 32, col: 7, offset: 614}, + pos: position{line: 35, col: 7, offset: 637}, expr: &actionExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, run: (*parser).callonGroupNode5, expr: &seqExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, exprs: []any{ &oneOrMoreExpr{ - pos: position{line: 237, col: 5, offset: 5077}, + pos: position{line: 240, col: 5, offset: 5100}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonGroupNode8, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -1980,23 +2019,23 @@ var g = &grammar{ }, }, &zeroOrMoreExpr{ - pos: position{line: 237, col: 11, offset: 5083}, + pos: position{line: 240, col: 11, offset: 5106}, expr: &seqExpr{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, exprs: []any{ &litMatcher{ - pos: position{line: 237, col: 12, offset: 5084}, + pos: position{line: 240, col: 12, offset: 5107}, val: ".", ignoreCase: false, want: "\".\"", }, &oneOrMoreExpr{ - pos: position{line: 237, col: 16, offset: 5088}, + pos: position{line: 240, col: 16, offset: 5111}, expr: &actionExpr{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, run: (*parser).callonGroupNode14, expr: &charClassMatcher{ - pos: position{line: 232, col: 5, offset: 5021}, + pos: position{line: 235, col: 5, offset: 5044}, val: "[A-Za-z]", ranges: []rune{'A', 'Z', 'a', 'z'}, ignoreCase: false, @@ -2013,25 +2052,25 @@ var g = &grammar{ }, }, &zeroOrOneExpr{ - pos: position{line: 32, col: 12, offset: 619}, + pos: position{line: 35, col: 12, offset: 642}, expr: &choiceExpr{ - pos: position{line: 32, col: 13, offset: 620}, + pos: position{line: 35, col: 13, offset: 643}, alternatives: []any{ &actionExpr{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, run: (*parser).callonGroupNode18, expr: &litMatcher{ - pos: position{line: 134, col: 5, offset: 3298}, + pos: position{line: 137, col: 5, offset: 3321}, val: ":", ignoreCase: false, want: "\":\"", }, }, &actionExpr{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, run: (*parser).callonGroupNode20, expr: &litMatcher{ - pos: position{line: 139, col: 5, offset: 3384}, + pos: position{line: 142, col: 5, offset: 3407}, val: "=", ignoreCase: false, want: "\"=\"", @@ -2041,21 +2080,21 @@ var g = &grammar{ }, }, &litMatcher{ - pos: position{line: 32, col: 53, offset: 660}, + pos: position{line: 35, col: 53, offset: 683}, val: "(", ignoreCase: false, want: "\"(\"", }, &labeledExpr{ - pos: position{line: 32, col: 57, offset: 664}, + pos: position{line: 35, col: 57, offset: 687}, label: "v", expr: &ruleRefExpr{ - pos: position{line: 32, col: 59, offset: 666}, + pos: position{line: 35, col: 59, offset: 689}, name: "Nodes", }, }, &litMatcher{ - pos: position{line: 32, col: 65, offset: 672}, + pos: position{line: 35, col: 65, offset: 695}, val: ")", ignoreCase: false, want: "\")\"", @@ -2067,6 +2106,28 @@ var g = &grammar{ }, } +func (c *current) onAST3() (any, error) { + return nil, nil + +} + +func (p *parser) callonAST3() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onAST3() +} + +func (c *current) onAST8(n any) (any, error) { + return nil, nil + +} + +func (p *parser) callonAST8() (any, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onAST8(stack["n"]) +} + func (c *current) onAST1(n any) (any, error) { return buildAST(n, c.text, c.pos) diff --git a/pkg/kql/dictionary_test.go b/pkg/kql/dictionary_test.go index a49d16dc18..36b5f20566 100644 --- a/pkg/kql/dictionary_test.go +++ b/pkg/kql/dictionary_test.go @@ -33,13 +33,13 @@ func TestParse_Spec(t *testing.T) { }, { name: `AND`, - error: kql.StartsWithBinaryOperatorError{ + error: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, { name: `AND cat AND dog`, - error: kql.StartsWithBinaryOperatorError{ + error: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, @@ -79,13 +79,13 @@ func TestParse_Spec(t *testing.T) { }, { name: `OR`, - error: kql.StartsWithBinaryOperatorError{ + error: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, { name: `OR cat AND dog`, - error: kql.StartsWithBinaryOperatorError{ + error: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, @@ -941,28 +941,58 @@ func TestParse_Errors(t *testing.T) { }, { query: "animal:(AND cat)", - error: kql.StartsWithBinaryOperatorError{ + error: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, { query: "animal:(OR cat)", - error: kql.StartsWithBinaryOperatorError{ + error: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, { query: "(AND cat)", - error: kql.StartsWithBinaryOperatorError{ + error: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, { query: "(OR cat)", - error: kql.StartsWithBinaryOperatorError{ + error: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolOR}, }, }, + { + query: `cat AND`, + error: kql.DanglingOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: `cat NOT`, + error: kql.DanglingOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolNOT}, + }, + }, + { + query: `cat AND OR dog`, + error: kql.DanglingOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolOR}, + }, + }, + { + query: `cat NOT AND dog`, + error: kql.DanglingOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolAND}, + }, + }, + { + query: `cat AND (dog NOT)`, + error: kql.DanglingOperatorError{ + Node: &ast.OperatorNode{Value: kql.BoolNOT}, + }, + }, { query: `cat dog`, ast: &ast.Ast{ diff --git a/pkg/kql/error.go b/pkg/kql/error.go index 47009001b3..f5940f1810 100644 --- a/pkg/kql/error.go +++ b/pkg/kql/error.go @@ -7,13 +7,26 @@ import ( "github.com/opencloud-eu/opencloud/pkg/ast" ) -// StartsWithBinaryOperatorError records an error and the operation that caused it. -type StartsWithBinaryOperatorError struct { +// DanglingOperatorError records an error and the operator that caused it. +type DanglingOperatorError struct { Node *ast.OperatorNode } -func (e StartsWithBinaryOperatorError) Error() string { - return "the expression can't begin from a binary operator: '" + e.Node.Value + "'" +func (e DanglingOperatorError) Error() string { + return "the operator '" + e.Node.Value + "' is missing an operand" +} + +// SyntaxError records an error and the query that caused it. +type SyntaxError struct { + Inner error +} + +func (e SyntaxError) Error() string { + return "invalid query syntax: " + e.Inner.Error() +} + +func (e SyntaxError) Unwrap() error { + return e.Inner } // NamedGroupInvalidNodesError records an error and the operation that caused it. @@ -39,17 +52,31 @@ func (e UnsupportedTimeRangeError) Error() string { return fmt.Sprintf("unable to convert '%v' to a time range", e.Value) } +func isGrammarActionError(err error) bool { + var ( + danglingOperator *DanglingOperatorError + namedGroupInvalidNodes *NamedGroupInvalidNodesError + unsupportedTimeRange *UnsupportedTimeRangeError + ) + + return errors.As(err, &danglingOperator) || + errors.As(err, &namedGroupInvalidNodes) || + errors.As(err, &unsupportedTimeRange) +} + // IsValidationError reports whether err is one of the KQL parse/validation // errors produced by this package, i.e. the query itself is at fault and the // caller should treat it as a bad request. func IsValidationError(err error) bool { var ( - startsWithBinaryOperator *StartsWithBinaryOperatorError - namedGroupInvalidNodes *NamedGroupInvalidNodesError - unsupportedTimeRange *UnsupportedTimeRangeError + danglingOperator *DanglingOperatorError + syntax *SyntaxError + namedGroupInvalidNodes *NamedGroupInvalidNodesError + unsupportedTimeRange *UnsupportedTimeRangeError ) - return errors.As(err, &startsWithBinaryOperator) || + return errors.As(err, &danglingOperator) || + errors.As(err, &syntax) || errors.As(err, &namedGroupInvalidNodes) || errors.As(err, &unsupportedTimeRange) } diff --git a/pkg/kql/kql.go b/pkg/kql/kql.go index a4132f4650..31dc9ab69a 100644 --- a/pkg/kql/kql.go +++ b/pkg/kql/kql.go @@ -26,19 +26,16 @@ func (b Builder) Build(q string) (*ast.Ast, error) { f, err := Parse("", []byte(q)) if err != nil { var list errList - errors.As(err, &list) - - for _, listError := range list { - var parserError *parserError - switch { - case errors.As(listError, &parserError): - if parserError.Inner != nil { + if errors.As(err, &list) { + for _, listError := range list { + var parserError *parserError + if errors.As(listError, &parserError) && isGrammarActionError(parserError.Inner) { return nil, parserError.Inner } - - return nil, listError } } + + return nil, &SyntaxError{Inner: err} } return f.(*ast.Ast), nil diff --git a/pkg/kql/kql_test.go b/pkg/kql/kql_test.go index d90245ccf3..aed6a549a6 100644 --- a/pkg/kql/kql_test.go +++ b/pkg/kql/kql_test.go @@ -21,7 +21,7 @@ func TestNewAST(t *testing.T) { { name: "error", givenQuery: kql.BoolAND, - expectedError: kql.StartsWithBinaryOperatorError{ + expectedError: kql.DanglingOperatorError{ Node: &ast.OperatorNode{Value: kql.BoolAND}, }, }, @@ -50,3 +50,45 @@ func TestNewAST(t *testing.T) { }) } } + +func TestBuild_ValidationErrors(t *testing.T) { + assert := tAssert.New(t) + + for _, q := range []string{ + "NOT", + "cat NOT", + "cat AND", + "AND cat", + "cat AND OR dog", + "cat NOT AND dog", + "(cat", + "cat)", + "()", + "cat) OR (dog", + } { + got, err := kql.Builder{}.Build(q) + assert.Nil(got, q) + assert.Error(err, q) + assert.True(kql.IsValidationError(err), "%q: %v", q, err) + } +} + +func TestBuild_ValidQueries(t *testing.T) { + assert := tAssert.New(t) + + for _, q := range []string{ + "cat", + "cat dog", + "NOT cat", + "NOT NOT cat", + "cat NOT dog", + "cat AND NOT dog", + "(cat OR dog)", + `name:"cat dog"`, + "cat AND (dog OR fish)", + } { + got, err := kql.Builder{}.Build(q) + assert.NoError(err, q) + assert.NotNil(got, q) + } +} diff --git a/pkg/kql/validate.go b/pkg/kql/validate.go index 1f7e38ccba..13d0388b24 100644 --- a/pkg/kql/validate.go +++ b/pkg/kql/validate.go @@ -5,23 +5,12 @@ import ( ) func validateAst(a *ast.Ast) error { - switch node := a.Nodes[0].(type) { - case *ast.OperatorNode: - switch node.Value { - case BoolAND, BoolOR: - return &StartsWithBinaryOperatorError{Node: node} - } - } - return nil + return validateOperators(a.Nodes) } func validateGroupNode(n *ast.GroupNode) error { - switch node := n.Nodes[0].(type) { - case *ast.OperatorNode: - switch node.Value { - case BoolAND, BoolOR: - return &StartsWithBinaryOperatorError{Node: node} - } + if err := validateOperators(n.Nodes); err != nil { + return err } if n.Key != "" { @@ -34,3 +23,28 @@ func validateGroupNode(n *ast.GroupNode) error { return nil } + +func validateOperators(nodes []ast.Node) error { + for i, node := range nodes { + op, ok := node.(*ast.OperatorNode) + if !ok { + continue + } + + switch op.Value { + case BoolAND, BoolOR: + if i == 0 { + return &DanglingOperatorError{Node: op} + } + if _, prevIsOperator := nodes[i-1].(*ast.OperatorNode); prevIsOperator { + return &DanglingOperatorError{Node: op} + } + } + + if i == len(nodes)-1 { + return &DanglingOperatorError{Node: op} + } + } + + return nil +} diff --git a/services/search/README.md b/services/search/README.md index d0f650e528..2c48aca119 100644 --- a/services/search/README.md +++ b/services/search/README.md @@ -80,6 +80,25 @@ Not all parts are supported, the following list gives an overview of parts that In [this ADR](https://github.com/owncloud/ocis/blob/docs/ocis/adr/0020-file-search-query-language.md) you can read why KQL was chosen. +## Query filter + +`SEARCH_QUERY_FILTER` is a KQL expression ANDed to every search. It applies on every engine, works on existing +indexes without reindexing, and keeps paging and totals right. Default: + +``` +NOT path:"./.space" AND NOT id:{{.SPACE_ROOT_PERSONAL}} +``` + +This leaves out `./.space` (the space image and readme) and the personal space root (named after its owner). Two +placeholders become the searched space's root id, or an id that matches nothing in other space types: + +* `{{.SPACE_ROOT_PERSONAL}}` for personal spaces. +* `{{.SPACE_ROOT_PROJECT}}` for project spaces. Not in the default, so a project space stays findable by name; add + `AND NOT id:{{.SPACE_ROOT_PROJECT}}` to drop it too. + +Quote the value in a shell or compose env, the braces are a template token. An empty value turns the filter off. The +service validates the filter at startup and does not start if it cannot parse it. + ## Content analysis / Extraction The search service supports the following content extraction methods: diff --git a/services/search/pkg/config/config.go b/services/search/pkg/config/config.go index cd4bce692a..d956ef2184 100644 --- a/services/search/pkg/config/config.go +++ b/services/search/pkg/config/config.go @@ -3,8 +3,9 @@ package config import ( "context" - "github.com/opencloud-eu/opencloud/pkg/shared" "go-micro.dev/v4/client" + + "github.com/opencloud-eu/opencloud/pkg/shared" ) // Config combines all available configuration parts. @@ -29,6 +30,7 @@ type Config struct { ContentExtractionSizeLimit uint64 `yaml:"content_extraction_size_limit" env:"SEARCH_CONTENT_EXTRACTION_SIZE_LIMIT" desc:"Maximum file size in bytes that is allowed for content extraction." introductionVersion:"1.0.0"` BatchSize int `yaml:"batch_size" env:"SEARCH_BATCH_SIZE" desc:"The number of documents to process in a single batch. Defaults to 500." introductionVersion:"1.0.0"` ReindexMaxConcurrency int `yaml:"reindex_concurrency" env:"SEARCH_REINDEX_MAX_CONCURRENCY" desc:"The maximum number of spaces that are reindexed concurrently when reindexing all spaces." introductionVersion:"7.4.0"` + QueryFilter string `yaml:"query_filter" env:"SEARCH_QUERY_FILTER" desc:"A KQL expression added to every search query with AND, e.g. to leave results out." introductionVersion:"%%NEXT%%"` ServiceAccount ServiceAccount `yaml:"service_account"` diff --git a/services/search/pkg/config/defaults/defaultconfig.go b/services/search/pkg/config/defaults/defaultconfig.go index 7145dfdc70..695175e560 100644 --- a/services/search/pkg/config/defaults/defaultconfig.go +++ b/services/search/pkg/config/defaults/defaultconfig.go @@ -66,6 +66,7 @@ func DefaultConfig() *config.Config { ContentExtractionSizeLimit: 20 * 1024 * 1024, // Limit content extraction to <20MB files by default BatchSize: 50, ReindexMaxConcurrency: 3, + QueryFilter: `NOT path:"./.space" AND NOT id:` + config.PersonalSpaceRootPlaceholder, } } diff --git a/services/search/pkg/config/parser/parse.go b/services/search/pkg/config/parser/parse.go index 514fceb59f..a3d95be153 100644 --- a/services/search/pkg/config/parser/parse.go +++ b/services/search/pkg/config/parser/parse.go @@ -2,8 +2,11 @@ package parser import ( "errors" + "fmt" + "strings" occfg "github.com/opencloud-eu/opencloud/pkg/config" + "github.com/opencloud-eu/opencloud/pkg/kql" "github.com/opencloud-eu/opencloud/pkg/shared" "github.com/opencloud-eu/opencloud/services/search/pkg/config" "github.com/opencloud-eu/opencloud/services/search/pkg/config/defaults" @@ -49,5 +52,26 @@ func Validate(cfg *config.Config) error { return shared.MissingServiceAccountSecret(cfg.Service.Name) } + return validateQueryFilter(cfg.QueryFilter) +} + +func validateQueryFilter(query string) error { + if query == "" { + return nil + } + + expanded := strings.NewReplacer( + config.PersonalSpaceRootPlaceholder, `"`+config.NoResourceID+`"`, + config.ProjectSpaceRootPlaceholder, `"`+config.NoResourceID+`"`, + ).Replace(query) + + if strings.Contains(expanded, "{{") || strings.Contains(expanded, "}}") { + return errors.New("search query filter: unknown placeholder") + } + + if _, err := (kql.Builder{}).Build(expanded); err != nil { + return fmt.Errorf("search query filter: %w", err) + } + return nil } diff --git a/services/search/pkg/config/parser/parse_test.go b/services/search/pkg/config/parser/parse_test.go new file mode 100644 index 0000000000..1454f4d91a --- /dev/null +++ b/services/search/pkg/config/parser/parse_test.go @@ -0,0 +1,23 @@ +package parser + +import "testing" + +func TestValidateQueryFilter(t *testing.T) { + for query, valid := range map[string]bool{ + "": true, + `NOT path:"./.space" AND NOT id:{{.SPACE_ROOT_PERSONAL}}`: true, + `NOT id:{{.SPACE_ROOT_PERSONAL}} AND NOT id:{{.SPACE_ROOT_PROJECT}}`: true, + `NOT mediatype:folder`: true, + `NOT name:"a(b"`: true, + `NOT id:{{.SPACE_ROOT_GRANT}}`: false, + `NOT id:{{.SPACE_ROOT_PERSONAL}`: false, + `(NOT path:x`: false, + `NOT path:x) OR (name:y`: false, + `AND AND`: false, + `NOT`: false, + } { + if err := validateQueryFilter(query); (err == nil) != valid { + t.Errorf("validateQueryFilter(%q) = %v, want valid %v", query, err, valid) + } + } +} diff --git a/services/search/pkg/config/search.go b/services/search/pkg/config/search.go index e626893212..a62f5662f8 100644 --- a/services/search/pkg/config/search.go +++ b/services/search/pkg/config/search.go @@ -2,6 +2,12 @@ package config import "time" +const ( + PersonalSpaceRootPlaceholder = "{{.SPACE_ROOT_PERSONAL}}" + ProjectSpaceRootPlaceholder = "{{.SPACE_ROOT_PROJECT}}" + NoResourceID = "none$none!none" +) + // Events combines the configuration options for the event bus. type Events struct { Disabled bool `yaml:"disabled" env:"SEARCH_EVENTS_DISABLED" desc:"Disables listening for events. Set this to true if the service should only handle GRPC requests." introductionVersion:"4.0.0"` diff --git a/services/search/pkg/parity/README.md b/services/search/pkg/parity/README.md index 186a48badb..4000676840 100644 --- a/services/search/pkg/parity/README.md +++ b/services/search/pkg/parity/README.md @@ -321,6 +321,23 @@ Fixtures: | VISIBILITY-06 | `hidden:banana` | no match | no match | no match | ✅ | | VISIBILITY-07 | `hidden:"true"` | .private, dotfile.txt, secret.txt | .private, dotfile.txt, secret.txt | .private, dotfile.txt, secret.txt | ✅ | +### filter + +Fixtures: + +- `alice`, ID = 1$1!1, folder, Path = . +- `.space`, ID = 1$1!.space, folder +- `readme.md`, ID = 1$1!readme.md, Path = ./.space/readme.md +- `.spaces`, ID = 1$1!.spaces, folder +- `notes.md`, ID = 1$1!notes.md + +| Case | Query | expected | bleve | OpenSearch | same? | +|---|---|---|---|---|---| +| FILTER-01 | `path:"./.space"` | .space, readme.md | .space, readme.md | .space, readme.md | ✅ | +| FILTER-02 | `(name:"*.md") AND (NOT path:"./....NOT id:"none$none!none")` | notes.md | notes.md | notes.md | ✅ | +| FILTER-03 | `(name:"alice" OR name:".spaces")...ace" AND NOT id:"1$1!1")` | .spaces | .spaces | .spaces | ✅ | +| FILTER-04 | `(name:"alice") AND (NOT path:"./...NOT id:"none$none!none")` | alice | alice | alice | ✅ | + ### boolean Fixtures: @@ -448,7 +465,11 @@ Fixtures: | Case | Query | expected | bleve | OpenSearch | same? | |---|---|---|---|---|---| | INVALID-01 | `AND mediatype:document` | bad request | bad request | bad request | ✅ | -| INVALID-02 | `mediatype:document AND` | alpha.txt | alpha.txt | alpha.txt | ✅ | +| INVALID-02 | `mediatype:document AND` | bad request | bad request | bad request | ✅ | +| INVALID-03 | `mediatype:document NOT` | bad request | bad request | bad request | ✅ | +| INVALID-04 | `mediatype:document AND OR name:alpha` | bad request | bad request | bad request | ✅ | +| INVALID-05 | `(mediatype:document` | bad request | bad request | bad request | ✅ | +| INVALID-06 | `mediatype:document) OR (name:alpha` | bad request | bad request | bad request | ✅ | ## Operations diff --git a/services/search/pkg/parity/parity_test.go b/services/search/pkg/parity/parity_test.go index 41695d18b0..2c0255b6cb 100644 --- a/services/search/pkg/parity/parity_test.go +++ b/services/search/pkg/parity/parity_test.go @@ -104,6 +104,7 @@ func queryGroups() []queryGroup { fieldsGroup(), deletedGroup(), visibilityGroup(), + filterGroup(), booleanGroup(), samenameGroup(), stressGroup(), diff --git a/services/search/pkg/parity/query_filter_test.go b/services/search/pkg/parity/query_filter_test.go new file mode 100644 index 0000000000..13bfaf2ebe --- /dev/null +++ b/services/search/pkg/parity/query_filter_test.go @@ -0,0 +1,24 @@ +package parity + +import ( + "github.com/opencloud-eu/opencloud/services/search/pkg/search" +) + +func filterGroup() queryGroup { + return queryGroup{ + name: "filter", + fixtures: []search.Resource{ + fixtureFolder("alice", withID("1$1!1"), withPath(".")), + fixtureFolder(".space", withPath("./.space")), + fixtureDoc("readme.md", withPath("./.space/readme.md")), + fixtureFolder(".spaces", withPath("./.spaces")), + fixtureDoc("notes.md"), + }, + cases: []queryCase{ + {id: 1, query: `path:"./.space"`, want: []string{".space", "readme.md"}}, + {id: 2, query: `(name:"*.md") AND (NOT path:"./.space" AND NOT id:"none$none!none")`, want: []string{"notes.md"}}, + {id: 3, query: `(name:"alice" OR name:".spaces") AND (NOT path:"./.space" AND NOT id:"1$1!1")`, want: []string{".spaces"}}, + {id: 4, query: `(name:"alice") AND (NOT path:"./.space" AND NOT id:"none$none!none")`, want: []string{"alice"}}, + }, + } +} diff --git a/services/search/pkg/parity/query_invalid_test.go b/services/search/pkg/parity/query_invalid_test.go index 87da4f0592..66f6235089 100644 --- a/services/search/pkg/parity/query_invalid_test.go +++ b/services/search/pkg/parity/query_invalid_test.go @@ -12,7 +12,11 @@ func invalidGroup() queryGroup { }, cases: []queryCase{ {id: 1, query: `AND mediatype:document`, wantBadRequest: true}, - {id: 2, query: `mediatype:document AND`, want: []string{"alpha.txt"}}, + {id: 2, query: `mediatype:document AND`, wantBadRequest: true}, + {id: 3, query: `mediatype:document NOT`, wantBadRequest: true}, + {id: 4, query: `mediatype:document AND OR name:alpha`, wantBadRequest: true}, + {id: 5, query: `(mediatype:document`, wantBadRequest: true}, + {id: 6, query: `mediatype:document) OR (name:alpha`, wantBadRequest: true}, }, } } diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go index a45968197e..fe2e6fe933 100644 --- a/services/search/pkg/search/service.go +++ b/services/search/pkg/search/service.go @@ -30,6 +30,7 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/protobuf/types/known/fieldmaskpb" + "github.com/opencloud-eu/opencloud/pkg/kql" "github.com/opencloud-eu/opencloud/pkg/log" searchmsg "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/messages/search/v0" searchsvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/search/v0" @@ -75,6 +76,8 @@ type Service struct { serviceAccountSecret string batchSize int + + queryFilter string } var errSkipSpace error @@ -92,11 +95,37 @@ func NewService(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], eng E serviceAccountSecret: cfg.ServiceAccount.ServiceAccountSecret, batchSize: cfg.BatchSize, + + queryFilter: cfg.QueryFilter, } return s } +// withQueryFilter adds the query filter to the engine query, so paging and totals stay right +func (s *Service) withQueryFilter(query string, space *provider.StorageSpace) string { + if s.queryFilter == "" { + return query + } + + return "(" + query + ") AND (" + ExpandQueryFilter(s.queryFilter, space) + ")" +} + +// ExpandQueryFilter fills the query filter placeholders for the given space. +func ExpandQueryFilter(query string, space *provider.StorageSpace) string { + rootID := func(spaceType string) string { + if space.GetSpaceType() == spaceType { + return `"` + storagespace.FormatResourceID(space.GetRoot()) + `"` + } + return `"` + config.NoResourceID + `"` + } + + return strings.NewReplacer( + config.PersonalSpaceRootPlaceholder, rootID(_spaceTypePersonal), + config.ProjectSpaceRootPlaceholder, rootID(_spaceTypeProject), + ).Replace(query) +} + // Search processes a search request and passes it down to the engine. func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) { s.logger.Debug().Str("query", req.Query).Msg("performing a search") @@ -136,6 +165,12 @@ func (s *Service) Search(ctx context.Context, req *searchsvc.SearchRequest) (*se if query == "" { return nil, errtypes.BadRequest("empty query provided") } + if _, err := (kql.Builder{}).Build(query); err != nil { + if kql.IsValidationError(err) { + return nil, errtypes.BadRequest(err.Error()) + } + return nil, err + } req.Query = query if len(scope) > 0 { scopedID, err := storagespace.ParseID(scope) @@ -413,7 +448,7 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest, } searchRequest := &searchsvc.SearchIndexRequest{ - Query: req.Query, + Query: s.withQueryFilter(req.Query, space), Ref: &searchmsg.Reference{ ResourceId: searchRootID, Path: searchPathPrefix, diff --git a/services/search/pkg/search/service_test.go b/services/search/pkg/search/service_test.go index 5db825bc9e..9eb695e4f6 100644 --- a/services/search/pkg/search/service_test.go +++ b/services/search/pkg/search/service_test.go @@ -177,6 +177,19 @@ var _ = Describe("Searchprovider", func() { ) }) + DescribeTable("ExpandQueryFilter", + func(spaceType, expected string) { + space := &sprovider.StorageSpace{ + SpaceType: spaceType, + Root: &sprovider.ResourceId{StorageId: "storageid", SpaceId: "spaceid", OpaqueId: "spaceid"}, + } + Expect(search.ExpandQueryFilter(`NOT id:{{.SPACE_ROOT_PERSONAL}} AND NOT id:{{.SPACE_ROOT_PROJECT}}`, space)).To(Equal(expected)) + }, + Entry("a personal space fills the personal placeholder", "personal", `NOT id:"storageid$spaceid!spaceid" AND NOT id:"none$none!none"`), + Entry("a project space fills the project placeholder", "project", `NOT id:"none$none!none" AND NOT id:"storageid$spaceid!spaceid"`), + Entry("a share fills neither", "grant", `NOT id:"none$none!none" AND NOT id:"none$none!none"`), + ) + DescribeTable("IsHidden", func(path string, hidden bool) { Expect(search.IsHidden(path)).To(Equal(hidden)) @@ -244,6 +257,17 @@ var _ = Describe("Searchprovider", func() { })) }) + It("adds the query filter with the personal space root filled in", func() { + s := search.NewService(gatewaySelector, indexClient, extractor, nil, logger, &config.Config{ + QueryFilter: `NOT path:"./.space" AND NOT id:{{.SPACE_ROOT_PERSONAL}}`, + }) + _, err := s.Search(ctx, &searchsvc.SearchRequest{Query: "foo"}) + Expect(err).ToNot(HaveOccurred()) + indexClient.AssertCalled(GinkgoT(), "Search", mock.Anything, mock.MatchedBy(func(req *searchsvc.SearchIndexRequest) bool { + return req.Query == `(foo) AND (NOT path:"./.space" AND NOT id:"storageid$personalspace!personalspace")` + })) + }) + It("searches the personal user space", func() { res, err := s.Search(ctx, &searchsvc.SearchRequest{ Query: "foo", diff --git a/tests/acceptance/features/apiSearch1/search.feature b/tests/acceptance/features/apiSearch1/search.feature index b6196b04e2..3046fd9b4d 100644 --- a/tests/acceptance/features/apiSearch1/search.feature +++ b/tests/acceptance/features/apiSearch1/search.feature @@ -100,12 +100,12 @@ Feature: Search @issue-10329 Scenario Outline: user can search hidden files Given using DAV path - And user "Alice" has created a folder ".space" in space "project101" - When user "Alice" searches for "*.sp*" using the WebDAV API + And user "Alice" has created a folder ".hidden" in space "project101" + When user "Alice" searches for "*.hid*" using the WebDAV API Then the HTTP status code should be "207" And the search result should contain "1" entries And the search result of user "Alice" should contain these entries: - | /.space | + | /.hidden | Examples: | dav-path-version | | old | @@ -113,6 +113,28 @@ Feature: Search | spaces | + Scenario Outline: user cannot search the .space folder of a space + Given using DAV path + And user "Alice" has created a folder ".space" in space "project101" + When user "Alice" searches for "*.sp*" using the WebDAV API + Then the HTTP status code should be "207" + And the search result should contain "0" entries + Examples: + | dav-path-version | + | old | + | new | + | spaces | + + + Scenario: user cannot search files inside the .space folder of a space + Given using spaces DAV path + And user "Alice" has created a folder ".space" in space "project101" + And user "Alice" has uploaded a file inside space "project101" with content "hidden content" to ".space/spaceInternal.txt" + When user "Alice" searches for "*spaceInternal*" using the WebDAV API + Then the HTTP status code should be "207" + And the search result should contain "0" entries + + Scenario Outline: user cannot search pending share Given user "Brian" has disabled auto-accepting And using DAV path diff --git a/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature b/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature index 8be5bb964b..6d7a200ee2 100644 --- a/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature +++ b/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature @@ -184,9 +184,8 @@ Feature: media type search And user "Alice" has uploaded file "filesForUpload/data.tar.bz2" to "data.tar.bz2" When user "Alice" searches for "mediatype:folder" using the WebDAV API Then the HTTP status code should be "207" - And the search result should contain "2" entries + And the search result should contain "1" entries And the search result of user "Alice" should contain these entries: - | %spaceid% | | testFolder | When user "Alice" searches for "mediatype:document" using the WebDAV API Then the HTTP status code should be "207" diff --git a/tests/acceptance/features/apiSearch2/tagSearch.feature b/tests/acceptance/features/apiSearch2/tagSearch.feature index a832a9513c..94fc1306d2 100644 --- a/tests/acceptance/features/apiSearch2/tagSearch.feature +++ b/tests/acceptance/features/apiSearch2/tagSearch.feature @@ -261,5 +261,4 @@ Feature: tag search | (tag:mathe OR tag:klass10) NOT tag:physik | 2 | answers | verification work | | tag:mathe NOT name:exercises | 1 | answers | | | tag:mathe AND NOT name:exercises | 1 | answers | | - # The third finding is the personal space itself - | NOT tag:mathe | 3 | verification work | withoutTagFolder | + | NOT tag:mathe | 2 | verification work | withoutTagFolder |