-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
tauslim
committed
Feb 27, 2024
1 parent
c4a9e76
commit 213720f
Showing
3 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |