Skip to content
Snippets Groups Projects
Commit b02e1c7b authored by Danis Kirasirov's avatar Danis Kirasirov
Browse files

Рефакторинг обработки функции len

parent 0a5db0f2
No related branches found
No related tags found
No related merge requests found
......@@ -231,19 +231,12 @@ func (c *compiler) identifier(node ast.Node) string {
}
func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} {
switch node.Operator {
case "==":
op := c.eval(node.Right)
lenNode, ok := node.Left.(*ast.BuiltinNode)
if ok && lenNode.Name == "len" && len(lenNode.Arguments) == 1 {
// Оптимизация для случая len(arr) == 0, т.к. $size не использует индекс
if op == 0 {
return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$eq": bson.A{}}}
}
return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$size": op}}
if result, ok := c.handleLenNode(node); ok {
return result
}
switch node.Operator {
case "==":
return bson.M{c.identifier(node.Left): c.eval(node.Right)}
case "!=":
......@@ -262,36 +255,10 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} {
return bson.M{c.identifier(node.Left): bson.M{"$nin": c.eval(node.Right)}}
case "<":
op := c.eval(node.Right)
lenNode, ok := node.Left.(*ast.BuiltinNode)
if ok && lenNode.Name == "len" && len(lenNode.Arguments) == 1 {
length, ok := op.(int)
if !ok {
panic("len must be compared with an integer")
}
return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length): bson.M{"$exists": false}}
}
return bson.M{c.identifier(node.Left): bson.M{"$lt": c.eval(node.Right)}}
case ">":
op := c.eval(node.Right)
lenNode, ok := node.Left.(*ast.BuiltinNode)
if ok && lenNode.Name == "len" && len(lenNode.Arguments) == 1 {
length, ok := op.(int)
if !ok {
panic("len must be compared with an integer")
}
// Оптимизация для случая len(arr) > 0
if length == 0 {
return bson.M{c.identifier(lenNode.Arguments[0]): bson.M{"$exists": true, "$type": "array", "$ne": bson.A{}}}
}
return bson.M{c.identifier(lenNode.Arguments[0]) + "." + strconv.Itoa(length): bson.M{"$exists": true}}
}
return bson.M{c.identifier(node.Left): bson.M{"$gt": op}}
return bson.M{c.identifier(node.Left): bson.M{"$gt": c.eval(node.Right)}}
case "<=":
return bson.M{c.identifier(node.Left): bson.M{"$lte": c.eval(node.Right)}}
......@@ -688,3 +655,56 @@ func (c *compiler) PairNode(node *ast.PairNode) interface{} {
//c.compile(node.Key)
//c.compile(node.Value)
}
func (c *compiler) handleLenNode(node *ast.BinaryNode) (result bson.M, ok bool) {
lenNode, ok := node.Left.(*ast.BuiltinNode)
if !ok || lenNode.Name != "len" {
return nil, false
}
if len(lenNode.Arguments) != 1 {
panic("len() expects exactly 1 argument")
}
length, ok := c.eval(node.Right).(int)
if !ok || 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
}
panic("invalid comparison operator with len()")
}
......@@ -35,7 +35,7 @@ func TestConvertToMongo(t *testing.T) {
{"len equal empty", "len(s) == 0", nil, bson.M{"s": bson.M{"$eq": bson.A{}}}, false},
{"len gt", "len(s) > 1", nil, bson.M{"s.1": bson.M{"$exists": true}}, false},
{"len gt 0", "len(s) > 0", nil, bson.M{"s": bson.M{"$exists": true, "$type": "array", "$ne": bson.A{}}}, false},
{"len lt", "len(s) < 1", nil, bson.M{"s.1": bson.M{"$exists": false}}, false},
{"len lt", "len(s) < 1", nil, bson.M{"s.0": bson.M{"$exists": false}}, 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},
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment