Skip to content

Commit

Permalink
feat: add base for mongo driver
Browse files Browse the repository at this point in the history
  • Loading branch information
tauslim committed Feb 27, 2024
1 parent c4a9e76 commit 213720f
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 0 deletions.
14 changes: 14 additions & 0 deletions pkg/driver/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,18 @@ package driver

import "gorql"

const (
AndOp = "AND"
OrOp = "OR"
NeOp = "NE"
EqOp = "EQ"
LikeOp = "LIKE"
MatchOp = "MATCH"
GtOp = "GT"
LtOp = "LT"
GeOp = "GE"
LeOp = "LE"
NotOp = "NOT"
)

type TranslatorOpFunc func(*gorql.RqlNode) (string, error)
165 changes: 165 additions & 0 deletions pkg/driver/mongo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package driver

import (
"fmt"
"gorql"
"strings"
)

type MongoTranslator struct {
rootNode *gorql.RqlRootNode
OpsDic map[string]TranslatorOpFunc
}

type AlterValueFunc func(interface{}) (interface{}, error)

func (mt *MongoTranslator) SetOpFunc(op string, f TranslatorOpFunc) {
mt.OpsDic[strings.ToUpper(op)] = f
}

func (mt *MongoTranslator) DeleteOpFunc(op string) {
delete(mt.OpsDic, strings.ToUpper(op))
}

func (mt *MongoTranslator) Where() (string, error) {
if mt.rootNode == nil {
return "", nil
}
return mt.where(mt.rootNode.Node)
}

func (mt *MongoTranslator) where(n *gorql.RqlNode) (string, error) {
if n == nil {
return ``, nil
}
f := mt.OpsDic[strings.ToUpper(n.Op)]
if f == nil {
return "", fmt.Errorf("no TranslatorOpFunc for op : '%s'", n.Op)
}
return f(n)
}

func NewMongoTranslator(r *gorql.RqlRootNode) (mt *MongoTranslator) {
mt = &MongoTranslator{r, map[string]TranslatorOpFunc{}}
mt.SetOpFunc(AndOp, mt.GetAndOrTranslatorOpFunc(strings.ToLower(AndOp)))
mt.SetOpFunc(OrOp, mt.GetAndOrTranslatorOpFunc(OrOp))
//mt.SetOpFunc(NeOp, mt.GetEqualityTranslatorOpFunc("!=", "IS NOT"))
mt.SetOpFunc(EqOp, mt.GetEqualityTranslatorOpFunc(strings.ToLower(EqOp)))
//mt.SetOpFunc(LikeOp, mt.GetFieldValueTranslatorFunc("LIKE", starToPercentFunc))
//mt.SetOpFunc(MatchOp, mt.GetFieldValueTranslatorFunc("ILIKE", starToPercentFunc))
mt.SetOpFunc(GtOp, mt.GetFieldValueTranslatorFunc(strings.ToLower(GtOp)))
//mt.SetOpFunc(LtOp, mt.GetFieldValueTranslatorFunc("<", nil))
//mt.SetOpFunc(GeOp, mt.GetFieldValueTranslatorFunc(">=", nil))
//mt.SetOpFunc(LeOp, mt.GetFieldValueTranslatorFunc("<=", nil))
mt.SetOpFunc(NotOp, mt.GetFieldValueTranslatorFunc(strings.ToLower(NotOp)))
return
}

func (mt *MongoTranslator) GetAndOrTranslatorOpFunc(op string) TranslatorOpFunc {
return func(n *gorql.RqlNode) (s string, err error) {
var ops []string
for _, a := range n.Args {
switch v := a.(type) {
case string:
if !IsValidField(v) {
return "", fmt.Errorf("invalid field name : %s", v)
}
s = s + v
case *gorql.RqlNode:
var tempS string
tempS, err = mt.where(v)
if err != nil {
return "", err
}
ops = append(ops, tempS)
}
}
return fmt.Sprintf("{'$%s': [%s]}", op, strings.Join(ops, ", ")), nil
}
}

func (mt *MongoTranslator) GetEqualityTranslatorOpFunc(op string) TranslatorOpFunc {
return func(n *gorql.RqlNode) (s string, err error) {
return mt.GetFieldValueTranslatorFunc(op)(n)
}
}

func (mt *MongoTranslator) GetFieldValueTranslatorFunc(op string) TranslatorOpFunc {
return func(n *gorql.RqlNode) (s string, err error) {
sep := ""
for i, a := range n.Args {
s += sep
switch v := a.(type) {
case *gorql.RqlNode:
var tempS string
tempS, err = mt.where(v)
if err != nil {
return "", err
}
s = s + tempS
default:
var tempS string
if i == 0 {
if IsValidField(v.(string)) {
tempS = Quote(v.(string))
} else {
return "", fmt.Errorf("first argument must be a valid field name (arg: %s)", v)
}
} else {
convertedValue, err := convert(v)
if err != nil {
return "", err
}
s += fmt.Sprintf("%v", convertedValue)
}
s += tempS
}
sep = fmt.Sprintf(": ")
}
return fmt.Sprintf(`{'$%s': {%s}}`, op, s), nil
}
}

//func (mt *MongoTranslator) GetOpFirstTranslatorFunc(op string) TranslatorOpFunc {
// return func(n *gorql.RqlNode) (s string, err error) {
// sep := ""
// for _, a := range n.Args {
// s += sep
// switch v := a.(type) {
// case string:
// var tempS string
// _, err := strconv.ParseInt(v, 10, 64)
// if err == nil || IsValidField(v) {
// tempS = v
// } else if valueAlterFunc != nil {
// tempS, err = valueAlterFunc(v)
// if err != nil {
// return "", err
// }
// } else {
// tempS = Quote(v)
// }
// s += tempS
// case *gorql.RqlNode:
// var tempS string
// tempS, err = mt.where(v)
// if err != nil {
// return "", err
// }
// s = s + tempS
// }
//
// sep = ", "
// }
//
// return fmt.Sprintf("{'$%s': %s}", op, strings.Join(ops, ", ")), nil
// }
//}

func convert(value interface{}) (interface{}, error) {
switch v := value.(type) {
case string:
return Quote(v), nil
}
return value, nil
}
64 changes: 64 additions & 0 deletions pkg/driver/mongo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package driver

import (
"gorql"
"strings"
"testing"
)

type MongodbTest struct {
Name string // Name of the test
RQL string // Input RQL query
Expected string // Expected Output Expected
WantParseError bool // Test should raise an error when parsing the RQL query
WantTranslatorError bool // Test should raise an error when translating to Expected
}

type MongoModel struct {
Foo string `rql:"filter"`
Price float64 `rql:"filter"`
Disabled bool `rql:"filter"`
}

func (test *MongodbTest) Run(t *testing.T) {
p, err := gorql.NewParser(&gorql.Config{Model: MongoModel{}})
if err != nil {
t.Fatalf("(%s) New parser error :%v\n", test.Name, err)
}
rqlNode, err := p.Parse(strings.NewReader(test.RQL))
if test.WantParseError != (err != nil) {
t.Fatalf("(%s) Expecting error :%v\nGot error : %v", test.Name, test.WantParseError, err)
}
mongoTranslator := NewMongoTranslator(rqlNode)
s, err := mongoTranslator.Where()
if test.WantTranslatorError != (err != nil) {
t.Fatalf("(%s) Expecting error :%v\nGot error : %v \n\tSQL = %s", test.Name, test.WantTranslatorError, err, s)
}

if s != test.Expected {
t.Fatalf("(%s) Translated Mongo query doesn’t match the expected one %s vs %s", test.Name, s, test.Expected)
}
}

var mongodbTests = []MongodbTest{
{
Name: `Basic translation with double equal operators`,
RQL: `and(foo=eq=42,price=eq=10)`,
Expected: `{'$and': [{'$eq': {'foo': '42'}}, {'$eq': {'price': 10}}]}`,
WantParseError: false,
WantTranslatorError: false,
},
//{
// Name: `Basic translation with func style operators`,
// RQL: `and(eq(foo,42),gt(price,10),not(disabled))`,
// Expected: `{'$and': [{'$eq': {'foo': '42'}}, {'$eq': {'price': 10}}, {'$eq': {'disabled': false}}]}`,
// WantParseError: false,
// WantTranslatorError: false,
//},
}

func TestMongodbParser(t *testing.T) {
for _, test := range mongodbTests {
test.Run(t)
}
}

0 comments on commit 213720f

Please sign in to comment.