diff --git a/services/search/pkg/opensearch/kql.go b/services/search/pkg/opensearch/kql.go index 632e62d3ea..ec77aa5121 100644 --- a/services/search/pkg/opensearch/kql.go +++ b/services/search/pkg/opensearch/kql.go @@ -47,12 +47,15 @@ func (k *KQL) compile(nodes []ast.Node) (Builder, error) { for i, node := range nodes { nextOp := k.getOperatorValueAt(nodes, i+1) + prevOp := k.getOperatorValueAt(nodes, i-1) switch { case nextOp == kql.BoolOR: add = boolQuery.Should case nextOp == kql.BoolAND: add = boolQuery.Must + case prevOp == kql.BoolNOT: + add = boolQuery.MustNot } builder, err := k.getBuilder(node) diff --git a/services/search/pkg/opensearch/kql_test.go b/services/search/pkg/opensearch/kql_test.go index e17449b406..6a2c896834 100644 --- a/services/search/pkg/opensearch/kql_test.go +++ b/services/search/pkg/opensearch/kql_test.go @@ -210,6 +210,36 @@ func TestKQL_Compile(t *testing.T) { opensearch.NewTermQuery[string]("age").Value("32"), ), }, + { + Name: "[NOT *]", + Got: &ast.Ast{ + Nodes: []ast.Node{ + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "age", Value: "32"}, + }, + }, + Want: opensearch.NewBoolQuery(). + MustNot( + opensearch.NewTermQuery[string]("age").Value("32"), + ), + }, + { + Name: "[* NOT *]", + Got: &ast.Ast{ + Nodes: []ast.Node{ + &ast.StringNode{Key: "name", Value: "openCloud"}, + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "age", Value: "32"}, + }, + }, + Want: opensearch.NewBoolQuery(). + Must( + opensearch.NewTermQuery[string]("Name").Value("openCloud"), + ). + MustNot( + opensearch.NewTermQuery[string]("age").Value("32"), + ), + }, { Name: "[* OR * OR *]", Got: &ast.Ast{ @@ -269,7 +299,7 @@ func TestKQL_Compile(t *testing.T) { ), }, { - Name: "NEW[* OR * AND *]", + Name: "[* OR * AND *]", Got: &ast.Ast{ Nodes: []ast.Node{ &ast.StringNode{Key: "a", Value: "a"}, @@ -314,6 +344,34 @@ func TestKQL_Compile(t *testing.T) { opensearch.NewTermQuery[string]("d").Value("d"), ), }, + { + Name: "[[* OR * OR *] AND NOT *]", + Got: &ast.Ast{ + Nodes: []ast.Node{ + &ast.GroupNode{Nodes: []ast.Node{ + &ast.StringNode{Key: "a", Value: "a"}, + &ast.OperatorNode{Value: "OR"}, + &ast.StringNode{Key: "b", Value: "b"}, + &ast.OperatorNode{Value: "OR"}, + &ast.StringNode{Key: "c", Value: "c"}, + }}, + &ast.OperatorNode{Value: "AND"}, + &ast.OperatorNode{Value: "NOT"}, + &ast.StringNode{Key: "d", Value: "d"}, + }, + }, + Want: opensearch.NewBoolQuery(). + Must( + opensearch.NewBoolQuery(opensearch.BoolQueryOptions{MinimumShouldMatch: 1}). + Should( + opensearch.NewTermQuery[string]("a").Value("a"), + opensearch.NewTermQuery[string]("b").Value("b"), + opensearch.NewTermQuery[string]("c").Value("c"), + ), + ).MustNot( + opensearch.NewTermQuery[string]("d").Value("d"), + ), + }, } for _, test := range tests {