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

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

parent 05fa795f
Branches
Tags
No related merge requests found
......@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/expr-lang/expr"
......@@ -232,6 +233,17 @@ 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}}
}
return bson.M{c.identifier(node.Left): c.eval(node.Right)}
case "!=":
......@@ -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)}}
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 ">":
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 "<=":
return bson.M{c.identifier(node.Left): bson.M{"$lte": c.eval(node.Right)}}
......
......@@ -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},
{"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},
{"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#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