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

Реализована функция len(arr) для <, >, ==

parent 05fa795f
No related branches found
No related tags found
No related merge requests found
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/expr-lang/expr" "github.com/expr-lang/expr"
...@@ -232,6 +233,17 @@ func (c *compiler) identifier(node ast.Node) string { ...@@ -232,6 +233,17 @@ func (c *compiler) identifier(node ast.Node) string {
func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} { func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} {
switch node.Operator { switch node.Operator {
case "==": 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}}
}
return bson.M{c.identifier(node.Left): c.eval(node.Right)} return bson.M{c.identifier(node.Left): c.eval(node.Right)}
case "!=": case "!=":
...@@ -250,10 +262,36 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} { ...@@ -250,10 +262,36 @@ func (c *compiler) BinaryNode(node *ast.BinaryNode) interface{} {
return bson.M{c.identifier(node.Left): bson.M{"$nin": c.eval(node.Right)}} return bson.M{c.identifier(node.Left): bson.M{"$nin": c.eval(node.Right)}}
case "<": 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)}} return bson.M{c.identifier(node.Left): bson.M{"$lt": c.eval(node.Right)}}
case ">": case ">":
return bson.M{c.identifier(node.Left): bson.M{"$gt": c.eval(node.Right)}} 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}}
case "<=": case "<=":
return bson.M{c.identifier(node.Left): bson.M{"$lte": c.eval(node.Right)}} return bson.M{c.identifier(node.Left): bson.M{"$lte": c.eval(node.Right)}}
......
...@@ -31,6 +31,11 @@ func TestConvertToMongo(t *testing.T) { ...@@ -31,6 +31,11 @@ func TestConvertToMongo(t *testing.T) {
{"in array", "s in [1,2,3]", nil, bson.M{"s": bson.M{"$in": []interface{}{1, 2, 3}}}, false}, {"in array", "s in [1,2,3]", nil, bson.M{"s": bson.M{"$in": []interface{}{1, 2, 3}}}, false},
{"not in array", "s not in [1,2,3]", nil, bson.M{"s": bson.M{"$nin": []interface{}{1, 2, 3}}}, false}, {"not in array", "s not in [1,2,3]", nil, bson.M{"s": bson.M{"$nin": []interface{}{1, 2, 3}}}, false},
{"exists", "exists(s)", nil, bson.M{"$or": bson.A{bson.M{"s": bson.M{"$exists": true, "$type": "array"}}, bson.M{"s": bson.M{"$ne": nil}}}}, false}, {"exists", "exists(s)", nil, bson.M{"$or": bson.A{bson.M{"s": bson.M{"$exists": true, "$type": "array"}}, bson.M{"s": bson.M{"$ne": nil}}}}, false},
{"len equal", "len(s) == 1", nil, bson.M{"s": bson.M{"$size": 1}}, false},
{"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},
{"field#1", "s.test > 3", nil, bson.M{"s.test": bson.M{"$gt": 3}}, 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#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}, {"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