Select Git revision
mongo_test.go
mongo_test.go 5.59 KiB
package expr
import (
"fmt"
"testing"
"time"
"git.perx.ru/perxis/perxis-go/pkg/id"
"github.com/expr-lang/expr"
"github.com/expr-lang/expr/ast"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"golang.org/x/net/context"
)
func TestConvertToMongo(t *testing.T) {
now := time.Now()
dt, _ := time.Parse("2006-01-02", "2021-08-31")
tm, _ := time.Parse(time.RFC3339, now.Format(time.RFC3339))
ctx := context.Background()
tests := []struct {
name string
eval string
env map[string]interface{}
wantB bson.M
wantErr bool
}{
{"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},
{"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.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},
{"contains", "s contains 'some'", nil, bson.M{"s": bson.M{"$regex": "some"}}, false},
{"contains with . + () $ {} ^", "value contains 'something with . + () $ {} ^'", nil, bson.M{"value": bson.M{"$regex": "something with \\. \\+ \\(\\) \\$ \\{\\} \\^"}}, false},
{"startsWith", "s startsWith 'some'", nil, bson.M{"s": bson.M{"$regex": "^some.*"}}, false},
{"startsWith . + () $ {} ^", "s startsWith '. + () $ {} ^'", nil, bson.M{"s": bson.M{"$regex": "^\\. \\+ \\(\\) \\$ \\{\\} \\^.*"}}, false},
{"endsWith", "s endsWith 'some'", nil, bson.M{"s": bson.M{"$regex": ".*some$"}}, false},
{"endsWith . + () $ {} ^", "s endsWith '. + () $ {} ^'", nil, bson.M{"s": bson.M{"$regex": ".*\\. \\+ \\(\\) \\$ \\{\\} \\^$"}}, false},
{"icontains", "icontains(s, 'some')", nil, bson.M{"s": bson.M{"$regex": "some", "$options": "i"}}, false},
{"icontains with . + () $ {} ^", "icontains (value, 'something with . + () $ {} ^')", nil, bson.M{"value": bson.M{"$regex": "something with \\. \\+ \\(\\) \\$ \\{\\} \\^", "$options": "i"}}, false},
{"istartsWith", "istartsWith(s, 'Some')", nil, bson.M{"s": bson.M{"$regex": "^Some.*", "$options": "i"}}, false},
{"istartsWith . + () $ {} ^ . + () $ {} ^", "istartsWith(s, '. + () $ {} ^')", nil, bson.M{"s": bson.M{"$regex": "^\\. \\+ \\(\\) \\$ \\{\\} \\^.*", "$options": "i"}}, false},
{"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#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},
{"near", "near(a, [55.5, 37.5], 1000)", map[string]interface{}{"a": []interface{}{55, 37}}, bson.M{"a.geometry": bson.M{"$near": bson.D{{Key: "$geometry", Value: map[string]interface{}{"coordinates": []interface{}{55.5, 37.5}, "type": "Point"}}, {Key: "$maxDistance", Value: 1000}}}}, false},
{"within", "within(a, 'box', [[54.54, 36.36], [55.55, 37.37]])", map[string]interface{}{"a": []interface{}{55, 37}}, bson.M{"a.geometry": bson.M{"$geoWithin": bson.M{"$box": []interface{}{[]interface{}{54.54, 36.36}, []interface{}{55.55, 37.37}}}}}, false},
{"time", "d > Time.Date('2021-08-31')", nil, bson.M{"d": bson.M{"$gt": dt}}, false},
{"time", fmt.Sprintf("d > Time.Time('%s')", now.Format(time.RFC3339)), nil, bson.M{"d": bson.M{"$gt": tm}}, false},
{"in", "In(s, [1,2,3])", nil, bson.M{"s": bson.M{"$in": []interface{}{1, 2, 3}}}, false},
{"in", "In(s, 1)", nil, bson.M{"s": bson.M{"$in": []interface{}{1}}}, false},
{"text search or id", "id", nil, nil, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotB, err := ConvertToMongo(ctx, tt.eval, tt.env, nil)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.wantB, gotB)
})
}
}
func BenchmarkConvertToMongo(b *testing.B) {
const idsNum = 1_000_000
ctx := context.Background()
ids := make([]string, idsNum)
for i := 0; i < idsNum; i++ {
ids[i] = id.GenerateNewID()
}
exp := InStringArray("id", ids)
//fmt.Println(len(exp))
for i := 0; i < b.N; i++ {
_, _ = ConvertToMongo(ctx, exp, nil, nil, expr.Patch(&testVisitor{}))
}
}
type testVisitor struct{}
func (v *testVisitor) Visit(node *ast.Node) {
if n, ok := (*node).(*ast.IdentifierNode); ok {
n.Value = "some" + "." + n.Value
}
}