diff --git a/pkg/expr/expr_test.go b/pkg/expr/expr_test.go index 15710da9fb3933690f677e1f9305288a786bac8b..f8909daadd2af1854100982d7a81f3cf86692f42 100644 --- a/pkg/expr/expr_test.go +++ b/pkg/expr/expr_test.go @@ -39,7 +39,15 @@ func TestIsExpression(t *testing.T) { {"time", fmt.Sprintf("d > Time.Time('%s')", now.Format(time.RFC3339)), true}, {"in", "In(s, [1,2,3])", true}, {"in", "In(s, 1)", true}, - {"exists", "exists(s)", true}, + {"exists#1", "exists(s)", true}, + {"exists#2", "exists", false}, + {"len#1", "len(s) == 1", true}, + {"len#2", "len(s) != 1", true}, + {"len#3", "len(s) > 1", true}, + {"len#4", "len(s) >= 1", true}, + {"len#5", "len(s) < 1", true}, + {"len#6", "len(s) <= 1", true}, + {"len#7", "len", false}, {"text search or id", "id", false}, {"numbers", "3", false}, } diff --git a/pkg/expr/mongo.go b/pkg/expr/mongo.go index bd57a30d727984091ddea0deb2135cfb8b64de8b..b0893b6157546d5c1dcb9920868d5cd3a3de1aa1 100644 --- a/pkg/expr/mongo.go +++ b/pkg/expr/mongo.go @@ -410,10 +410,10 @@ func (c *compiler) CallNode(node *ast.CallNode) interface{} { return bson.M{fields: bson.M{"$in": array}} case "exists": - field := c.identifier(node.Arguments[0]) - if field == "" { - panic("incorrect argument, empty field name") + if len(node.Arguments) != 1 { + panic("exists() expects exactly 1 argument") } + field := c.identifier(node.Arguments[0]) return bson.M{"$or": bson.A{ bson.M{field: bson.M{"$exists": true, "$type": "array"}}, bson.M{field: bson.M{"$ne": nil}}, diff --git a/pkg/expr/mongo_test.go b/pkg/expr/mongo_test.go index ae6bee1681fe3bed38d2743d80cfe6a835961701..4adeaffdb5bdd176fd3d1910a86be6aecb3a5326 100644 --- a/pkg/expr/mongo_test.go +++ b/pkg/expr/mongo_test.go @@ -30,12 +30,25 @@ func TestConvertToMongo(t *testing.T) { {"equal", "s == 3", nil, bson.M{"s": 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}, - {"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}, + {"exists#1", "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#2", "exists(s, s)", nil, nil, true}, + {"len#1", "len(s)", nil, nil, true}, + {"len#2", "len(s) <> 1", nil, nil, true}, + {"len#3", "len(s) == -1", nil, nil, true}, + {"len#4", "len(s, s) == -1", nil, nil, true}, + {"len#5", "len(s) == s", nil, nil, true}, + {"len eq", "len(s) == 1", nil, bson.M{"s": bson.M{"$size": 1}}, false}, + {"len eq zero", "len(s) == 0", nil, bson.M{"s": bson.M{"$eq": bson.A{}}}, false}, + {"len ne", "len(s) != 1", nil, bson.M{"s": bson.M{"$not": bson.M{"$size": 1}}}, false}, + {"len ne zero", "len(s) != 0", nil, bson.M{"s": bson.M{"$exists": true, "$ne": bson.A{}, "$type": "array"}}, 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 gt zero", "len(s) > 0", nil, bson.M{"s": bson.M{"$exists": true, "$type": "array", "$ne": bson.A{}}}, false}, + {"len gte", "len(s) >= 1", nil, bson.M{"s.0": bson.M{"$exists": true}}, false}, + {"len gte zero", "len(s) >= 0", nil, bson.M{"s": bson.M{"$exists": true, "$type": "array"}}, false}, {"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}, {"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}, @@ -52,7 +65,7 @@ func TestConvertToMongo(t *testing.T) { {"iendsWith", "iendsWith(s, 'some')", nil, bson.M{"s": bson.M{"$regex": ".*some$", "$options": "i"}}, false}, {"iendsWith . + () $ {} ^", "iendsWith(s,'. + () $ {} ^')", nil, bson.M{"s": bson.M{"$regex": ".*\\. \\+ \\(\\) \\$ \\{\\} \\^$", "$options": "i"}}, false}, {"or", "s==2 || s > 10", nil, bson.M{"$or": bson.A{bson.M{"s": 2}, bson.M{"s": bson.M{"$gt": 10}}}}, false}, - {"not#1", "not icontains(s, 'some')", nil, bson.M{"$nor":bson.A{bson.M{"s":bson.M{"$options":"i", "$regex":"some"}}}}, false}, + {"not#1", "not icontains(s, 'some')", nil, bson.M{"$nor": bson.A{bson.M{"s": bson.M{"$options": "i", "$regex": "some"}}}}, false}, {"not#2", "not (s.test > 3)", nil, bson.M{"$nor": bson.A{bson.M{"s.test": bson.M{"$gt": 3}}}}, false}, {"search", "search('some') || s > 10", nil, bson.M{"$or": bson.A{bson.M{"$text": bson.M{"$search": "some"}}, bson.M{"s": bson.M{"$gt": 10}}}}, false}, {"vars:or", "s== a + 2 || s > a + 10", map[string]interface{}{"a": 100}, bson.M{"$or": bson.A{bson.M{"s": 102}, bson.M{"s": bson.M{"$gt": 110}}}}, false},