From eff513b0fad4888f939466819f291b3f8b0078cb Mon Sep 17 00:00:00 2001 From: Danis Kirasirov <dbgbbu@gmail.com> Date: Tue, 30 Jan 2024 12:31:34 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=B0=D0=B2=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/expr/mongo.go | 71 +++++++++++++++++++----------------------- pkg/expr/mongo_test.go | 2 +- 2 files changed, 33 insertions(+), 40 deletions(-) diff --git a/pkg/expr/mongo.go b/pkg/expr/mongo.go index b0893b61..ed35a6dc 100644 --- a/pkg/expr/mongo.go +++ b/pkg/expr/mongo.go @@ -231,7 +231,7 @@ func (c *compiler) identifier(node ast.Node) string { } func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} { - if result, ok := c.handleLenNode(node); ok { + if result := c.handleLenNode(node); result != nil { return result } @@ -656,10 +656,12 @@ func (c *compiler) PairNode(node *ast.PairNode) interface{} { //c.compile(node.Value) } -func (c *compiler) handleLenNode(node *ast.BinaryNode) (result bson.M, ok bool) { +// handleLenNode получает узел AST и возвращает запрос для mongo, +// если узел представляет вызов функции len, и nil в противном случае. +func (c *compiler) handleLenNode(node *ast.BinaryNode) bson.M { lenNode, ok := node.Left.(*ast.BuiltinNode) if !ok || lenNode.Name != "len" { - return nil, false + return nil } if len(lenNode.Arguments) != 1 { @@ -667,44 +669,35 @@ func (c *compiler) handleLenNode(node *ast.BinaryNode) (result bson.M, ok bool) } length, ok := c.eval(node.Right).(int) - if !ok || length < 0 { + if !ok { + panic("len() can only be compared with number value") + } + if length < 0 { panic("len() can only be compared with non-negative number") } - switch node.Operator { - case "==": - if length == 0 { - return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$eq": bson.A{}}}, true - } - return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$size": length}}, true - - case "!=": - if length == 0 { - return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$exists": true, "$type": "array", "$ne": bson.A{}}}, true - } - return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$not": bson.M{"$size": length}}}, true - - case ">": - if length == 0 { - return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$exists": true, "$type": "array", "$ne": bson.A{}}}, true - } - return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length): bson.M{"$exists": true}}, true - - case ">=": - if length == 0 { - return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$exists": true, "$type": "array"}}, true - } - return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length-1): bson.M{"$exists": true}}, true - - case "<": - if length == 0 { - panic("invalid comparison: len() cannot be less than 0") - } - return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length-1): bson.M{"$exists": false}}, true - - case "<=": - return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length): bson.M{"$exists": false}}, true + switch op := node.Operator; { + case (op == "==" || op == "<=") && length == 0: + return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$eq": bson.A{}}} + case (op == "!=" || op == ">") && length == 0: + return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$exists": true, "$type": "array", "$ne": bson.A{}}} + case op == "==": + return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$size": length}} + case op == "!=": + return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$not": bson.M{"$size": length}}} + case op == ">": + return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length): bson.M{"$exists": true}} + case op == ">=" && length == 0: + return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$exists": true, "$type": "array"}} + case op == ">=": + return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length-1): bson.M{"$exists": true}} + case op == "<" && length == 0: + panic("invalid comparison: len() cannot be less than 0") + case op == "<": + return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length-1): bson.M{"$exists": false}} + case op == "<=": + return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length): bson.M{"$exists": false}} + default: + panic("invalid comparison operator with len()") } - - panic("invalid comparison operator with len()") } diff --git a/pkg/expr/mongo_test.go b/pkg/expr/mongo_test.go index 4adeaffd..1dde0f37 100644 --- a/pkg/expr/mongo_test.go +++ b/pkg/expr/mongo_test.go @@ -48,7 +48,7 @@ func TestConvertToMongo(t *testing.T) { {"len lt", "len(s) < 1", nil, bson.M{"s.0": bson.M{"$exists": false}}, false}, {"len lt zero", "len(s) < 0", nil, nil, true}, {"len lte", "len(s) <= 1", nil, bson.M{"s.1": bson.M{"$exists": false}}, false}, - {"len lte zero", "len(s) <= 0", nil, bson.M{"s.0": bson.M{"$exists": false}}, false}, + {"len lte zero", "len(s) <= 0", nil, bson.M{"s": bson.M{"$eq": bson.A{}}}, false}, {"field#1", "s.test > 3", nil, bson.M{"s.test": bson.M{"$gt": 3}}, false}, {"field#2", "s['test'] > 3", nil, bson.M{"s.test": bson.M{"$gt": 3}}, false}, {"field#3", "s[test] > 3", nil, bson.M{"s.test": bson.M{"$gt": 3}}, false}, -- GitLab