mirror of
https://gitee.com/johng/gf
synced 2026-06-09 11:03:59 +08:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 58b2efc900 | |||
| 28326606f5 | |||
| 91c98bbb60 | |||
| c0236d7dfa | |||
| d4051df5b6 | |||
| 88045417ff | |||
| 500efb5601 | |||
| 97fe8235da | |||
| e1164e935b | |||
| b26330aee1 | |||
| 2b083709b5 | |||
| 0ac45dc379 | |||
| 650916c22a | |||
| 2804183325 |
@ -1,5 +1,6 @@
|
||||
|
||||
# MySQL数据库配置
|
||||
[database]
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
|
||||
|
||||
24
.example/database/gdb/mysql/gdb_complecated.go
Normal file
24
.example/database/gdb/mysql/gdb_complecated.go
Normal 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)
|
||||
|
||||
}
|
||||
@ -7,7 +7,7 @@ import (
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
//db.SetDebug(true)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
|
||||
34
.example/database/gdb/mysql/issue364.go
Normal file
34
.example/database/gdb/mysql/issue364.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"time"
|
||||
)
|
||||
|
||||
func test1() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
time.Sleep(1 * time.Minute)
|
||||
r, e := db.Table("test").Where("id", 10000).Count()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
g.Dump(r)
|
||||
}
|
||||
|
||||
func test2() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
dao := db.Table("test").Safe()
|
||||
time.Sleep(1 * time.Minute)
|
||||
r, e := dao.Where("id", 10000).Count()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
g.Dump(r)
|
||||
}
|
||||
|
||||
func main() {
|
||||
test1()
|
||||
test2()
|
||||
}
|
||||
60
.example/frame/mvc/app/model/defaults/user.go
Normal file
60
.example/frame/mvc/app/model/defaults/user.go
Normal 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()
|
||||
}
|
||||
@ -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"
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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
|
||||
)
|
||||
@ -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
|
||||
}
|
||||
@ -9,7 +9,7 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetSessionMaxAge(2 * time.Second)
|
||||
s.SetSessionMaxAge(61 * time.Second)
|
||||
s.BindHandler("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Second())
|
||||
r.Response.Write("ok")
|
||||
|
||||
11
.example/os/glog/glog_context.go
Normal file
11
.example/os/glog/glog_context.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := context.WithValue(context.Background(), "key", "value")
|
||||
fmt.Printf("%v", c)
|
||||
}
|
||||
@ -1,12 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
b, _ := json.Marshal([]interface{}{1, 2, 3, 4, 5, 123.456, "a"})
|
||||
fmt.Println(gconv.String(b))
|
||||
func loadRouter(domain *ghttp.Domain) {
|
||||
domain.Group("/", func(g *ghttp.RouterGroup) {
|
||||
g.Group("/app", func(gApp *ghttp.RouterGroup) {
|
||||
// 该路由规则仅会在GET请求下有效
|
||||
gApp.GET("/{table}/list/{page}.html", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Router)
|
||||
})
|
||||
// 该路由规则仅会在GET请求及localhost域名下有效
|
||||
gApp.GET("/order/info/{order_id}", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Router)
|
||||
})
|
||||
// 该路由规则仅会在DELETE请求下有效
|
||||
gApp.DELETE("/comment/{id}", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Router)
|
||||
})
|
||||
})
|
||||
// 该路由规则仅会在GET请求下有效
|
||||
g.GET("/{table}/list/{page}.html", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Router)
|
||||
})
|
||||
// 该路由规则仅会在GET请求及localhost域名下有效
|
||||
g.GET("/order/info/{order_id}", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Router)
|
||||
})
|
||||
// 该路由规则仅会在DELETE请求下有效
|
||||
g.DELETE("/comment/{id}", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Router)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
|
||||
domain := s.Domain("localhost")
|
||||
loadRouter(domain)
|
||||
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -141,6 +141,41 @@ func (m *AnyAnyMap) Get(key interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *AnyAnyMap) Pop() (key, value interface{}) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes <size> items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[interface{}]interface{}, size)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
@ -321,10 +356,7 @@ func (m *AnyAnyMap) Size() int {
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *AnyAnyMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
|
||||
@ -141,6 +141,41 @@ func (m *IntAnyMap) Get(key int) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *IntAnyMap) Pop() (key int, value interface{}) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes <size> items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *IntAnyMap) Pops(size int) map[int]interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]interface{}, size)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
@ -321,10 +356,7 @@ func (m *IntAnyMap) Size() int {
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntAnyMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
|
||||
@ -139,6 +139,41 @@ func (m *IntIntMap) Get(key int) int {
|
||||
return val
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *IntIntMap) Pop() (key, value int) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes <size> items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *IntIntMap) Pops(size int) map[int]int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]int, size)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
@ -298,10 +333,7 @@ func (m *IntIntMap) Size() int {
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntIntMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
|
||||
@ -139,6 +139,41 @@ func (m *IntStrMap) Get(key int) string {
|
||||
return val
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *IntStrMap) Pop() (key int, value string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes <size> items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *IntStrMap) Pops(size int) map[int]string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]string, size)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
@ -298,10 +333,7 @@ func (m *IntStrMap) Size() int {
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *IntStrMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
|
||||
@ -135,6 +135,41 @@ func (m *StrAnyMap) Get(key string) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *StrAnyMap) Pop() (key string, value interface{}) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes <size> items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *StrAnyMap) Pops(size int) map[string]interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]interface{}, size)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
@ -317,10 +352,7 @@ func (m *StrAnyMap) Size() int {
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrAnyMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
|
||||
@ -139,6 +139,41 @@ func (m *StrIntMap) Get(key string) int {
|
||||
return val
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *StrIntMap) Pop() (key string, value int) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes <size> items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *StrIntMap) Pops(size int) map[string]int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]int, size)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
@ -300,10 +335,7 @@ func (m *StrIntMap) Size() int {
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrIntMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
|
||||
@ -139,6 +139,41 @@ func (m *StrStrMap) Get(key string) string {
|
||||
return val
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *StrStrMap) Pop() (key, value string) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for key, value = range m.data {
|
||||
delete(m.data, key)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes <size> items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *StrStrMap) Pops(size int) map[string]string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]string, size)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
@ -300,10 +335,7 @@ func (m *StrStrMap) Size() int {
|
||||
// IsEmpty checks whether the map is empty.
|
||||
// It returns true if map is empty, or else false.
|
||||
func (m *StrStrMap) IsEmpty() bool {
|
||||
m.mu.RLock()
|
||||
empty := len(m.data) == 0
|
||||
m.mu.RUnlock()
|
||||
return empty
|
||||
return m.Size() == 0
|
||||
}
|
||||
|
||||
// Clear deletes all data of the map, it will remake a new underlying data map.
|
||||
|
||||
@ -189,6 +189,45 @@ func (m *ListMap) Get(key interface{}) (value interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
func (m *ListMap) Pop() (key, value interface{}) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, e := range m.data {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
delete(m.data, k)
|
||||
m.list.Remove(e)
|
||||
return k, value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pops retrieves and deletes <size> items from the map.
|
||||
// It returns all items if size == -1.
|
||||
func (m *ListMap) Pops(size int) map[interface{}]interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if size > len(m.data) || size == -1 {
|
||||
size = len(m.data)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[interface{}]interface{}, size)
|
||||
for k, e := range m.data {
|
||||
value := e.Value.(*gListMapNode).value
|
||||
delete(m.data, k)
|
||||
m.list.Remove(e)
|
||||
newMap[k] = value
|
||||
index++
|
||||
if index == size {
|
||||
break
|
||||
}
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
|
||||
// if not exists, set value to the map with given <key>,
|
||||
// or else just return the existing value.
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
@ -221,3 +222,56 @@ func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
@ -203,3 +204,56 @@ func Test_IntAnyMap_Json(t *testing.T) {
|
||||
gtest.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{1, 2})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{1, 2})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
@ -206,3 +207,56 @@ func Test_IntIntMap_Json(t *testing.T) {
|
||||
gtest.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntIntMapFrom(g.MapIntInt{
|
||||
1: 11,
|
||||
2: 22,
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{1, 2})
|
||||
gtest.AssertIN(v1, g.Slice{11, 22})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{1, 2})
|
||||
gtest.AssertIN(v2, g.Slice{11, 22})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntIntMapFrom(g.MapIntInt{
|
||||
1: 11,
|
||||
2: 22,
|
||||
3: 33,
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{11, 22, 33})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{11, 22, 33})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
@ -207,3 +208,56 @@ func Test_IntStrMap_Json(t *testing.T) {
|
||||
gtest.Assert(m.Get(2), data[2])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{1, 2})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{1, 2})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{1, 2, 3})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -177,3 +178,56 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewListMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewListMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
@ -215,3 +216,56 @@ func Test_StrAnyMap_Json(t *testing.T) {
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
@ -218,3 +219,56 @@ func Test_StrIntMap_Json(t *testing.T) {
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrIntMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrIntMapFrom(g.MapStrInt{
|
||||
"k1": 11,
|
||||
"k2": 22,
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v1, g.Slice{11, 22})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v2, g.Slice{11, 22})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrIntMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrIntMapFrom(g.MapStrInt{
|
||||
"k1": 11,
|
||||
"k2": 22,
|
||||
"k3": 33,
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{11, 22, 33})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{11, 22, 33})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
@ -215,3 +216,56 @@ func Test_StrStrMap_Json(t *testing.T) {
|
||||
gtest.Assert(m.Get("k2"), data["k2"])
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrStrMap_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrStrMapFrom(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
gtest.Assert(m.Size(), 2)
|
||||
|
||||
k1, v1 := m.Pop()
|
||||
gtest.AssertIN(k1, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v1, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 1)
|
||||
k2, v2 := m.Pop()
|
||||
gtest.AssertIN(k2, g.Slice{"k1", "k2"})
|
||||
gtest.AssertIN(v2, g.Slice{"v1", "v2"})
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.AssertNE(k1, k2)
|
||||
gtest.AssertNE(v1, v2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrStrMap_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
m := gmap.NewStrStrMapFrom(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
gtest.Assert(m.Size(), 3)
|
||||
|
||||
kArray := garray.New()
|
||||
vArray := garray.New()
|
||||
for k, v := range m.Pops(1) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 2)
|
||||
for k, v := range m.Pops(2) {
|
||||
gtest.AssertIN(k, g.Slice{"k1", "k2", "k3"})
|
||||
gtest.AssertIN(v, g.Slice{"v1", "v2", "v3"})
|
||||
kArray.Append(k)
|
||||
vArray.Append(v)
|
||||
}
|
||||
gtest.Assert(m.Size(), 0)
|
||||
|
||||
gtest.Assert(kArray.Unique().Len(), 3)
|
||||
gtest.Assert(vArray.Unique().Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -373,24 +373,30 @@ func (set *Set) Sum() (sum int) {
|
||||
|
||||
// Pops randomly pops an item from set.
|
||||
func (set *Set) Pop() interface{} {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for k, _ := range set.data {
|
||||
delete(set.data, k)
|
||||
return k
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pops randomly pops <size> items from set.
|
||||
// It returns all items if size == -1.
|
||||
func (set *Set) Pops(size int) []interface{} {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if size > len(set.data) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if size > len(set.data) || size == -1 {
|
||||
size = len(set.data)
|
||||
}
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
array := make([]interface{}, size)
|
||||
for k, _ := range set.data {
|
||||
delete(set.data, k)
|
||||
array[index] = k
|
||||
index++
|
||||
if index == size {
|
||||
|
||||
@ -346,24 +346,30 @@ func (set *IntSet) Sum() (sum int) {
|
||||
|
||||
// Pops randomly pops an item from set.
|
||||
func (set *IntSet) Pop() int {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for k, _ := range set.data {
|
||||
delete(set.data, k)
|
||||
return k
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Pops randomly pops <size> items from set.
|
||||
// It returns all items if size == -1.
|
||||
func (set *IntSet) Pops(size int) []int {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if size > len(set.data) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if size > len(set.data) || size == -1 {
|
||||
size = len(set.data)
|
||||
}
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
array := make([]int, size)
|
||||
for k, _ := range set.data {
|
||||
delete(set.data, k)
|
||||
array[index] = k
|
||||
index++
|
||||
if index == size {
|
||||
|
||||
@ -360,24 +360,30 @@ func (set *StrSet) Sum() (sum int) {
|
||||
|
||||
// Pops randomly pops an item from set.
|
||||
func (set *StrSet) Pop() string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
for k, _ := range set.data {
|
||||
delete(set.data, k)
|
||||
return k
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Pops randomly pops <size> items from set.
|
||||
// It returns all items if size == -1.
|
||||
func (set *StrSet) Pops(size int) []string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if size > len(set.data) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if size > len(set.data) || size == -1 {
|
||||
size = len(set.data)
|
||||
}
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
array := make([]string, size)
|
||||
for k, _ := range set.data {
|
||||
delete(set.data, k)
|
||||
array[index] = k
|
||||
index++
|
||||
if index == size {
|
||||
|
||||
@ -261,19 +261,35 @@ func TestSet_Sum(t *testing.T) {
|
||||
|
||||
func TestSet_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.New(true)
|
||||
s1.Add(1).Add(2).Add(3).Add(4)
|
||||
gtest.AssertIN(s1.Pop(), []int{1, 2, 3, 4})
|
||||
s := gset.New(true)
|
||||
s.Add(1).Add(2).Add(3).Add(4)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.AssertIN(s.Pop(), []int{1, 2, 3, 4})
|
||||
gtest.Assert(s.Size(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.New(true)
|
||||
s1.Add(1).Add(2).Add(3).Add(4)
|
||||
gtest.AssertIN(s1.Pops(1), []int{1, 2, 3, 4})
|
||||
gtest.AssertIN(s1.Pops(6), []int{1, 2, 3, 4})
|
||||
gtest.Assert(len(s1.Pops(2)), 2)
|
||||
s := gset.New(true)
|
||||
s.Add(1).Add(2).Add(3).Add(4)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.Assert(s.Pops(0), nil)
|
||||
gtest.AssertIN(s.Pops(1), []int{1, 2, 3, 4})
|
||||
gtest.Assert(s.Size(), 3)
|
||||
a := s.Pops(6)
|
||||
gtest.Assert(len(a), 3)
|
||||
gtest.AssertIN(a, []int{1, 2, 3, 4})
|
||||
gtest.Assert(s.Size(), 0)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
s := gset.New(true)
|
||||
a := []interface{}{1, 2, 3, 4}
|
||||
s.Add(a...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.Assert(s.Pops(-2), nil)
|
||||
gtest.AssertIN(s.Pops(-1), a)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -223,11 +223,36 @@ func TestIntSet_Sum(t *testing.T) {
|
||||
|
||||
func TestIntSet_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewIntSet()
|
||||
s1.Add(4).Add(2).Add(3)
|
||||
gtest.AssertIN(s1.Pop(), []int{4, 2, 3})
|
||||
gtest.AssertIN(s1.Pop(), []int{4, 2, 3})
|
||||
gtest.Assert(s1.Size(), 3)
|
||||
s := gset.NewIntSet()
|
||||
s.Add(4).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
gtest.AssertIN(s.Pop(), []int{4, 2, 3})
|
||||
gtest.AssertIN(s.Pop(), []int{4, 2, 3})
|
||||
gtest.Assert(s.Size(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(1).Add(4).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.Assert(s.Pops(0), nil)
|
||||
gtest.AssertIN(s.Pops(1), []int{1, 4, 2, 3})
|
||||
gtest.Assert(s.Size(), 3)
|
||||
a := s.Pops(2)
|
||||
gtest.Assert(len(a), 2)
|
||||
gtest.AssertIN(a, []int{1, 4, 2, 3})
|
||||
gtest.Assert(s.Size(), 1)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewIntSet(true)
|
||||
a := []int{1, 2, 3, 4}
|
||||
s.Add(a...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.Assert(s.Pops(-2), nil)
|
||||
gtest.AssertIN(s.Pops(-1), a)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -259,20 +259,36 @@ func TestStrSet_Remove(t *testing.T) {
|
||||
|
||||
func TestStrSet_Pop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewStrSetFrom([]string{"a", "b", "c"}, true)
|
||||
str1 := s1.Pop()
|
||||
gtest.Assert(strings.Contains("a,b,c", str1), true)
|
||||
a := []string{"a", "b", "c", "d"}
|
||||
s := gset.NewStrSetFrom(a, true)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.AssertIN(s.Pop(), a)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
gtest.AssertIN(s.Pop(), a)
|
||||
gtest.Assert(s.Size(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_Pops(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewStrSetFrom([]string{"a", "b", "c"}, true)
|
||||
strs1 := s1.Pops(2)
|
||||
gtest.AssertIN(strs1, []string{"a", "b", "c"})
|
||||
gtest.Assert(len(strs1), 2)
|
||||
str2 := s1.Pops(7)
|
||||
gtest.AssertIN(str2, []string{"a", "b", "c"})
|
||||
a := []string{"a", "b", "c", "d"}
|
||||
s := gset.NewStrSetFrom(a, true)
|
||||
array := s.Pops(2)
|
||||
gtest.Assert(len(array), 2)
|
||||
gtest.Assert(s.Size(), 2)
|
||||
gtest.AssertIN(array, a)
|
||||
gtest.Assert(s.Pops(0), nil)
|
||||
gtest.AssertIN(s.Pops(2), a)
|
||||
gtest.Assert(s.Size(), 0)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewStrSet(true)
|
||||
a := []string{"1", "2", "3", "4"}
|
||||
s.Add(a...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.Assert(s.Pops(-2), nil)
|
||||
gtest.AssertIN(s.Pops(-1), a)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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 // 执行结束时间(毫秒)
|
||||
}
|
||||
|
||||
// 表字段结构信息
|
||||
@ -230,7 +230,7 @@ func New(name ...string) (db DB, err error) {
|
||||
// which is DEFAULT_GROUP_NAME in default.
|
||||
func Instance(name ...string) (db DB, err error) {
|
||||
group := configs.defaultGroup
|
||||
if len(name) > 0 {
|
||||
if len(name) > 0 && name[0] != "" {
|
||||
group = name[0]
|
||||
}
|
||||
v := instances.GetOrSetFuncLock(group, func() interface{} {
|
||||
@ -344,9 +344,9 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
|
||||
}
|
||||
|
||||
if bs.maxConnLifetime > 0 {
|
||||
sqlDb.SetConnMaxLifetime(time.Duration(bs.maxConnLifetime) * time.Second)
|
||||
sqlDb.SetConnMaxLifetime(bs.maxConnLifetime * time.Second)
|
||||
} else if node.MaxConnLifetime > 0 {
|
||||
sqlDb.SetConnMaxLifetime(time.Duration(node.MaxConnLifetime) * time.Second)
|
||||
sqlDb.SetConnMaxLifetime(node.MaxConnLifetime * time.Second)
|
||||
}
|
||||
return sqlDb
|
||||
}, 0)
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -31,8 +30,8 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
wordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
|
||||
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`)
|
||||
wordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`) // Regular expression object for a word.
|
||||
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`) // Regular expression object for a string which has operator at its tail.
|
||||
)
|
||||
|
||||
// 获取最近一条执行的sql
|
||||
@ -68,23 +67,24 @@ 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)
|
||||
} else {
|
||||
bs.logger.Debug(s)
|
||||
bs.logger.StackWithFilter(gPATH_FILTER_KEY).Debug(s)
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,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)
|
||||
@ -143,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)
|
||||
@ -354,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))
|
||||
}
|
||||
@ -434,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))
|
||||
}
|
||||
@ -521,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
|
||||
}
|
||||
@ -542,11 +544,9 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
|
||||
}
|
||||
params := []interface{}(nil)
|
||||
switch kind {
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
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))
|
||||
}
|
||||
@ -571,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
|
||||
}
|
||||
@ -643,91 +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 {
|
||||
// map/struct类型
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
for key, value := range structToMap(where) {
|
||||
// 字段安全符号判断
|
||||
key = bs.db.quoteWord(key)
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND ")
|
||||
}
|
||||
// 支持slice键值/属性,如果只有一个?占位符号,那么作为IN查询,否则打散作为多个查询参数
|
||||
rv := reflect.ValueOf(value)
|
||||
switch rv.Kind() {
|
||||
case reflect.Slice:
|
||||
fallthrough
|
||||
case 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 key[len(key)-1] != '<' && key[len(key)-1] != '>' && key[len(key)-1] != '=' {
|
||||
buffer.WriteString(key + "=?")
|
||||
} else {
|
||||
buffer.WriteString(key + " ?")
|
||||
}
|
||||
} else {
|
||||
buffer.WriteString(key)
|
||||
}
|
||||
newArgs = append(newArgs, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
buffer.WriteString(gconv.String(where))
|
||||
}
|
||||
// 没有任何条件查询参数,直接返回
|
||||
if buffer.Len() == 0 {
|
||||
return "", args
|
||||
}
|
||||
newArgs = append(newArgs, args...)
|
||||
newWhere = buffer.String()
|
||||
if len(newArgs) > 0 {
|
||||
// 支持例如 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)
|
||||
}
|
||||
|
||||
// 使用关键字操作符转义给定字符串。
|
||||
// 如果给定的字符串不为单词,那么不转义,直接返回该字符串。
|
||||
func (bs *dbBase) quoteWord(s string) string {
|
||||
@ -739,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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
@ -23,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操作是否开启查询缓存功能
|
||||
cacheTime int // 查询缓存时间
|
||||
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
|
||||
)
|
||||
|
||||
@ -178,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()
|
||||
@ -185,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
|
||||
}
|
||||
|
||||
@ -281,12 +302,17 @@ func (md *Model) Batch(batch int) *Model {
|
||||
}
|
||||
|
||||
// 查询缓存/清除缓存操作,需要注意的是,事务查询不支持缓存。
|
||||
// 当time < 0时表示清除缓存, time=0时表示不过期, time > 0时表示过期时间,time过期时间单位:秒;
|
||||
// name表示自定义的缓存名称(注意不要出现重复),便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称),
|
||||
// 例如:查询缓存时设置名称,在特定的业务逻辑中清理缓存时可以给定缓存名称进行精准清理。
|
||||
func (md *Model) Cache(time int, name ...string) *Model {
|
||||
// 1. 当 expire < 0时表示清除缓存,expire=0 时表示不过期, expire > 0时表示过期时间。
|
||||
// 2. expire参数类型为interface{},这是一个兼容旧版本的方式,该参数支持 int/time.Duration 类型,当传递类型为int时,表示缓存多少秒。
|
||||
// 3. name表示自定义的缓存名称(注意不要出现重复),便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称),
|
||||
// 例如:查询缓存时设置名称,在特定的业务逻辑中清理缓存时可以给定缓存名称进行精准清理。
|
||||
func (md *Model) Cache(expire interface{}, name ...string) *Model {
|
||||
model := md.getModel()
|
||||
model.cacheTime = time
|
||||
if d, ok := expire.(time.Duration); ok {
|
||||
model.cacheExpire = d
|
||||
} else {
|
||||
model.cacheExpire = gconv.Duration(expire) * time.Second
|
||||
}
|
||||
if len(name) > 0 {
|
||||
model.cacheName = name[0]
|
||||
}
|
||||
@ -328,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]
|
||||
}
|
||||
@ -352,7 +378,7 @@ func (md *Model) filterDataForInsertOrUpdate(data interface{}) interface{} {
|
||||
} else if m, ok := md.data.(Map); ok {
|
||||
return md.doFilterDataMapForInsertOrUpdate(m, true)
|
||||
}
|
||||
return nil
|
||||
return data
|
||||
}
|
||||
|
||||
// doFilterDataMapForInsertOrUpdate does the filter features for map.
|
||||
@ -361,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, ",") {
|
||||
@ -647,10 +675,10 @@ func (md *Model) getAll(query string, args ...interface{}) (result Result, err e
|
||||
result, err = md.db.doGetAll(md.getLink(), query, args...)
|
||||
// 查询缓存保存处理
|
||||
if len(cacheKey) > 0 && err == nil {
|
||||
if md.cacheTime < 0 {
|
||||
if md.cacheExpire < 0 {
|
||||
md.db.getCache().Remove(cacheKey)
|
||||
} else {
|
||||
md.db.getCache().Set(cacheKey, result, md.cacheTime*1000)
|
||||
md.db.getCache().Set(cacheKey, result, md.cacheExpire)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
@ -658,13 +686,51 @@ func (md *Model) getAll(query string, args ...interface{}) (result Result, err e
|
||||
|
||||
// 检查是否需要查询查询缓存
|
||||
func (md *Model) checkAndRemoveCache() {
|
||||
if md.cacheEnabled && md.cacheTime < 0 && len(md.cacheName) > 0 {
|
||||
if md.cacheEnabled && md.cacheExpire < 0 && len(md.cacheName) > 0 {
|
||||
md.db.getCache().Remove(md.cacheName)
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化当前输入参数,返回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
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -28,13 +28,13 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{}
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
return fieldValue
|
||||
|
||||
case "int", "tinyint", "small_int", "medium_int":
|
||||
case "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint":
|
||||
if gstr.ContainsI(fieldType, "unsigned") {
|
||||
gconv.Uint(string(fieldValue))
|
||||
}
|
||||
return gconv.Int(string(fieldValue))
|
||||
|
||||
case "big_int":
|
||||
case "big_int", "bigint":
|
||||
if gstr.ContainsI(fieldType, "unsigned") {
|
||||
gconv.Uint64(string(fieldValue))
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ package gdb_test
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
@ -225,6 +227,14 @@ func Test_Model_Update(t *testing.T) {
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
|
||||
// Update + Data(string)
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Data("passport='user_33'").Where("passport='user_3'").Update()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Clone(t *testing.T) {
|
||||
@ -756,6 +766,46 @@ func Test_Model_Where(t *testing.T) {
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
|
||||
// gmap.Map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(gmap.NewFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// gmap.Map key operator
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(gmap.NewFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
|
||||
// list map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(gmap.NewListMapFrom(g.MapAnyAny{"id": 3, "nickname": "name_3"})).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// list map key operator
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(gmap.NewListMapFrom(g.MapAnyAny{"id>": 1, "id<": 3})).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
|
||||
// tree map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id": 3, "nickname": "name_3"})).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// tree map key operator
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(gmap.NewTreeMapFrom(gutil.ComparatorString, g.MapAnyAny{"id>": 1, "id<": 3})).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
|
||||
// complicated where 1
|
||||
gtest.Case(t, func() {
|
||||
//db.SetDebug(true)
|
||||
@ -1002,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",
|
||||
@ -1030,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")
|
||||
})
|
||||
}
|
||||
|
||||
23
frame/g/g.go
23
frame/g/g.go
@ -30,17 +30,18 @@ type MapIntBool = map[int]bool
|
||||
|
||||
// Frequently-used slice type alias.
|
||||
type List = []Map
|
||||
type ListAnyStr = []map[interface{}]string
|
||||
type ListAnyInt = []map[interface{}]int
|
||||
type ListStrAny = []map[string]interface{}
|
||||
type ListStrStr = []map[string]string
|
||||
type ListStrInt = []map[string]int
|
||||
type ListIntAny = []map[int]interface{}
|
||||
type ListIntStr = []map[int]string
|
||||
type ListIntInt = []map[int]int
|
||||
type ListAnyBool = []map[interface{}]bool
|
||||
type ListStrBool = []map[string]bool
|
||||
type ListIntBool = []map[int]bool
|
||||
type ListAnyAny = []Map
|
||||
type ListAnyStr = []MapAnyStr
|
||||
type ListAnyInt = []MapAnyInt
|
||||
type ListStrAny = []MapStrAny
|
||||
type ListStrStr = []MapStrStr
|
||||
type ListStrInt = []MapStrInt
|
||||
type ListIntAny = []MapIntAny
|
||||
type ListIntStr = []MapIntStr
|
||||
type ListIntInt = []MapIntInt
|
||||
type ListAnyBool = []MapAnyBool
|
||||
type ListStrBool = []MapStrBool
|
||||
type ListIntBool = []MapIntBool
|
||||
|
||||
// Frequently-used slice type alias.
|
||||
type Slice = []interface{}
|
||||
|
||||
@ -27,7 +27,6 @@ import (
|
||||
|
||||
const (
|
||||
gFRAME_CORE_COMPONENT_NAME_REDIS = "gf.core.component.redis"
|
||||
gFRAME_CORE_COMPONENT_NAME_GKVDB = "gf.core.component.gkvdb"
|
||||
gFRAME_CORE_COMPONENT_NAME_DATABASE = "gf.core.component.database"
|
||||
)
|
||||
|
||||
@ -160,66 +159,13 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
|
||||
return nil
|
||||
}
|
||||
node := &gdb.ConfigNode{}
|
||||
if value, ok := nodeMap["host"]; ok {
|
||||
node.Host = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["port"]; ok {
|
||||
node.Port = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["user"]; ok {
|
||||
node.User = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["pass"]; ok {
|
||||
node.Pass = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["name"]; ok {
|
||||
node.Name = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["type"]; ok {
|
||||
node.Type = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["role"]; ok {
|
||||
node.Role = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["debug"]; ok {
|
||||
node.Debug = gconv.Bool(value)
|
||||
}
|
||||
if value, ok := nodeMap["charset"]; ok {
|
||||
node.Charset = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["weight"]; ok {
|
||||
node.Weight = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["linkinfo"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["link-info"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["linkInfo"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
err := gconv.Struct(nodeMap, node)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
}
|
||||
if value, ok := nodeMap["link"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["max-idle"]; ok {
|
||||
node.MaxIdleConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxIdle"]; ok {
|
||||
node.MaxIdleConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["max-open"]; ok {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxOpen"]; ok {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["max-lifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Duration(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxLifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Duration(value)
|
||||
}
|
||||
// Parse link syntax.
|
||||
if node.LinkInfo != "" && node.Type == "" {
|
||||
match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.LinkInfo)
|
||||
|
||||
@ -118,7 +118,7 @@ func (m *Manager) Translate(content string, language ...string) string {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
var data map[string]string
|
||||
if len(language) > 0 {
|
||||
if len(language) > 0 && language[0] != "" {
|
||||
data = m.data[language[0]]
|
||||
} else {
|
||||
data = m.data[m.options.Language]
|
||||
|
||||
@ -137,6 +137,10 @@ func (r *Response) WriteStatus(status int, content ...interface{}) {
|
||||
} else {
|
||||
r.Write(http.StatusText(status))
|
||||
}
|
||||
if r.Header().Get("Content-Type") == "" {
|
||||
r.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
//r.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
}
|
||||
}
|
||||
|
||||
// ServeFile serves the file to the response.
|
||||
|
||||
@ -58,7 +58,7 @@ func (r *Response) ParseTplContent(content string, params ...gview.Params) (stri
|
||||
// 内置变量/对象
|
||||
func (r *Response) buildInVars(params ...map[string]interface{}) map[string]interface{} {
|
||||
vars := map[string]interface{}(nil)
|
||||
if len(params) > 0 {
|
||||
if len(params) > 0 && params[0] != nil {
|
||||
vars = params[0]
|
||||
} else {
|
||||
vars = make(map[string]interface{})
|
||||
|
||||
@ -155,10 +155,6 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
}
|
||||
if request.Response.Header().Get("Content-Type") == "" {
|
||||
request.Response.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
//r.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
}
|
||||
|
||||
// 设置Session Id到Cookie中
|
||||
if request.Session.IsDirty() && request.Session.Id() != request.GetSessionId() {
|
||||
@ -254,7 +250,7 @@ func (s *Server) serveFile(r *Request, f *staticServeFile, allowIndex ...bool) {
|
||||
} else {
|
||||
info := f.file.FileInfo()
|
||||
r.Response.wroteHeader = true
|
||||
http.ServeContent(r.Response.Writer, r.Request, info.Name(), info.ModTime(), f.file)
|
||||
http.ServeContent(r.Response.Writer.RawWriter(), r.Request, info.Name(), info.ModTime(), f.file)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -254,6 +254,10 @@ func (g *RouterGroup) doBind(bindType string, pattern string, object interface{}
|
||||
if err != nil {
|
||||
glog.Fatalf("invalid pattern: %s", pattern)
|
||||
}
|
||||
// If there'a already a domain, unset the domain field in the pattern.
|
||||
if g.domain != nil {
|
||||
domain = ""
|
||||
}
|
||||
if bindType == "REST" {
|
||||
pattern = prefix + "/" + strings.TrimLeft(path, "/")
|
||||
} else {
|
||||
|
||||
@ -208,8 +208,11 @@ func (item *handlerParsedItem) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// 生成回调方法查询的Key
|
||||
func (s *Server) serveHandlerKey(method, path, domain string) string {
|
||||
if method == "" {
|
||||
return path + "@" + strings.ToLower(domain)
|
||||
if len(domain) > 0 {
|
||||
domain = "@" + domain
|
||||
}
|
||||
return strings.ToUpper(method) + ":" + path + "@" + strings.ToLower(domain)
|
||||
if method == "" {
|
||||
return path + strings.ToLower(domain)
|
||||
}
|
||||
return strings.ToUpper(method) + ":" + path + strings.ToLower(domain)
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// 基本路由功能以及优先级测试
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
@ -17,7 +16,6 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
// 基本路由功能测试
|
||||
func Test_Router_DomainBasic(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
@ -69,7 +67,6 @@ func Test_Router_DomainBasic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// 测试HTTP Method注册.
|
||||
func Test_Router_DomainMethod(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
@ -162,7 +159,6 @@ func Test_Router_DomainMethod(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// 测试状态返回.
|
||||
func Test_Router_DomainStatus(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
@ -259,7 +255,6 @@ func Test_Router_DomainStatus(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// 自定义状态码处理.
|
||||
func Test_Router_DomainCustomStatusHandler(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
@ -299,7 +294,6 @@ func Test_Router_DomainCustomStatusHandler(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// 测试不存在的路由.
|
||||
func Test_Router_Domain404(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
@ -332,3 +326,45 @@ func Test_Router_Domain404(t *testing.T) {
|
||||
gtest.Assert(client.GetContent("/"), "hello")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Router_DomainGroup(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
d := s.Domain("localhost, local")
|
||||
d.Group("/", func(g *ghttp.RouterGroup) {
|
||||
g.Group("/app", func(gApp *ghttp.RouterGroup) {
|
||||
gApp.GET("/{table}/list/{page}.html", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Get("table"), "&", r.Get("page"))
|
||||
})
|
||||
gApp.GET("/order/info/{order_id}", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Get("order_id"))
|
||||
})
|
||||
gApp.DELETE("/comment/{id}", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Get("id"))
|
||||
})
|
||||
})
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
gtest.Case(t, func() {
|
||||
client1 := ghttp.NewClient()
|
||||
client1.SetPrefix(fmt.Sprintf("http://local:%d", p))
|
||||
|
||||
client2 := ghttp.NewClient()
|
||||
client2.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client1.GetContent("/app/t/list/2.html"), "t&2")
|
||||
gtest.Assert(client2.GetContent("/app/t/list/2.html"), "Not Found")
|
||||
|
||||
gtest.Assert(client1.GetContent("/app/order/info/2"), "2")
|
||||
gtest.Assert(client2.GetContent("/app/order/info/2"), "Not Found")
|
||||
|
||||
gtest.Assert(client1.GetContent("/app/comment/20"), "Not Found")
|
||||
gtest.Assert(client2.GetContent("/app/comment/20"), "Not Found")
|
||||
|
||||
gtest.Assert(client1.DeleteContent("/app/comment/20"), "20")
|
||||
gtest.Assert(client2.DeleteContent("/app/comment/20"), "Not Found")
|
||||
})
|
||||
}
|
||||
|
||||
64
net/ghttp/ghttp_unit_router_object_rest2_test.go
Normal file
64
net/ghttp/ghttp_unit_router_object_rest2_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
type ObjectRest2 struct{}
|
||||
|
||||
func (o *ObjectRest2) Init(r *ghttp.Request) {
|
||||
r.Response.Write("1")
|
||||
}
|
||||
|
||||
func (o *ObjectRest2) Shut(r *ghttp.Request) {
|
||||
r.Response.Write("2")
|
||||
}
|
||||
|
||||
func (o *ObjectRest2) Get(r *ghttp.Request) {
|
||||
r.Response.Write("Object Get", r.Get("id"))
|
||||
}
|
||||
|
||||
func (o *ObjectRest2) Put(r *ghttp.Request) {
|
||||
r.Response.Write("Object Put", r.Get("id"))
|
||||
}
|
||||
|
||||
func (o *ObjectRest2) Post(r *ghttp.Request) {
|
||||
r.Response.Write("Object Post", r.Get("id"))
|
||||
}
|
||||
|
||||
func (o *ObjectRest2) Delete(r *ghttp.Request) {
|
||||
r.Response.Write("Object Delete", r.Get("id"))
|
||||
}
|
||||
|
||||
func Test_Router_ObjectRest_Id(t *testing.T) {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindObjectRest("/object/:id", new(ObjectRest2))
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouteMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.Case(t, func() {
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/object/99"), "1Object Get992")
|
||||
gtest.Assert(client.PutContent("/object/99"), "1Object Put992")
|
||||
gtest.Assert(client.PostContent("/object/99"), "1Object Post992")
|
||||
gtest.Assert(client.DeleteContent("/object/99"), "1Object Delete992")
|
||||
})
|
||||
}
|
||||
89
net/ghttp/ghttp_unit_template_test.go
Normal file
89
net/ghttp/ghttp_unit_template_test.go
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// static service testing.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Template_Layout1(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
v := gview.New(gfile.Join(gdebug.CallerDirectory(), "testdata", "template", "layout1"))
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.SetView(v)
|
||||
s.BindHandler("/layout", func(r *ghttp.Request) {
|
||||
err := r.Response.WriteTpl("layout.html", g.Map{
|
||||
"mainTpl": "main/main1.html",
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
s.BindHandler("/nil", func(r *ghttp.Request) {
|
||||
err := r.Response.WriteTpl("layout.html", nil)
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
s.SetDumpRouteMap(false)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/layout"), "123")
|
||||
gtest.Assert(client.GetContent("/nil"), "123")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Template_Layout2(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
v := gview.New(gfile.Join(gdebug.CallerDirectory(), "testdata", "template", "layout2"))
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.SetView(v)
|
||||
s.BindHandler("/main1", func(r *ghttp.Request) {
|
||||
err := r.Response.WriteTpl("layout.html", g.Map{
|
||||
"mainTpl": "main/main1.html",
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
s.BindHandler("/main2", func(r *ghttp.Request) {
|
||||
err := r.Response.WriteTpl("layout.html", g.Map{
|
||||
"mainTpl": "main/main2.html",
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
s.BindHandler("/nil", func(r *ghttp.Request) {
|
||||
err := r.Response.WriteTpl("layout.html", nil)
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
s.SetDumpRouteMap(false)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
gtest.Assert(client.GetContent("/"), "Not Found")
|
||||
gtest.Assert(client.GetContent("/main1"), "a1b")
|
||||
gtest.Assert(client.GetContent("/main2"), "a2b")
|
||||
gtest.Assert(client.GetContent("/nil"), "ab")
|
||||
})
|
||||
}
|
||||
1
net/ghttp/testdata/template/layout1/container.html
vendored
Normal file
1
net/ghttp/testdata/template/layout1/container.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{define "container"}}2{{end}}
|
||||
1
net/ghttp/testdata/template/layout1/footer.html
vendored
Normal file
1
net/ghttp/testdata/template/layout1/footer.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{define "footer"}}3{{end}}
|
||||
1
net/ghttp/testdata/template/layout1/header.html
vendored
Normal file
1
net/ghttp/testdata/template/layout1/header.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{define "header"}}1{{end}}
|
||||
1
net/ghttp/testdata/template/layout1/layout.html
vendored
Normal file
1
net/ghttp/testdata/template/layout1/layout.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{template "header"}}{{template "container"}}{{template "footer"}}
|
||||
1
net/ghttp/testdata/template/layout2/footer.html
vendored
Normal file
1
net/ghttp/testdata/template/layout2/footer.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
b
|
||||
1
net/ghttp/testdata/template/layout2/header.html
vendored
Normal file
1
net/ghttp/testdata/template/layout2/header.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
a
|
||||
1
net/ghttp/testdata/template/layout2/layout.html
vendored
Normal file
1
net/ghttp/testdata/template/layout2/layout.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{include "header.html" .}}{{include .mainTpl .}}{{include "footer.html" .}}
|
||||
1
net/ghttp/testdata/template/layout2/main/main1.html
vendored
Normal file
1
net/ghttp/testdata/template/layout2/main/main1.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
1
|
||||
1
net/ghttp/testdata/template/layout2/main/main2.html
vendored
Normal file
1
net/ghttp/testdata/template/layout2/main/main2.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
2
|
||||
@ -28,7 +28,7 @@ type Storage interface {
|
||||
RemoveAll() error
|
||||
|
||||
// GetSession returns the session data map for given session id.
|
||||
// The parameter specifies the TTL for this session.
|
||||
// The parameter <ttl> specifies the TTL for this session.
|
||||
// It returns nil if the TTL is exceeded.
|
||||
GetSession(id string, ttl time.Duration) map[string]interface{}
|
||||
// SetSession updates the data map for specified session id.
|
||||
|
||||
@ -72,8 +72,13 @@ func NewStorageFile(path ...string) *StorageFile {
|
||||
cryptoEnabled: DefaultStorageFileCryptoEnabled,
|
||||
updatingIdSet: gset.NewStrSet(true),
|
||||
}
|
||||
// Batch updates the TTL for session ids timely.
|
||||
gtimer.AddSingleton(DefaultStorageFileLoopInterval, func() {
|
||||
for _, id := range s.updatingIdSet.Slice() {
|
||||
id := ""
|
||||
for {
|
||||
if id = s.updatingIdSet.Pop(); id == "" {
|
||||
break
|
||||
}
|
||||
s.doUpdateTTL(id)
|
||||
}
|
||||
})
|
||||
@ -160,7 +165,6 @@ func (s *StorageFile) GetSession(id string, ttl time.Duration) map[string]interf
|
||||
}
|
||||
|
||||
// SetSession updates the content for session id.
|
||||
// Note that the parameter <content> is the serialized bytes for session map.
|
||||
func (s *StorageFile) SetSession(id string, data map[string]interface{}) error {
|
||||
path := s.sessionFilePath(id)
|
||||
content, err := json.Marshal(data)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -74,13 +74,17 @@ func (view *View) funcGe(value interface{}, other interface{}) bool {
|
||||
}
|
||||
|
||||
// Build-in template function: include
|
||||
func (view *View) funcInclude(file string, data ...map[string]interface{}) string {
|
||||
func (view *View) funcInclude(file interface{}, data ...map[string]interface{}) string {
|
||||
var m map[string]interface{} = nil
|
||||
if len(data) > 0 {
|
||||
m = data[0]
|
||||
}
|
||||
path := gconv.String(file)
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
// It will search the file internally.
|
||||
content, err := view.Parse(file, m)
|
||||
content, err := view.Parse(path, m)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
array := strings.Split(str, delimiter)
|
||||
for k, v := range array {
|
||||
v = strings.Trim(v, cut)
|
||||
// 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 := make([]string, 0)
|
||||
for _, v := range strings.Split(str, delimiter) {
|
||||
v = Trim(v, characterMask...)
|
||||
if v != "" {
|
||||
array[k] = strings.Trim(v, cut)
|
||||
array = append(array, v)
|
||||
}
|
||||
}
|
||||
return array
|
||||
@ -457,12 +458,13 @@ 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 {
|
||||
array := make([]string, 0)
|
||||
for _, v := range strings.Split(str, delimiter) {
|
||||
v = strings.TrimSpace(v)
|
||||
if v != "" {
|
||||
array[k] = v
|
||||
array = append(array, v)
|
||||
}
|
||||
}
|
||||
return array
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -176,6 +176,22 @@ func Test_ChunkSplit(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SplitAndTrim(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := `
|
||||
|
||||
010
|
||||
|
||||
020
|
||||
|
||||
`
|
||||
a := gstr.SplitAndTrim(s, "\n", "0")
|
||||
gtest.Assert(len(a), 2)
|
||||
gtest.Assert(a[0], "1")
|
||||
gtest.Assert(a[1], "2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Fields(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gstr.Fields("我爱 Go Frame"), []string{
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
package gstr_test
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
@ -18,6 +19,17 @@ import (
|
||||
|
||||
func Test_Parse(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// url
|
||||
gtest.Case(t, func() {
|
||||
s := "goframe.org/index?name=john&score=100"
|
||||
u, err := url.Parse(s)
|
||||
gtest.Assert(err, nil)
|
||||
m, err := gstr.Parse(u.RawQuery)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(m["name"], "john")
|
||||
gtest.Assert(m["score"], "100")
|
||||
})
|
||||
|
||||
// name overwrite
|
||||
m, err := gstr.Parse("a=1&a=2")
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
@ -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("我爱中国人", "我爱"), "中国人")
|
||||
})
|
||||
}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.9.8"
|
||||
const VERSION = "v1.9.10"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user