Skip to content

Commit

Permalink
builder的update与where map支持传入_custom_前缀的key&&Comparable的value (#151)
Browse files Browse the repository at this point in the history
* feat: add custom sql support and several json helper function

* doc: update builder readme.md since additional new custom functions
  • Loading branch information
jiangwei2514 authored Jul 9, 2024
1 parent 47b22f0 commit 07898fa
Show file tree
Hide file tree
Showing 5 changed files with 762 additions and 25 deletions.
17 changes: 12 additions & 5 deletions builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
qb "github.com/didi/gendry/builder"
"github.com/didi/gendry/builder"
)

func main() {
db,err := sql.Open("mysql", "xxxxxxxxxxx")
if nil != err {
panic(err)
}
mp := map[string]interface{}{
where := map[string]interface{}{
"_custom_0": builder.Custom("name=?", "name0"),
"_custom_1": builder.JsonContains("my_json->'$.list'", 1),
"country": "China",
"role": "driver",
"age >": 45,
Expand All @@ -44,10 +46,10 @@ func main() {
},
"_orderby": "age desc",
}
cond,vals,err := qb.BuildSelect("tableName", where, []string{"name", "count(price) as total", "age"})
cond,vals,err := builder.BuildSelect("tableName", where, []string{"name", "count(price) as total", "age"})

//cond: SELECT name,count(price) as total,age FROM tableName WHERE (((x1=? AND x2>=?) OR (x3=? AND x4!=?)) AND country=? AND gmt_create < gmt_modified AND role=? AND age>?) GROUP BY name HAVING (total>? AND total<=?) ORDER BY age DESC
//vals: []interface{}{11, 45, "234", "tx2", "China", "driver", 45, 1000, 50000}
//cond: SELECT name,count(price) as total,age FROM tableName WHERE (name=? AND (? MEMBER OF(my_json->'$.list')) AND ((x1=? AND x2>=?) OR (x3=? AND x4!=?)) AND country=? AND role=? AND age>? AND gmt_create<gmt_modified) GROUP BY name HAVING (total>? AND total<=?) ORDER BY age desc
//vals: []interface{}{"name0", 1, 11, 45, "234", "tx2", "China", "driver", 45, 1000, 50000}

if nil != err {
panic(err)
Expand Down Expand Up @@ -111,10 +113,12 @@ others supported:
* _having
* _limit
* _lockMode
* _custom_xxx

``` go
where := map[string]interface{}{
"age >": 100,
"_custom_1": builder.JsonContains("my_json->'$.list'", 1),
"_or": []map[string]interface{}{
{
"x1": 11,
Expand All @@ -140,6 +144,8 @@ Note:
* value of _lockMode only supports `share` and `exclusive` temporarily:
* `share` representative `SELECT ... LOCK IN SHARE MODE`. Unfortunately, the current version does not support `SELECT ... FOR SHARE`, It'll be supported in the future.
* `exclusive` representative `SELECT ... FOR UPDATE`
* if key starts with `_custom_`, the corresponding value must be a `builder.Comparable`. We provide builtin type such as `Custom` and `JsonContains`. You can also provide your own implementation if you want
* `JsonSet`,`JsonArrayAppend`,`JsonArrayInsert`,`JsonRemove` should be used in update map rather than where map

#### Aggregate

Expand Down Expand Up @@ -199,6 +205,7 @@ where := map[string]interface{}{
update := map[string]interface{}{
"role": "primaryschoolstudent",
"rank": 5,
"_custom_0": qb.JsonArrayAppend("my_json", "$", 0, "$", 1),
}
cond,vals,err := qb.BuildUpdate("table_name", where, update)

Expand Down
19 changes: 18 additions & 1 deletion builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var (
errLockModeValueType = errors.New(`[builder] the value of "_lockMode" must be of string type`)
errNotAllowedLockMode = errors.New(`[builder] the value of "_lockMode" is not allowed`)
errLimitType = errors.New(`[builder] the value of "_limit" must be one of int,uint,int64,uint64`)
errCustomValueType = errors.New(`[builder] the value of "_custom_" must impl Comparable`)

errWhereInterfaceSliceType = `[builder] the value of "xxx %s" must be of []interface{} type`
errEmptySliceCondition = `[builder] the value of "%s" must contain at least one element`
Expand Down Expand Up @@ -252,7 +253,15 @@ func getWhereConditions(where map[string]interface{}, ignoreKeys map[string]stru
var comparables []Comparable
var field, operator string
var err error
for key, val := range where {
// to keep the result in certain order
keys := make([]string, 0, len(where))
for key := range where {
keys = append(keys, key)
}
defaultSortAlgorithm(keys)

for _, key := range keys {
val := where[key]
if _, ok := ignoreKeys[key]; ok {
continue
}
Expand All @@ -278,6 +287,14 @@ func getWhereConditions(where map[string]interface{}, ignoreKeys map[string]stru
comparables = append(comparables, OrWhere(orWhereComparable))
continue
}
if strings.HasPrefix(key, "_custom_") {
v, ok := val.(Comparable)
if !ok {
return nil, errCustomValueType
}
comparables = append(comparables, v)
continue
}
field, operator, err = splitKey(key, val)
if nil != err {
return nil, err
Expand Down
49 changes: 30 additions & 19 deletions builder/dao.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ var (
}
)

//the order of a map is unpredicatable so we need a sort algorithm to sort the fields
//and make it predicatable
// the order of a map is unpredicatable so we need a sort algorithm to sort the fields
// and make it predicatable
var (
defaultSortAlgorithm = sort.Strings
)

//Comparable requires type implements the Build method
// Comparable requires type implements the Build method
type Comparable interface {
Build() ([]string, []interface{})
}
Expand Down Expand Up @@ -123,58 +123,58 @@ func (l NotLike) Build() ([]string, []interface{}) {
return cond, vals
}

//Eq means equal(=)
// Eq means equal(=)
type Eq map[string]interface{}

//Build implements the Comparable interface
// Build implements the Comparable interface
func (e Eq) Build() ([]string, []interface{}) {
return build(e, "=")
}

//Ne means Not Equal(!=)
// Ne means Not Equal(!=)
type Ne map[string]interface{}

//Build implements the Comparable interface
// Build implements the Comparable interface
func (n Ne) Build() ([]string, []interface{}) {
return build(n, "!=")
}

//Lt means less than(<)
// Lt means less than(<)
type Lt map[string]interface{}

//Build implements the Comparable interface
// Build implements the Comparable interface
func (l Lt) Build() ([]string, []interface{}) {
return build(l, "<")
}

//Lte means less than or equal(<=)
// Lte means less than or equal(<=)
type Lte map[string]interface{}

//Build implements the Comparable interface
// Build implements the Comparable interface
func (l Lte) Build() ([]string, []interface{}) {
return build(l, "<=")
}

//Gt means greater than(>)
// Gt means greater than(>)
type Gt map[string]interface{}

//Build implements the Comparable interface
// Build implements the Comparable interface
func (g Gt) Build() ([]string, []interface{}) {
return build(g, ">")
}

//Gte means greater than or equal(>=)
// Gte means greater than or equal(>=)
type Gte map[string]interface{}

//Build implements the Comparable interface
// Build implements the Comparable interface
func (g Gte) Build() ([]string, []interface{}) {
return build(g, ">=")
}

//In means in
// In means in
type In map[string][]interface{}

//Build implements the Comparable interface
// Build implements the Comparable interface
func (i In) Build() ([]string, []interface{}) {
if nil == i || 0 == len(i) {
return nil, nil
Expand All @@ -199,10 +199,10 @@ func buildIn(field string, vals []interface{}) (cond string) {
return
}

//NotIn means not in
// NotIn means not in
type NotIn map[string][]interface{}

//Build implements the Comparable interface
// Build implements the Comparable interface
func (i NotIn) Build() ([]string, []interface{}) {
if nil == i || 0 == len(i) {
return nil, nil
Expand Down Expand Up @@ -416,6 +416,17 @@ func resolveUpdate(update map[string]interface{}) (sets string, vals []interface
sb.WriteString(fmt.Sprintf("%s=%s,", k, v))
continue
}
if strings.HasPrefix(k, "_custom_") {
if custom, ok := v.(Comparable); ok {
sql, val := custom.Build()
for _, s := range sql {
sb.WriteString(s)
sb.WriteByte(',')
}
vals = append(vals, val...)
}
continue
}
vals = append(vals, v)
sb.WriteString(fmt.Sprintf("%s=?,", quoteField(k)))
}
Expand Down
Loading

0 comments on commit 07898fa

Please sign in to comment.