improve gdb/gstr/gconv/garray

This commit is contained in:
John 2019-10-21 19:13:25 +08:00
parent 88045417ff
commit d4051df5b6
31 changed files with 867 additions and 567 deletions

View File

@ -0,0 +1,24 @@
package main
import (
"github.com/gogf/gf/frame/g"
)
func main() {
// error!
r, err := g.DB().Table("user").Where(g.Map{
"or": g.Map{
"nickname": "jim",
"create_time > ": "2019-10-01",
},
"and": g.Map{
"nickname": "tom",
"create_time > ": "2019-10-01",
},
}).All()
if err != nil {
panic(err)
}
g.Dump(r)
}

View File

@ -0,0 +1,60 @@
// This is auto-generated by gf cli tool. You may not really want to edit it.
package defaults
import (
"database/sql"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
)
import (
"github.com/gogf/gf/os/gtime"
)
// User is the golang structure for table user.
type User struct {
Id int `orm:"id,primary" json:"id"`
Passport string `orm:"passport" json:"passport"`
Password string `orm:"password" json:"password"`
Nickname string `orm:"nickname,unique" json:"nickname"`
CreateTime *gtime.Time `orm:"create_time" json:"create_time"`
}
var (
// TableUser is the table name of user.
TableUser = "user"
// ModelUser is the model object of user.
ModelUser = g.DB("default").Table(TableUser).Safe()
)
// Inserts does "INSERT...INTO..." statement for inserting current object into table.
func (r *User) Insert() (result sql.Result, err error) {
return ModelUser.Data(r).Insert()
}
// Replace does "REPLACE...INTO..." statement for inserting current object into table.
// If there's already another same record in the table (it checks using primary key or unique index),
// it deletes it and insert this one.
func (r *User) Replace() (result sql.Result, err error) {
return ModelUser.Data(r).Replace()
}
// Save does "INSERT...INTO..." statement for inserting/updating current object into table.
// It updates the record if there's already another same record in the table
// (it checks using primary key or unique index).
func (r *User) Save() (result sql.Result, err error) {
return ModelUser.Data(r).Save()
}
// Update does "UPDATE...WHERE..." statement for updating current object from table.
// It updates the record if there's already another same record in the table
// (it checks using primary key or unique index).
func (r *User) Update() (result sql.Result, err error) {
return ModelUser.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update()
}
// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table.
func (r *User) Delete() (result sql.Result, err error) {
return ModelUser.Where(gdb.GetWhereConditionOfStruct(r)).Delete()
}

View File

@ -1,4 +1,11 @@
viewpath = "/home/www/templates"
# MySQL数据库配置
[database]
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
[redis]
disk = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"

View File

@ -1,15 +1,13 @@
package main
import (
_ "github.com/gogf/gf/.example/frame/mvc/controller/demo"
_ "github.com/gogf/gf/.example/frame/mvc/controller/stats"
"github.com/gogf/gf/frame/g"
"fmt"
"github.com/gogf/gf/.example/frame/mvc/app/model/defaults"
"github.com/gogf/gf/database/gdb"
)
func main() {
//g.Server().SetDumpRouteMap(false)
g.Server().SetPort(8199)
g.Server().Run()
u := defaults.User{Id: 1, Nickname: "test"}
fmt.Println(gdb.GetWhereConditionOfStruct(&u))
fmt.Println(u.Replace())
}

View File

@ -1,8 +0,0 @@
package test
import "github.com/gogf/gf/database/gdb"
var (
// ConfigGroup is the configuration group name for this model.
ConfigGroup = gdb.DEFAULT_GROUP_NAME
)

View File

@ -1,92 +0,0 @@
package test
import (
"database/sql"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/os/gtime"
)
// User is the golang structure for table user.
type User struct {
Id int `orm:"id,primary" json:"id"`
Passport string `orm:"passport" json:"passport"`
Password string `orm:"password" json:"password"`
NickName string `orm:"nickname" json:"nick_name"`
CreateTime *gtime.Time `orm:"create_time" json:"create_time"`
}
// UserModel is the model of convenient operations for table user.
type UserModel struct {
*gdb.Model
TableName string
}
var (
// UserTableName is the table name of user.
UserTableName = "user"
)
// ModelUser creates and returns a new model object for table user.
func ModelUser() *UserModel {
return &UserModel{
g.DB(ConfigGroup).Table(UserTableName).Safe(),
UserTableName,
}
}
// Inserts does "INSERT...INTO..." statement for inserting current object into table.
func (r *User) Insert() (result sql.Result, err error) {
return ModelUser().Data(r).Insert()
}
// Replace does "REPLACE...INTO..." statement for inserting current object into table.
// If there's already another same record in the table (it checks using primary key or unique index),
// it deletes it and insert this one.
func (r *User) Replace() (result sql.Result, err error) {
return ModelUser().Data(r).Replace()
}
// Save does "INSERT...INTO..." statement for inserting/updating current object into table.
// It updates the record if there's already another same record in the table
// (it checks using primary key or unique index).
func (r *User) Save() (result sql.Result, err error) {
return ModelUser().Data(r).Save()
}
// Update does "UPDATE...WHERE..." statement for updating current object from table.
// It updates the record if there's already another same record in the table
// (it checks using primary key or unique index).
func (r *User) Update() (result sql.Result, err error) {
return ModelUser().Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update()
}
// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table.
func (r *User) Delete() (result sql.Result, err error) {
return ModelUser().Where(gdb.GetWhereConditionOfStruct(r)).Delete()
}
// Select overwrite the Select method from gdb.Model for model
// as retuning all objects with specified structure.
func (m *UserModel) Select() ([]*User, error) {
array := ([]*User)(nil)
if err := m.Scan(&array); err != nil {
return nil, err
}
return array, nil
}
// First does the same logistics as One method from gdb.Model for model
// as retuning first/one object with specified structure.
func (m *UserModel) First() (*User, error) {
list, err := m.Select()
if err != nil {
return nil, err
}
if len(list) > 0 {
return list[0], nil
}
return nil, nil
}

View File

@ -1,12 +1,10 @@
package main
import (
"encoding/json"
"fmt"
"github.com/gogf/gf/util/gconv"
)
func main() {
b, _ := json.Marshal([]interface{}{1, 2, 3, 4, 5, 123.456, "a"})
fmt.Println(gconv.String(b))
fmt.Println('\f')
fmt.Println(0xA0)
}

View File

@ -370,6 +370,11 @@ func (a *Array) Slice() []interface{} {
}
}
// Interfaces returns current array as []interface{}.
func (a *Array) Interfaces() []interface{} {
return a.Slice()
}
// Clone returns a new array, which is a copy of current array.
func (a *Array) Clone() (newArray *Array) {
a.mu.RLock()

View File

@ -376,6 +376,17 @@ func (a *IntArray) Slice() []int {
return array
}
// Interfaces returns current array as []interface{}.
func (a *IntArray) Interfaces() []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
for k, v := range a.array {
array[k] = v
}
return array
}
// Clone returns a new array, which is a copy of current array.
func (a *IntArray) Clone() (newArray *IntArray) {
a.mu.RLock()

View File

@ -378,6 +378,17 @@ func (a *StrArray) Slice() []string {
return array
}
// Interfaces returns current array as []interface{}.
func (a *StrArray) Interfaces() []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
for k, v := range a.array {
array[k] = v
}
return array
}
// Clone returns a new array, which is a copy of current array.
func (a *StrArray) Clone() (newArray *StrArray) {
a.mu.RLock()

View File

@ -342,6 +342,11 @@ func (a *SortedArray) Slice() []interface{} {
return array
}
// Interfaces returns current array as []interface{}.
func (a *SortedArray) Interfaces() []interface{} {
return a.Slice()
}
// Contains checks whether a value exists in the array.
func (a *SortedArray) Contains(value interface{}) bool {
return a.Search(value) != -1

View File

@ -328,6 +328,17 @@ func (a *SortedIntArray) Slice() []int {
return array
}
// Interfaces returns current array as []interface{}.
func (a *SortedIntArray) Interfaces() []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
for k, v := range a.array {
array[k] = v
}
return array
}
// Contains checks whether a value exists in the array.
func (a *SortedIntArray) Contains(value int) bool {
return a.Search(value) != -1

View File

@ -329,6 +329,17 @@ func (a *SortedStrArray) Slice() []string {
return array
}
// Interfaces returns current array as []interface{}.
func (a *SortedStrArray) Interfaces() []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
array := make([]interface{}, len(a.array))
for k, v := range a.array {
array[k] = v
}
return array
}
// Contains checks whether a value exists in the array.
func (a *SortedStrArray) Contains(value string) bool {
return a.Search(value) != -1

View File

@ -26,6 +26,7 @@ func Test_Array_Basic(t *testing.T) {
array2 := garray.NewArrayFrom(expect)
array3 := garray.NewArrayFrom([]interface{}{})
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.Interfaces(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)

View File

@ -27,6 +27,7 @@ func Test_IntArray_Basic(t *testing.T) {
array := garray.NewIntArrayFrom(expect)
array2 := garray.NewIntArrayFrom(expect2)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.Interfaces(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)

View File

@ -27,6 +27,7 @@ func Test_StrArray_Basic(t *testing.T) {
array2 := garray.NewStrArrayFrom(expect, true)
array3 := garray.NewStrArrayFrom([]string{})
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.Interfaces(), expect)
array.Set(0, "100")
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)

View File

@ -561,6 +561,7 @@ func TestSortedArray_Json(t *testing.T) {
err := json.Unmarshal(b2, &a3)
gtest.Assert(err, nil)
gtest.Assert(a3.Slice(), s1)
gtest.Assert(a3.Interfaces(), s1)
})
gtest.Case(t, func() {

View File

@ -26,6 +26,7 @@ func TestNewSortedIntArrayFrom(t *testing.T) {
array1 := garray.NewSortedIntArrayFrom(a1, true)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
gtest.Assert(array1.Slice(), a1)
gtest.Assert(array1.Interfaces(), a1)
})
}

View File

@ -448,11 +448,13 @@ func TestSortedStrArray_Json(t *testing.T) {
a2 := garray.NewSortedStrArray()
err1 = json.Unmarshal(b2, &a2)
gtest.Assert(a2.Slice(), s2)
gtest.Assert(a2.Interfaces(), s2)
var a3 garray.SortedStrArray
err := json.Unmarshal(b2, &a3)
gtest.Assert(err, nil)
gtest.Assert(a3.Slice(), s1)
gtest.Assert(a3.Interfaces(), s1)
})
gtest.Case(t, func() {

View File

@ -99,9 +99,8 @@ type DB interface {
getChars() (charLeft string, charRight string)
getDebug() bool
quoteWord(s string) string
setSchema(sqlDb *sql.DB, schema string) error
doSetSchema(sqlDb *sql.DB, schema string) error
filterFields(table string, data map[string]interface{}) map[string]interface{}
formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{})
convertValue(fieldValue []byte, fieldType string) interface{}
rowsToResult(rows *sql.Rows) (Result, error)
handleSqlBeforeExec(sql string) string
@ -131,11 +130,12 @@ type dbBase struct {
// 执行的SQL对象
type Sql struct {
Sql string // SQL语句(可能带有预处理占位符)
Args []interface{} // 预处理参数值列表
Error error // 执行结果(nil为成功)
Start int64 // 执行开始时间(毫秒)
End int64 // 执行结束时间(毫秒)
Sql string // SQL语句(可能带有预处理占位符)
Args []interface{} // 预处理参数值列表
Format string // 格式化后的SQL语句仅供参考
Error error // 执行结果(nil为成功)
Start int64 // 执行开始时间(毫秒)
End int64 // 执行结束时间(毫秒)
}
// 表字段结构信息
@ -359,7 +359,7 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
}
// 是否手动选择数据库
if v := bs.schema.Val(); v != "" {
if e := bs.db.setSchema(sqlDb, v); e != nil {
if e := bs.db.doSetSchema(sqlDb, v); e != nil {
err = e
}
}

View File

@ -8,11 +8,9 @@
package gdb
import (
"bytes"
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/container/gmap"
"reflect"
"regexp"
"strings"
@ -69,18 +67,19 @@ func (bs *dbBase) PrintQueriedSqls() {
sqlSlice := bs.GetQueriedSqls()
for k, v := range sqlSlice {
fmt.Println(len(sqlSlice)-k, ":")
fmt.Println(" Sql :", v.Sql)
fmt.Println(" Args :", v.Args)
fmt.Println(" Error:", v.Error)
fmt.Println(" Start:", gtime.NewFromTimeStamp(v.Start).Format("Y-m-d H:i:s.u"))
fmt.Println(" End :", gtime.NewFromTimeStamp(v.End).Format("Y-m-d H:i:s.u"))
fmt.Println(" Cost :", v.End-v.Start, "ms")
fmt.Println(" Sql :", v.Sql)
fmt.Println(" Args :", v.Args)
fmt.Println(" Format :", v.Format)
fmt.Println(" Error :", v.Error)
fmt.Println(" Start :", gtime.NewFromTimeStamp(v.Start).Format("Y-m-d H:i:s.u"))
fmt.Println(" End :", gtime.NewFromTimeStamp(v.End).Format("Y-m-d H:i:s.u"))
fmt.Println(" Cost :", v.End-v.Start, "ms")
}
}
// 打印SQL对象(仅在debug=true时有效)
func (bs *dbBase) printSql(v *Sql) {
s := fmt.Sprintf("[%d ms] %s", v.End-v.Start, bindArgsToQuery(v.Sql, v.Args))
s := fmt.Sprintf("[%d ms] %s", v.End-v.Start, v.Format)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
bs.logger.StackWithFilter(gPATH_FILTER_KEY).Error(s)
@ -107,11 +106,12 @@ func (bs *dbBase) doQuery(link dbLink, query string, args ...interface{}) (rows
rows, err = link.Query(query, args...)
mTime2 := gtime.Millisecond()
s := &Sql{
Sql: query,
Args: args,
Error: err,
Start: mTime1,
End: mTime2,
Sql: query,
Args: args,
Format: bindArgsToQuery(query, args),
Error: err,
Start: mTime1,
End: mTime2,
}
bs.sqls.Put(s)
bs.printSql(s)
@ -144,11 +144,12 @@ func (bs *dbBase) doExec(link dbLink, query string, args ...interface{}) (result
result, err = link.Exec(query, args...)
mTime2 := gtime.Millisecond()
s := &Sql{
Sql: query,
Args: args,
Error: err,
Start: mTime1,
End: mTime2,
Sql: query,
Args: args,
Format: bindArgsToQuery(query, args),
Error: err,
Start: mTime1,
End: mTime2,
}
bs.sqls.Put(s)
bs.printSql(s)
@ -355,7 +356,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
case reflect.Slice, reflect.Array:
return bs.db.doBatchInsert(link, table, data, option, batch...)
case reflect.Map, reflect.Struct:
dataMap = structToMap(data)
dataMap = varToMapDeep(data)
default:
return result, errors.New(fmt.Sprint("unsupported data type:", kind))
}
@ -435,10 +436,10 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
case reflect.Slice, reflect.Array:
listMap = make(List, rv.Len())
for i := 0; i < rv.Len(); i++ {
listMap[i] = structToMap(rv.Index(i).Interface())
listMap[i] = varToMapDeep(rv.Index(i).Interface())
}
case reflect.Map, reflect.Struct:
listMap = List{structToMap(list)}
listMap = List{varToMapDeep(list)}
default:
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
}
@ -522,7 +523,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
// CURD操作:数据更新统一采用sql预处理。
// data参数支持string/map/struct/*struct类型。
func (bs *dbBase) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := bs.db.formatWhere(condition, args)
newWhere, newArgs := formatWhere(bs.db, condition, args, false)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
@ -545,7 +546,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
switch kind {
case reflect.Map, reflect.Struct:
var fields []string
for k, v := range structToMap(data) {
for k, v := range varToMapDeep(data) {
fields = append(fields, bs.db.quoteWord(k)+"=?")
params = append(params, convertParam(v))
}
@ -570,7 +571,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
// CURD操作:删除数据
func (bs *dbBase) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
newWhere, newArgs := bs.db.formatWhere(condition, args)
newWhere, newArgs := formatWhere(bs.db, condition, args, false)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
@ -642,109 +643,6 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
return records, nil
}
// 格式化Where查询条件。
func (bs *dbBase) formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
buffer := bytes.NewBuffer(nil)
rv := reflect.ValueOf(where)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Map:
for key, value := range structToMap(where) {
newArgs = bs.formatWhereKeyValue(buffer, newArgs, key, value)
}
case reflect.Struct:
// ListMap and TreeMap are ordered map,
// which are index-friendly for where conditions.
switch m := where.(type) {
case *gmap.ListMap:
m.Iterator(func(key, value interface{}) bool {
newArgs = bs.formatWhereKeyValue(buffer, newArgs, gconv.String(key), value)
return true
})
case *gmap.TreeMap:
m.Iterator(func(key, value interface{}) bool {
newArgs = bs.formatWhereKeyValue(buffer, newArgs, gconv.String(key), value)
return true
})
default:
for key, value := range structToMap(where) {
newArgs = bs.formatWhereKeyValue(buffer, newArgs, key, value)
}
}
default:
buffer.WriteString(gconv.String(where))
}
if buffer.Len() == 0 {
return "", args
}
newArgs = append(newArgs, args...)
newWhere = buffer.String()
if len(newArgs) > 0 {
// It supports formats like: Where/And/Or("uid", 1) , Where/And/Or("uid>=", 1)
if gstr.Pos(newWhere, "?") == -1 {
if lastOperatorReg.MatchString(newWhere) {
newWhere += "?"
} else if wordReg.MatchString(newWhere) {
newWhere += "=?"
}
}
}
return handlerSliceArguments(newWhere, newArgs)
}
// formatWhereKeyValue handles each key-value pair of the param map.
func (bs *dbBase) formatWhereKeyValue(buffer *bytes.Buffer, newArgs []interface{}, key string, value interface{}) []interface{} {
key = bs.db.quoteWord(key)
if buffer.Len() > 0 {
buffer.WriteString(" AND ")
}
// 支持slice键值/属性,如果只有一个?占位符号那么作为IN查询否则打散作为多个查询参数
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice, reflect.Array:
count := gstr.Count(key, "?")
if count == 0 {
buffer.WriteString(key + " IN(?)")
newArgs = append(newArgs, value)
} else if count != rv.Len() {
buffer.WriteString(key)
newArgs = append(newArgs, value)
} else {
buffer.WriteString(key)
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
newArgs = append(newArgs, gconv.Interfaces(value)...)
}
default:
if value == nil {
buffer.WriteString(key)
} else {
// 支持key带操作符号注意like也算是操作符号
key = gstr.Trim(key)
if gstr.Pos(key, "?") == -1 {
like := " like"
if len(key) > len(like) && gstr.Equal(key[len(key)-len(like):], like) {
buffer.WriteString(key + " ?")
} else if lastOperatorReg.MatchString(key) {
buffer.WriteString(key + " ?")
} else {
buffer.WriteString(key + "=?")
}
} else {
buffer.WriteString(key)
}
newArgs = append(newArgs, value)
}
}
return newArgs
}
// 使用关键字操作符转义给定字符串。
// 如果给定的字符串不为单词,那么不转义,直接返回该字符串。
func (bs *dbBase) quoteWord(s string) string {
@ -756,7 +654,7 @@ func (bs *dbBase) quoteWord(s string) string {
}
// 动态切换数据库
func (bs *dbBase) setSchema(sqlDb *sql.DB, schema string) error {
func (bs *dbBase) doSetSchema(sqlDb *sql.DB, schema string) error {
_, err := sqlDb.Exec("USE " + schema)
return err
}

View File

@ -7,9 +7,11 @@
package gdb
import (
"bytes"
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/internal/empty"
"reflect"
"strings"
"time"
@ -21,11 +23,21 @@ import (
"github.com/gogf/gf/util/gconv"
)
// Type assert api for String().
// Type assert api for String.
type apiString interface {
String() string
}
// Type assert api for Iterator.
type apiIterator interface {
Iterator(f func(key, value interface{}) bool)
}
// Type assert api for Interfaces.
type apiInterfaces interface {
Interfaces() []interface{}
}
const (
ORM_TAG_FOR_STRUCT = "orm"
ORM_TAG_FOR_UNIQUE = "unique"
@ -63,6 +75,173 @@ func formatQuery(query string, args []interface{}) (newQuery string, newArgs []i
return handlerSliceArguments(query, args)
}
// 格式化Where查询条件。
// TODO []interface{} type support for parameter <where> does not completed yet.
func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (newWhere string, newArgs []interface{}) {
buffer := bytes.NewBuffer(nil)
rv := reflect.ValueOf(where)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Array, reflect.Slice:
newArgs = formatWhereInterfaces(db, gconv.Interfaces(where), buffer, newArgs)
case reflect.Map:
for key, value := range varToMapDeep(where) {
if omitEmpty && empty.IsEmpty(value) {
continue
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value)
}
case reflect.Struct:
// If <where> struct implements apiIterator interface,
// it then uses its Iterate function to iterates its key-value pairs.
// For example, ListMap and TreeMap are ordered map,
// which implement apiIterator interface and are index-friendly for where conditions.
if iterator, ok := where.(apiIterator); ok {
iterator.Iterator(func(key, value interface{}) bool {
if omitEmpty && empty.IsEmpty(value) {
return true
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, gconv.String(key), value)
return true
})
break
}
// TODO garray support.
for key, value := range varToMapDeep(where) {
if omitEmpty && empty.IsEmpty(value) {
continue
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value)
}
default:
buffer.WriteString(gconv.String(where))
}
if buffer.Len() == 0 {
return "", args
}
newArgs = append(newArgs, args...)
newWhere = buffer.String()
if len(newArgs) > 0 {
// It supports formats like: Where/And/Or("uid", 1) , Where/And/Or("uid>=", 1)
if gstr.Pos(newWhere, "?") == -1 {
if lastOperatorReg.MatchString(newWhere) {
newWhere += "?"
} else if wordReg.MatchString(newWhere) {
newWhere += "=?"
}
}
}
return handlerSliceArguments(newWhere, newArgs)
}
// formatWhereInterfaces formats <where> as []interface{}.
// TODO []interface{} type support for parameter <where> does not completed yet.
func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, newArgs []interface{}) []interface{} {
var str string
var array []interface{}
var holderCount int
for i := 0; i < len(where); {
if holderCount > 0 {
array = gconv.Interfaces(where[i])
newArgs = append(newArgs, array...)
holderCount -= len(array)
} else {
str = gconv.String(where[i])
holderCount = gstr.Count(str, "?")
buffer.WriteString(str)
}
}
return newArgs
}
// formatWhereKeyValue handles each key-value pair of the parameter map.
func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key string, value interface{}) []interface{} {
key = db.quoteWord(key)
if buffer.Len() > 0 {
buffer.WriteString(" AND ")
}
// 支持slice键值/属性,如果只有一个?占位符号那么作为IN查询否则打散作为多个查询参数
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice, reflect.Array:
count := gstr.Count(key, "?")
if count == 0 {
buffer.WriteString(key + " IN(?)")
newArgs = append(newArgs, value)
} else if count != rv.Len() {
buffer.WriteString(key)
newArgs = append(newArgs, value)
} else {
buffer.WriteString(key)
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
newArgs = append(newArgs, gconv.Interfaces(value)...)
}
default:
if value == nil {
buffer.WriteString(key)
} else {
// 支持key带操作符号注意like也算是操作符号
key = gstr.Trim(key)
if gstr.Pos(key, "?") == -1 {
like := " like"
if len(key) > len(like) && gstr.Equal(key[len(key)-len(like):], like) {
buffer.WriteString(key + " ?")
} else if lastOperatorReg.MatchString(key) {
buffer.WriteString(key + " ?")
} else {
buffer.WriteString(key + "=?")
}
} else {
buffer.WriteString(key)
}
newArgs = append(newArgs, value)
}
}
return newArgs
}
// 将对象转换为map如果对象带有继承对象那么执行递归转换。
// 该方法用于将变量传递给数据库执行之前。
func varToMapDeep(obj interface{}) map[string]interface{} {
data := gconv.Map(obj, ORM_TAG_FOR_STRUCT)
for key, value := range data {
rv := reflect.ValueOf(value)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Struct:
// 底层数据库引擎支持 time.Time/*time.Time 类型
if _, ok := value.(time.Time); ok {
continue
}
if _, ok := value.(*time.Time); ok {
continue
}
// 如果执行String方法那么执行字符串转换
if s, ok := value.(apiString); ok {
data[key] = s.String()
continue
}
delete(data, key)
for k, v := range varToMapDeep(value) {
data[k] = v
}
}
}
return data
}
// 处理预处理占位符与slice类型的参数。
// 需要注意的是,
// 如果是链式操作,在条件参数中也会调用该方法处理查询参数,
@ -164,40 +343,6 @@ func getInsertOperationByOption(option int) string {
return operator
}
// 将对象转换为map如果对象带有继承对象那么执行递归转换。
// 该方法用于将变量传递给数据库执行之前。
func structToMap(obj interface{}) map[string]interface{} {
data := gconv.Map(obj, ORM_TAG_FOR_STRUCT)
for key, value := range data {
rv := reflect.ValueOf(value)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Struct:
// 底层数据库引擎支持 time.Time/*time.Time 类型
if _, ok := value.(time.Time); ok {
continue
}
if _, ok := value.(*time.Time); ok {
continue
}
// 如果执行String方法那么执行字符串转换
if s, ok := value.(apiString); ok {
data[key] = s.String()
continue
}
delete(data, key)
for k, v := range structToMap(value) {
data[k] = v
}
}
}
return data
}
// 将参数绑定到SQL语句中仅用于调试打印。
func bindArgsToQuery(query string, args []interface{}) string {
index := -1
@ -210,6 +355,9 @@ func bindArgsToQuery(query string, args []interface{}) string {
rv := reflect.ValueOf(args[index])
kind := rv.Kind()
if kind == reflect.Ptr {
if rv.IsNil() || !rv.IsValid() {
return "null"
}
rv = rv.Elem()
kind = rv.Kind()
}

View File

@ -24,33 +24,44 @@ import (
// 数据库链式操作模型对象
type Model struct {
db DB // 数据库操作对象
tx *TX // 数据库事务对象
linkType int // 连接对象类型(用于主从集群时开发者自定义操作对象)
tablesInit string // 初始化Model时的表名称(可以是多个)
tables string // 数据库操作表
fields string // 操作字段
where string // 操作条件
whereArgs []interface{} // 操作条件参数
groupBy string // 分组语句
orderBy string // 排序语句
start int // 分页开始
limit int // 分页条数
option int // 操作选项
offset int // 查询偏移量(OFFSET语法)
data interface{} // 操作数据(注意仅支持Map/List/string类型)
batch int // 批量操作条数
filter bool // 是否按照表字段过滤data参数
cacheEnabled bool // 当前SQL操作是否开启查询缓存功能
cacheExpire time.Duration // 查询缓存时间
cacheName string // 查询缓存名称
safe bool // 当前模型是否安全模式(默认非安全表示链式操作直接修改当前模型属性;否则每一次链式操作都是返回新的模型对象)
db DB // 数据库操作对象
tx *TX // 数据库事务对象
linkType int // 连接对象类型(用于主从集群时开发者自定义操作对象)
tablesInit string // 初始化Model时的表名称(可以是多个)
tables string // 数据库操作表
fields string // 操作字段
where string // 操作条件
whereArgs []interface{} // 操作条件参数
whereHolder []*whereHolder // 操作条件预处理
groupBy string // 分组语句
orderBy string // 排序语句
start int // 分页开始
limit int // 分页条数
option int // 操作选项
offset int // 查询偏移量(OFFSET语法)
data interface{} // 操作数据(注意仅支持Map/List/string类型)
batch int // 批量操作条数
filter bool // 是否按照表字段过滤data参数
cacheEnabled bool // 当前SQL操作是否开启查询缓存功能
cacheExpire time.Duration // 查询缓存时间
cacheName string // 查询缓存名称
safe bool // 当前模型是否安全模式(默认非安全表示链式操作直接修改当前模型属性;否则每一次链式操作都是返回新的模型对象)
}
// whereHolder is the holder for where condition preparing.
type whereHolder struct {
operator int // Operator for this holder.
where interface{} // Where parameter.
args []interface{} // Arguments for where parameter.
}
const (
gLINK_TYPE_MASTER = 1
gLINK_TYPE_SLAVE = 2
OPTION_OMITEMPTY = 1 << iota
gLINK_TYPE_MASTER = 1
gLINK_TYPE_SLAVE = 2
gWHERE_HOLDER_WHERE = 1
gWHERE_HOLDER_AND = 2
gWHERE_HOLDER_OR = 3
OPTION_OMITEMPTY = 1 << iota
OPTION_ALLOWEMPTY
)
@ -179,6 +190,11 @@ func (md *Model) Option(option int) *Model {
return model
}
// 链式操作,设置 OPTION_OMITEMPTY 常用选项
func (md *Model) OptionOmitEmpty() *Model {
return md.Option(OPTION_OMITEMPTY)
}
// 链式操作,过滤字段
func (md *Model) Filter() *Model {
model := md.getModel()
@ -186,42 +202,46 @@ func (md *Model) Filter() *Model {
return model
}
// 链式操作condition支持string & gdb.Map.
// 链式操作condition支持string/map/gmap/struct/*struct.
// 注意多个Where调用时会自动转换为And条件调用。
func (md *Model) Where(where interface{}, args ...interface{}) *Model {
model := md.getModel()
if model.where != "" {
return md.And(where, args...)
if model.whereHolder == nil {
model.whereHolder = make([]*whereHolder, 0)
}
newWhere, newArgs := md.db.formatWhere(where, args)
model.where = newWhere
model.whereArgs = newArgs
model.whereHolder = append(model.whereHolder, &whereHolder{
operator: gWHERE_HOLDER_WHERE,
where: where,
args: args,
})
return model
}
// 链式操作添加AND条件到Where中
func (md *Model) And(where interface{}, args ...interface{}) *Model {
model := md.getModel()
newWhere, newArgs := md.db.formatWhere(where, args)
if len(model.where) > 0 && model.where[0] == '(' {
model.where = fmt.Sprintf(`%s AND (%s)`, model.where, newWhere)
} else {
model.where = fmt.Sprintf(`(%s) AND (%s)`, model.where, newWhere)
if model.whereHolder == nil {
model.whereHolder = make([]*whereHolder, 0)
}
model.whereArgs = append(model.whereArgs, newArgs...)
model.whereHolder = append(model.whereHolder, &whereHolder{
operator: gWHERE_HOLDER_AND,
where: where,
args: args,
})
return model
}
// 链式操作添加OR条件到Where中
func (md *Model) Or(where interface{}, args ...interface{}) *Model {
model := md.getModel()
newWhere, newArgs := md.db.formatWhere(where, args)
if len(model.where) > 0 && model.where[0] == '(' {
model.where = fmt.Sprintf(`%s OR (%s)`, model.where, newWhere)
} else {
model.where = fmt.Sprintf(`(%s) OR (%s)`, model.where, newWhere)
if model.whereHolder == nil {
model.whereHolder = make([]*whereHolder, 0)
}
model.whereArgs = append(model.whereArgs, newArgs...)
model.whereHolder = append(model.whereHolder, &whereHolder{
operator: gWHERE_HOLDER_OR,
where: where,
args: args,
})
return model
}
@ -334,11 +354,11 @@ func (md *Model) Data(data ...interface{}) *Model {
case reflect.Slice, reflect.Array:
list := make(List, rv.Len())
for i := 0; i < rv.Len(); i++ {
list[i] = structToMap(rv.Index(i).Interface())
list[i] = varToMapDeep(rv.Index(i).Interface())
}
model.data = list
case reflect.Map, reflect.Struct:
model.data = structToMap(data[0])
model.data = varToMapDeep(data[0])
default:
model.data = data[0]
}
@ -367,11 +387,13 @@ func (md *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
if md.filter {
data = md.db.filterFields(md.tables, data)
}
// Remove key-value pairs of which the value is empty.
if allowOmitEmpty && md.option&OPTION_OMITEMPTY > 0 {
m := gmap.NewStrAnyMapFrom(data)
m.FilterEmpty()
data = m.Map()
}
// Keep specified fields.
if len(md.fields) > 0 && md.fields != "*" {
set := gset.NewStrSet()
for _, v := range gstr.SplitAndTrimSpace(md.fields, ",") {
@ -671,6 +693,44 @@ func (md *Model) checkAndRemoveCache() {
// 格式化当前输入参数返回SQL条件语句不带参数
func (md *Model) getConditionSql() string {
if len(md.whereHolder) > 0 {
for _, v := range md.whereHolder {
switch v.operator {
case gWHERE_HOLDER_WHERE:
if md.where == "" {
newWhere, newArgs := formatWhere(md.db, v.where, v.args, md.option&OPTION_OMITEMPTY > 0)
if len(newWhere) > 0 {
md.where = newWhere
md.whereArgs = newArgs
}
continue
}
fallthrough
case gWHERE_HOLDER_AND:
newWhere, newArgs := formatWhere(md.db, v.where, v.args, md.option&OPTION_OMITEMPTY > 0)
if len(newWhere) > 0 {
if md.where[0] == '(' {
md.where = fmt.Sprintf(`%s AND (%s)`, md.where, newWhere)
} else {
md.where = fmt.Sprintf(`(%s) AND (%s)`, md.where, newWhere)
}
md.whereArgs = append(md.whereArgs, newArgs...)
}
case gWHERE_HOLDER_OR:
newWhere, newArgs := formatWhere(md.db, v.where, v.args, md.option&OPTION_OMITEMPTY > 0)
if len(newWhere) > 0 {
if md.where[0] == '(' {
md.where = fmt.Sprintf(`%s OR (%s)`, md.where, newWhere)
} else {
md.where = fmt.Sprintf(`(%s) OR (%s)`, md.where, newWhere)
}
md.whereArgs = append(md.whereArgs, newArgs...)
}
}
}
}
s := ""
if md.where != "" {
s += " WHERE " + md.where

View File

@ -218,7 +218,7 @@ func (db *dbOracle) doInsert(link dbLink, table string, data interface{}, option
case reflect.Map:
fallthrough
case reflect.Struct:
dataMap = structToMap(data)
dataMap = varToMapDeep(data)
default:
return result, errors.New(fmt.Sprint("unsupported data type:", kind))
}
@ -330,12 +330,12 @@ func (db *dbOracle) doBatchInsert(link dbLink, table string, list interface{}, o
case reflect.Array:
listMap = make(List, rv.Len())
for i := 0; i < rv.Len(); i++ {
listMap[i] = structToMap(rv.Index(i).Interface())
listMap[i] = varToMapDeep(rv.Index(i).Interface())
}
case reflect.Map:
fallthrough
case reflect.Struct:
listMap = List{Map(structToMap(list))}
listMap = List{Map(varToMapDeep(list))}
default:
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
}

View File

@ -165,7 +165,7 @@ func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Resul
// CURD操作:数据更新统一采用sql预处理,
// data参数支持字符串或者关联数组类型内部会自行做判断处理.
func (tx *TX) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := tx.db.formatWhere(condition, args)
newWhere, newArgs := formatWhere(tx.db, condition, args, false)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
@ -179,7 +179,7 @@ func (tx *TX) doUpdate(table string, data interface{}, condition string, args ..
// CURD操作:删除数据
func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := tx.db.formatWhere(condition, args)
newWhere, newArgs := formatWhere(tx.db, condition, args, false)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}

View File

@ -1052,15 +1052,15 @@ func Test_Model_Option_Map(t *testing.T) {
_, err = db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{"nickname": ""}).Where("id", 2).Update()
gtest.AssertNE(err, nil)
r, err = db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update()
r, err = db.Table(table).OptionOmitEmpty().Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update()
gtest.Assert(err, nil)
n, _ = r.RowsAffected()
gtest.Assert(n, 1)
_, err = db.Table(table).Option(gdb.OPTION_OMITEMPTY).Fields("nickname").Data(g.Map{"nickname": "", "password": "123"}).Where("id", 4).Update()
_, err = db.Table(table).OptionOmitEmpty().Fields("nickname").Data(g.Map{"nickname": "", "password": "123"}).Where("id", 4).Update()
gtest.AssertNE(err, nil)
r, err = db.Table(table).Option(gdb.OPTION_OMITEMPTY).
r, err = db.Table(table).OptionOmitEmpty().
Fields("password").Data(g.Map{
"nickname": "",
"passport": "123",
@ -1080,75 +1080,95 @@ func Test_Model_Option_Map(t *testing.T) {
func Test_Model_Option_List(t *testing.T) {
gtest.Case(t, func() {
gtest.Case(t, func() {
table := createTable()
defer dropTable(table)
r, err := db.Table(table).Fields("id, password").Data(g.List{
g.Map{
"id": 1,
"passport": "1",
"password": "1",
"nickname": "1",
},
g.Map{
"id": 2,
"passport": "2",
"password": "2",
"nickname": "2",
},
}).Save()
gtest.Assert(err, nil)
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
list, err := db.Table(table).OrderBy("id asc").All()
gtest.Assert(err, nil)
gtest.Assert(len(list), 2)
gtest.Assert(list[0]["id"].String(), "1")
gtest.Assert(list[0]["nickname"].String(), "")
gtest.Assert(list[0]["passport"].String(), "")
gtest.Assert(list[0]["password"].String(), "1")
table := createTable()
defer dropTable(table)
r, err := db.Table(table).Fields("id, password").Data(g.List{
g.Map{
"id": 1,
"passport": "1",
"password": "1",
"nickname": "1",
},
g.Map{
"id": 2,
"passport": "2",
"password": "2",
"nickname": "2",
},
}).Save()
gtest.Assert(err, nil)
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
list, err := db.Table(table).OrderBy("id asc").All()
gtest.Assert(err, nil)
gtest.Assert(len(list), 2)
gtest.Assert(list[0]["id"].String(), "1")
gtest.Assert(list[0]["nickname"].String(), "")
gtest.Assert(list[0]["passport"].String(), "")
gtest.Assert(list[0]["password"].String(), "1")
gtest.Assert(list[1]["id"].String(), "2")
gtest.Assert(list[1]["nickname"].String(), "")
gtest.Assert(list[1]["passport"].String(), "")
gtest.Assert(list[1]["password"].String(), "2")
})
gtest.Assert(list[1]["id"].String(), "2")
gtest.Assert(list[1]["nickname"].String(), "")
gtest.Assert(list[1]["passport"].String(), "")
gtest.Assert(list[1]["password"].String(), "2")
})
gtest.Case(t, func() {
gtest.Case(t, func() {
table := createTable()
defer dropTable(table)
r, err := db.Table(table).Option(gdb.OPTION_OMITEMPTY).Fields("id, password").Data(g.List{
g.Map{
"id": 1,
"passport": "1",
"password": 0,
"nickname": "1",
},
g.Map{
"id": 2,
"passport": "2",
"password": "2",
"nickname": "2",
},
}).Save()
gtest.Assert(err, nil)
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
list, err := db.Table(table).OrderBy("id asc").All()
g.Dump(list)
gtest.Assert(err, nil)
gtest.Assert(len(list), 2)
gtest.Assert(list[0]["id"].String(), "1")
gtest.Assert(list[0]["nickname"].String(), "")
gtest.Assert(list[0]["passport"].String(), "")
gtest.Assert(list[0]["password"].String(), "0")
table := createTable()
defer dropTable(table)
r, err := db.Table(table).OptionOmitEmpty().Fields("id, password").Data(g.List{
g.Map{
"id": 1,
"passport": "1",
"password": 0,
"nickname": "1",
},
g.Map{
"id": 2,
"passport": "2",
"password": "2",
"nickname": "2",
},
}).Save()
gtest.Assert(err, nil)
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
list, err := db.Table(table).OrderBy("id asc").All()
g.Dump(list)
gtest.Assert(err, nil)
gtest.Assert(len(list), 2)
gtest.Assert(list[0]["id"].String(), "1")
gtest.Assert(list[0]["nickname"].String(), "")
gtest.Assert(list[0]["passport"].String(), "")
gtest.Assert(list[0]["password"].String(), "0")
gtest.Assert(list[1]["id"].String(), "2")
gtest.Assert(list[1]["nickname"].String(), "")
gtest.Assert(list[1]["passport"].String(), "")
gtest.Assert(list[1]["password"].String(), "2")
gtest.Assert(list[1]["id"].String(), "2")
gtest.Assert(list[1]["nickname"].String(), "")
gtest.Assert(list[1]["passport"].String(), "")
gtest.Assert(list[1]["password"].String(), "2")
})
})
}
func Test_Model_Option_Where(t *testing.T) {
gtest.Case(t, func() {
table := createInitTable()
defer dropTable(table)
r, err := db.Table(table).OptionOmitEmpty().Data("nickname", 1).Where(g.Map{"id": 0, "passport": ""}).Update()
gtest.Assert(err, nil)
n, _ := r.RowsAffected()
gtest.Assert(n, INIT_DATA_SIZE)
})
gtest.Case(t, func() {
table := createInitTable()
defer dropTable(table)
r, err := db.Table(table).OptionOmitEmpty().Data("nickname", 1).Where(g.Map{"id": 1, "passport": ""}).Update()
gtest.Assert(err, nil)
n, _ := r.RowsAffected()
gtest.Assert(n, 1)
v, err := db.Table(table).Where("id", 1).Fields("nickname").Value()
gtest.Assert(err, nil)
gtest.Assert(v.String(), "1")
})
}

View File

@ -100,6 +100,9 @@ func (t *Time) Millisecond() int64 {
// String returns current time object as string.
func (t *Time) String() string {
if t == nil {
return ""
}
return t.Format("Y-m-d H:i:s")
}
@ -200,6 +203,10 @@ func (t *Time) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (t *Time) UnmarshalJSON(b []byte) error {
if len(b) == 0 {
t.Time = time.Time{}
return nil
}
newTime, err := StrToTime(string(bytes.Trim(b, `"`)))
if err != nil {
return err

View File

@ -443,13 +443,14 @@ func Split(str, delimiter string) []string {
}
// SplitAndTrim splits string <str> by a string <delimiter> to an array,
// and calls Trim to every element of this array.
func SplitAndTrim(str, delimiter, cut string) []string {
// and calls Trim to every element of this array. It ignores the elements
// which are empty after Trim.
func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
array := strings.Split(str, delimiter)
for k, v := range array {
v = strings.Trim(v, cut)
v = Trim(v, characterMask...)
if v != "" {
array[k] = strings.Trim(v, cut)
array[k] = v
}
}
return array
@ -457,6 +458,7 @@ func SplitAndTrim(str, delimiter, cut string) []string {
// SplitAndTrimSpace splits string <str> by a string <delimiter> to an array,
// and calls TrimSpace to every element of this array.
// Deprecated.
func SplitAndTrimSpace(str, delimiter string) []string {
array := strings.Split(str, delimiter)
for k, v := range array {

View File

@ -6,29 +6,52 @@
package gstr
import "strings"
import (
"strings"
)
var (
// defaultTrimChars are the characters which are stripped by Trim* functions in default.
defaultTrimChars = string([]byte{
'\t', // Tab.
'\v', // Vertical tab.
'\n', // New line (line feed).
'\r', // Carriage return.
'\f', // New page.
' ', // Ordinary space.
0x00, // NUL-byte.
0x85, // Delete.
0xA0, // Non-breaking space.
})
)
// Trim strips whitespace (or other characters) from the beginning and end of a string.
// The optional parameter <characterMask> specifies the additional stripped characters.
func Trim(str string, characterMask ...string) string {
if len(characterMask) > 0 {
return strings.Trim(str, characterMask[0])
if len(characterMask) == 0 {
return strings.Trim(str, defaultTrimChars)
} else {
return strings.TrimSpace(str)
return strings.Trim(str, defaultTrimChars+characterMask[0])
}
}
// TrimStr strips all of the given <cut> string from the beginning and end of a string.
// Note that it does not strips the whitespaces of its beginning or end.
func TrimStr(str string, cut string) string {
return TrimLeftStr(TrimRightStr(str, cut), cut)
}
// TrimLeft strips whitespace (or other characters) from the beginning of a string.
func TrimLeft(str string, characterMask ...string) string {
mask := ""
if len(characterMask) == 0 {
mask = string([]byte{'\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0})
return strings.TrimLeft(str, defaultTrimChars)
} else {
mask = characterMask[0]
return strings.TrimLeft(str, defaultTrimChars+characterMask[0])
}
return strings.TrimLeft(str, mask)
}
// TrimLeftStr strips all of the given <cut> string from the beginning of a string.
// Note that it does not strips the whitespaces of its beginning.
func TrimLeftStr(str string, cut string) string {
for str[0:len(cut)] == cut {
str = str[len(cut):]
@ -38,19 +61,19 @@ func TrimLeftStr(str string, cut string) string {
// TrimRight strips whitespace (or other characters) from the end of a string.
func TrimRight(str string, characterMask ...string) string {
mask := ""
if len(characterMask) == 0 {
mask = string([]byte{'\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0})
return strings.TrimRight(str, defaultTrimChars)
} else {
mask = characterMask[0]
return strings.TrimRight(str, defaultTrimChars+characterMask[0])
}
return strings.TrimRight(str, mask)
}
// TrimRightStr strips all of the given <cut> string from the end of a string.
// Note that it does not strips the whitespaces of its end.
func TrimRightStr(str string, cut string) string {
var length int
for {
length := len(str)
length = len(str)
if str[length-len(cut):length] == cut {
str = str[:length-len(cut)]
} else {

View File

@ -22,6 +22,15 @@ func Test_Trim(t *testing.T) {
})
}
func Test_TrimStr(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gstr.TrimStr("gogo我爱gogo", "go"), "我爱")
})
gtest.Case(t, func() {
gtest.Assert(gstr.TrimStr("啊我爱中国人啊", "啊"), "我爱中国人")
})
}
func Test_TrimRight(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gstr.TrimRight(" 123456\n "), " 123456")
@ -33,6 +42,9 @@ func Test_TrimRightStr(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gstr.TrimRightStr("gogo我爱gogo", "go"), "gogo我爱")
})
gtest.Case(t, func() {
gtest.Assert(gstr.TrimRightStr("我爱中国人", "人"), "我爱中国")
})
}
func Test_TrimLeft(t *testing.T) {
@ -46,4 +58,7 @@ func Test_TrimLeftStr(t *testing.T) {
gtest.Case(t, func() {
gtest.Assert(gstr.TrimLeftStr("gogo我爱gogo", "go"), "我爱gogo")
})
gtest.Case(t, func() {
gtest.Assert(gstr.TrimLeftStr("我爱中国人", "我爱"), "中国人")
})
}

View File

@ -67,71 +67,86 @@ func Ints(i interface{}) []int {
if r, ok := i.([]int); ok {
return r
} else {
array := make([]int, 0)
var array []int
switch value := i.(type) {
case []string:
for _, v := range value {
array = append(array, Int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = Int(v)
}
case []int8:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []int16:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []int32:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []int64:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []uint:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []uint8:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []uint16:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []uint32:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []uint64:
for _, v := range value {
array = append(array, int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = int(v)
}
case []bool:
for _, v := range value {
array = make([]int, len(value))
for k, v := range value {
if v {
array = append(array, 1)
array[k] = 1
} else {
array = append(array, 0)
array[k] = 0
}
}
case []float32:
for _, v := range value {
array = append(array, Int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = Int(v)
}
case []float64:
for _, v := range value {
array = append(array, Int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = Int(v)
}
case []interface{}:
for _, v := range value {
array = append(array, Int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = Int(v)
}
case [][]byte:
for _, v := range value {
array = append(array, Int(v))
array = make([]int, len(value))
for k, v := range value {
array[k] = Int(v)
}
default:
return []int{Int(i)}
@ -148,67 +163,81 @@ func Uints(i interface{}) []uint {
if r, ok := i.([]uint); ok {
return r
} else {
array := make([]uint, 0)
var array []uint
switch value := i.(type) {
case []string:
for _, v := range value {
array = append(array, Uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = Uint(v)
}
case []int8:
for _, v := range value {
array = append(array, uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = uint(v)
}
case []int16:
for _, v := range value {
array = append(array, uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = uint(v)
}
case []int32:
for _, v := range value {
array = append(array, uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = uint(v)
}
case []int64:
for _, v := range value {
array = append(array, uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = uint(v)
}
case []uint8:
for _, v := range value {
array = append(array, uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = uint(v)
}
case []uint16:
for _, v := range value {
array = append(array, uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = uint(v)
}
case []uint32:
for _, v := range value {
array = append(array, uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = uint(v)
}
case []uint64:
for _, v := range value {
array = append(array, uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = uint(v)
}
case []bool:
for _, v := range value {
array = make([]uint, len(value))
for k, v := range value {
if v {
array = append(array, 1)
array[k] = 1
} else {
array = append(array, 0)
array[k] = 0
}
}
case []float32:
for _, v := range value {
array = append(array, Uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = Uint(v)
}
case []float64:
for _, v := range value {
array = append(array, Uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = Uint(v)
}
case []interface{}:
for _, v := range value {
array = append(array, Uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = Uint(v)
}
case [][]byte:
for _, v := range value {
array = append(array, Uint(v))
array = make([]uint, len(value))
for k, v := range value {
array[k] = Uint(v)
}
default:
return []uint{Uint(i)}
@ -225,67 +254,82 @@ func Strings(i interface{}) []string {
if r, ok := i.([]string); ok {
return r
} else {
array := make([]string, 0)
var array []string
switch value := i.(type) {
case []int:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []int8:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []int16:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []int32:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []int64:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []uint:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []uint8:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []uint16:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []uint32:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []uint64:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []bool:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []float32:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []float64:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case []interface{}:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
case [][]byte:
for _, v := range value {
array = append(array, String(v))
array = make([]string, len(value))
for k, v := range value {
array[k] = String(v)
}
default:
return []string{String(i)}
@ -302,63 +346,76 @@ func Floats(i interface{}) []float64 {
if r, ok := i.([]float64); ok {
return r
} else {
array := make([]float64, 0)
var array []float64
switch value := i.(type) {
case []string:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []int:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []int8:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []int16:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []int32:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []int64:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []uint:
for _, v := range value {
array = append(array, Float64(v))
}
case []uint8:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []uint16:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []uint32:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []uint64:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []bool:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []float32:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
case []interface{}:
for _, v := range value {
array = append(array, Float64(v))
array = make([]float64, len(value))
for k, v := range value {
array[k] = Float64(v)
}
default:
return []float64{Float64(i)}
@ -367,6 +424,11 @@ func Floats(i interface{}) []float64 {
}
}
// Type assert api for Interfaces.
type apiInterfaces interface {
Interfaces() []interface{}
}
// Interfaces converts <i> to []interface{}.
func Interfaces(i interface{}) []interface{} {
if i == nil {
@ -374,64 +436,79 @@ func Interfaces(i interface{}) []interface{} {
}
if r, ok := i.([]interface{}); ok {
return r
} else if r, ok := i.(apiInterfaces); ok {
return r.Interfaces()
} else {
array := make([]interface{}, 0)
var array []interface{}
switch value := i.(type) {
case []string:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []int:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []int8:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []int16:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []int32:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []int64:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []uint:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []uint8:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []uint16:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []uint32:
for _, v := range value {
array = append(array, v)
}
case []uint64:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []bool:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []float32:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
case []float64:
for _, v := range value {
array = append(array, v)
array = make([]interface{}, len(value))
for k, v := range value {
array[k] = v
}
default:
// Finally we use reflection.
@ -443,17 +520,19 @@ func Interfaces(i interface{}) []interface{} {
}
switch kind {
case reflect.Slice, reflect.Array:
array = make([]interface{}, rv.Len())
for i := 0; i < rv.Len(); i++ {
array = append(array, rv.Index(i).Interface())
array[i] = rv.Index(i).Interface()
}
case reflect.Struct:
rt := rv.Type()
array = make([]interface{}, rv.NumField())
for i := 0; i < rv.NumField(); i++ {
// Only public attributes.
if !utilstr.IsLetterUpper(rt.Field(i).Name[0]) {
continue
}
array = append(array, rv.Field(i).Interface())
array[i] = rv.Field(i).Interface()
}
default:
return []interface{}{i}