diff --git a/changelog/unreleased/kql-fix-unary.md b/changelog/unreleased/kql-fix-unary.md new file mode 100644 index 00000000000..2ba72a9848d --- /dev/null +++ b/changelog/unreleased/kql-fix-unary.md @@ -0,0 +1,6 @@ +Fix: Fixed cunary in the beginning + +Fixed case when the unary in the beginning lead to panic + + +https://github.com/owncloud/ocis/pull/7247 diff --git a/services/search/pkg/query/bleve/compiler.go b/services/search/pkg/query/bleve/compiler.go index 114052bcce1..5876f0b81ab 100644 --- a/services/search/pkg/query/bleve/compiler.go +++ b/services/search/pkg/query/bleve/compiler.go @@ -39,7 +39,10 @@ func (c Compiler) Compile(givenAst *ast.Ast) (bleveQuery.Query, error) { } func compile(a *ast.Ast) (bleveQuery.Query, error) { - q, _ := walk(0, a.Nodes) + q, _, err := walk(0, a.Nodes) + if err != nil { + return nil, err + } switch q.(type) { case *bleveQuery.ConjunctionQuery, *bleveQuery.DisjunctionQuery: return q, nil @@ -47,7 +50,7 @@ func compile(a *ast.Ast) (bleveQuery.Query, error) { return bleve.NewConjunctionQuery(q), nil } -func walk(offset int, nodes []ast.Node) (bleveQuery.Query, int) { +func walk(offset int, nodes []ast.Node) (bleveQuery.Query, int, error) { var prev, next bleveQuery.Query var operator *ast.OperatorNode var isGroup bool @@ -113,7 +116,10 @@ func walk(offset int, nodes []ast.Node) (bleveQuery.Query, int) { if n.Key != "" { n = normalizeGroupingProperty(n) } - q, _ := walk(0, n.Nodes) + q, _, err := walk(0, n.Nodes) + if err != nil { + return nil, 0, err + } if prev == nil { prev = q isGroup = true @@ -124,10 +130,19 @@ func walk(offset int, nodes []ast.Node) (bleveQuery.Query, int) { if n.Value == kql.BoolAND || n.Value == kql.BoolOR { operator = n } else if n.Value == kql.BoolNOT { - next, offset = nextNode(i+1, nodes) + var err error + next, offset, err = nextNode(i+1, nodes) + if err != nil { + return nil, 0, err + } q := bleve.NewBooleanQuery() q.AddMustNot(next) - next = q + if prev == nil { + // unary in the beginning + prev = q + } else { + next = q + } } } if prev != nil && next != nil && operator != nil { @@ -140,13 +155,19 @@ func walk(offset int, nodes []ast.Node) (bleveQuery.Query, int) { i = offset } } - return prev, offset + if prev == nil { + return nil, 0, fmt.Errorf("can not compile the query") + } + return prev, offset, nil } -func nextNode(offset int, nodes []ast.Node) (bleveQuery.Query, int) { +func nextNode(offset int, nodes []ast.Node) (bleveQuery.Query, int, error) { if n, ok := nodes[offset].(*ast.GroupNode); ok { - gq, _ := walk(0, n.Nodes) - return gq, offset + 1 + gq, _, err := walk(0, n.Nodes) + if err != nil { + return nil, 0, err + } + return gq, offset + 1, nil } if n, ok := nodes[offset].(*ast.OperatorNode); ok { if n.Value == kql.BoolNOT { diff --git a/services/search/pkg/query/bleve/compiler_test.go b/services/search/pkg/query/bleve/compiler_test.go index fa4860cfd17..d48f4c61e04 100644 --- a/services/search/pkg/query/bleve/compiler_test.go +++ b/services/search/pkg/query/bleve/compiler_test.go @@ -255,6 +255,56 @@ func Test_compile(t *testing.T) { }), wantErr: false, }, + { + name: `(tag:klass10) NOT tag:physik`, + args: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{ + Key: "", + Nodes: []ast.Node{ + &ast.StringNode{Key: "tag", Value: "klass10"}, + }, + }, + &ast.OperatorNode{Value: "AND"}, + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "tag", Value: "physik"}, + }, + }, + want: query.NewConjunctionQuery([]query.Query{ + query.NewQueryStringQuery(`Tags:klass10`), + query.NewBooleanQuery(nil, nil, []query.Query{query.NewQueryStringQuery(`Tags:physik`)}), + }), + wantErr: false, + }, + { + name: `tag:klass10 NOT tag:physik`, + args: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "tag", Value: "klass10"}, + &ast.OperatorNode{Value: "OR"}, + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "tag", Value: "physik"}, + }, + }, + want: query.NewDisjunctionQuery([]query.Query{ + query.NewQueryStringQuery(`Tags:klass10`), + query.NewBooleanQuery(nil, nil, []query.Query{query.NewQueryStringQuery(`Tags:physik`)}), + }), + wantErr: false, + }, + { + name: `NOT tag:physik`, + args: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "tag", Value: "physik"}, + }, + }, + want: query.NewConjunctionQuery([]query.Query{ + query.NewBooleanQuery(nil, nil, []query.Query{query.NewQueryStringQuery(`Tags:physik`)}), + }), + wantErr: false, + }, { name: `ast.DateTimeNode`, args: &ast.Ast{ diff --git a/services/search/pkg/query/kql/dictionary_test.go b/services/search/pkg/query/kql/dictionary_test.go index 0b56eba69de..943b00dabdc 100644 --- a/services/search/pkg/query/kql/dictionary_test.go +++ b/services/search/pkg/query/kql/dictionary_test.go @@ -279,6 +279,7 @@ func TestParse(t *testing.T) { }, }, }, + { name: "DateTimeRestrictionNode", givenQuery: []string{ diff --git a/services/search/pkg/query/kql/normalize_test.go b/services/search/pkg/query/kql/normalize_test.go index 7beaf60e70a..c1c4b55954c 100644 --- a/services/search/pkg/query/kql/normalize_test.go +++ b/services/search/pkg/query/kql/normalize_test.go @@ -98,6 +98,50 @@ func TestNormalizeNodes(t *testing.T) { &ast.DateTimeNode{Key: "mtime", Operator: &ast.OperatorNode{Value: "="}, Value: now}, }, }, + // TODO + // This two expressions are contradictory + // (tag:klass10) NOT tag:physik will be transeted to tag:klass10 AND NOT tag:physik + // But + // tag:klass10 NOT tag:physik will be transeted to tag:klass10 OR NOT tag:physik + // How do we have to handle it? + { + name: "implicit AND - (tag:klass10) NOT tag:physik", + givenNodes: []ast.Node{ + &ast.GroupNode{ + Key: "", + Nodes: []ast.Node{ + &ast.StringNode{Key: "tag", Value: "klass10"}, + }, + }, + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "tag", Value: "physik"}, + }, + expectedNodes: []ast.Node{ + &ast.GroupNode{ + Key: "", + Nodes: []ast.Node{ + &ast.StringNode{Key: "tag", Value: "klass10"}, + }, + }, + &ast.OperatorNode{Value: "AND"}, + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "tag", Value: "physik"}, + }, + }, + { + name: "implicit AND - tag:klass10 NOT tag:physik", + givenNodes: []ast.Node{ + &ast.StringNode{Key: "tag", Value: "klass10"}, + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "tag", Value: "physik"}, + }, + expectedNodes: []ast.Node{ + &ast.StringNode{Key: "tag", Value: "klass10"}, + &ast.OperatorNode{Value: "OR"}, + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "tag", Value: "physik"}, + }, + }, } assert := tAssert.New(t)