package expr import ( "fmt" "testing" "time" "git.perx.ru/perxis/perxis-go/pkg/id" "github.com/antonmedv/expr" "github.com/antonmedv/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}, {"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}, {"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, bson.M{"$or": bson.A{bson.M{"_id": "id"}, bson.M{"$text": bson.M{"$search": "id"}}}}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotB, err := ConvertToMongo(ctx, tt.eval, tt.env, nil) 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) Enter(node *ast.Node) {} func (v *testVisitor) Exit(node *ast.Node) { if n, ok := (*node).(*ast.IdentifierNode); ok { n.Value = "some" + "." + n.Value } }