Compare commits

..

139 Commits

Author SHA1 Message Date
7e95058cb5 donator updates 2020-06-08 22:34:48 +08:00
f33753e0cd add example for package ghttp/glog 2020-06-08 20:21:35 +08:00
ac71e1d753 rename Dump to RawDump for ghttp.ClientResponse 2020-06-08 19:26:14 +08:00
8151b6efd6 add more example for package gredis 2020-06-08 19:17:24 +08:00
94de306c93 add zero time filtering for package gdb 2020-06-08 13:46:45 +08:00
51f8ea26de add example for package gredis 2020-06-06 18:12:42 +08:00
29d9bb17cd improve function Unscoped for package gdb 2020-06-06 16:28:16 +08:00
30501a882d improve function Struct for package gconv 2020-06-06 15:31:04 +08:00
f2f98e1d16 rename testing file names for package gdb 2020-06-06 14:38:05 +08:00
cbf465eeea improve body interface implements for ghttp.Reqest/Response 2020-06-06 13:34:58 +08:00
79c400e912 Merge branch 'master' of https://github.com/gogf/gf 2020-06-06 10:10:05 +08:00
cc4c49b5b0 add unit testing case for pointer attribute mapping for function Parse 2020-06-06 10:09:19 +08:00
96ea2c911d Merge pull request #696 from sanrentai/master
the sqlserver type money  should be converted to float64
2020-06-05 21:08:49 +08:00
131b11680a readme update 2020-06-05 20:36:05 +08:00
3c6ab96283 fix issue in function Walk and add more example for package gset 2020-06-04 21:46:22 +08:00
d2c4fa921a add more example for package gmap 2020-06-04 20:45:18 +08:00
dbb51a9328 improve package glist 2020-06-04 20:13:33 +08:00
55bfc3fa73 add more examples for package garray 2020-06-04 18:28:33 +08:00
d9c5182d1a gitignore updates 2020-06-04 17:56:32 +08:00
899fcbf2da add mor example for package garray 2020-06-04 17:55:43 +08:00
6e2c0d8706 comment and readme update 2020-06-04 17:29:16 +08:00
c4e599ce63 improve random bytes buffer hanlding for package grand 2020-06-04 14:45:56 +08:00
9cad9e4467 add more unit testing cases for package gstr 2020-06-04 00:03:30 +08:00
48019444ea README and DONATOR update 2020-06-03 23:49:12 +08:00
76d31a7fbb improve slice result handling by treating it as string slice for package gredis 2020-06-03 23:44:21 +08:00
facb9d93c0 fix issue of multiple slice arguments handling in function where 2020-06-03 21:36:16 +08:00
1d6bd46c5e README updates 2020-06-03 20:33:21 +08:00
8646dc1825 Merge pull request #702 from misitebao/master
更新 readme 中文版
2020-06-03 18:49:35 +08:00
2992bdeed7 修改之前的捐赠名称 2020-06-03 11:38:21 +08:00
c5601e6c88 更新 readme 中文版 2020-06-03 10:59:20 +08:00
ef1d9a561c improve the route feature for ghttp.Server 2020-06-03 00:09:51 +08:00
2d3b32c94a money 类型转换错误 2020-05-29 15:41:37 +08:00
269378aa0d fix issue in unit testing case for package gsession; version updates 2020-05-28 20:28:07 +08:00
6889542801 improve package guid 2020-05-27 11:26:05 +08:00
8e6f1f1740 improve performance of random buffer usage for package grand 2020-05-25 23:39:09 +08:00
b6ab1a992c improve empty checks for common interfaces implementer 2020-05-25 14:26:08 +08:00
a5a267567c comment update for package garray 2020-05-22 12:04:58 +08:00
f05f855c07 improve default max header bytes from 1KB to 10KB for ghttp.Server 2020-05-21 20:10:38 +08:00
bc5f773ba6 make WriteTimeout default to 0 for ghttp.Server 2020-05-19 19:16:47 +08:00
55308cc37c improve package gipv4 by formating the value retrieving fuctions to Get* 2020-05-18 23:50:31 +08:00
4d9db6edf0 improve package gconv for map converting 2020-05-18 20:58:19 +08:00
38111a64e3 improve files listing for ghttp.Server 2020-05-18 20:09:00 +08:00
bd8d3fca08 improve domain rule for package gvalid 2020-05-18 19:44:15 +08:00
89ccaa3915 improve function Do for package gredis, adding auto marshal feature for arguments 2020-05-18 19:18:53 +08:00
7c2cff7d99 add function GetVar for package gcache 2020-05-17 18:16:26 +08:00
788e15dbb6 comment update for package guid 2020-05-17 15:40:24 +08:00
a0172d9d7e README updates 2020-05-17 15:16:13 +08:00
bc1a7a1644 README updates 2020-05-17 15:11:07 +08:00
c98234d3e6 improve session feature by allowing custom session id creating function for ghttp.Server 2020-05-17 14:33:27 +08:00
45a94d23d5 improve session feature by allowing custom session id creating function for ghttp.Server 2020-05-17 14:33:21 +08:00
351de5ee6a fix concurrent issue in buffer handling of package grand 2020-05-17 12:46:28 +08:00
3559436d1e improve package guid 2020-05-17 08:50:37 +08:00
189f4c1637 improve package gsession 2020-05-16 22:08:16 +08:00
caead810e2 add package guid; improve performance of package grand 2020-05-16 21:56:31 +08:00
ebdc5d9c9d comment update for package grand 2020-05-16 16:08:30 +08:00
4164059211 add function B for package grand; improve package grand 2020-05-16 16:05:31 +08:00
bd27258c46 donator updates 2020-05-16 15:43:16 +08:00
ddf0605bc4 go mod tidy 2020-05-16 15:36:04 +08:00
991dbe50e0 add more functions for package gipv4; move package guuid to standalone package of gogf 2020-05-16 15:35:21 +08:00
8050efb835 improve package gfile 2020-05-16 14:06:35 +08:00
9355bc73a2 improve package gvalid 2020-05-16 13:31:24 +08:00
acc2a6a353 improve copy feature for package gfile 2020-05-16 00:50:01 +08:00
09e83e7b8d improve version functions for package gstr 2020-05-16 00:12:23 +08:00
c0df8a3d80 improve version functions for package gstr 2020-05-16 00:11:08 +08:00
33e890d225 improve version functions for package gstr 2020-05-16 00:10:12 +08:00
750e5df962 improve gstr.CompareVersion 2020-05-15 23:42:17 +08:00
74c65439fc add function ScanDirFunc for package gfile 2020-05-15 21:51:03 +08:00
f290bd7170 fix issue in http.File implements for package gres 2020-05-14 21:25:54 +08:00
d398b749d4 improve support for types of database pgsql 2020-05-14 21:15:44 +08:00
2fd5a1574a improve package gres 2020-05-14 20:32:01 +08:00
a7504f0629 add more unit testing cases for package gdb 2020-05-13 23:21:11 +08:00
3c35bb85ee improve unit testing cases for package gi18n/ghttp 2020-05-13 19:35:56 +08:00
6621edb04c improve i18n of chinese for package gvalid 2020-05-13 19:10:38 +08:00
b0c722e297 improve package gvalid 2020-05-10 22:34:16 +08:00
47a6284274 improve package gvalid 2020-05-10 22:32:10 +08:00
150b8edb70 improve package gvalid 2020-05-10 17:49:23 +08:00
aa5d3285eb improve package gvalid 2020-05-10 16:48:00 +08:00
993d6e3076 improve package gvalid 2020-05-10 10:56:11 +08:00
78ad9d8c2d improve error message of package gvalid 2020-05-10 09:05:52 +08:00
feff3ddce3 comment update for package ghttp 2020-05-09 19:19:42 +08:00
80fddad64d fix issue in PopRight function for package garray; improve json.Marshal for package garray 2020-05-08 21:08:06 +08:00
1e6dd0be02 readme updates 2020-05-08 19:16:52 +08:00
22ecf4f1b6 add context feature for package glog 2020-05-08 17:12:37 +08:00
5d21148657 comment updates for package ghttp 2020-05-07 23:05:33 +08:00
edb745ed92 version updates 2020-05-07 11:31:34 +08:00
61f4e0da6f improve package gcache 2020-05-06 21:09:02 +08:00
767afa3106 improve package gcache 2020-05-06 19:06:49 +08:00
16779359ee remove package gchan 2020-05-06 18:43:19 +08:00
770653fafa enable the route dump for unit testing of logging feature for ghttp.Server 2020-05-05 00:09:39 +08:00
0c021b6da7 add unit testing case of status handler feature for ghttp.Server 2020-05-05 00:01:09 +08:00
ec92b08f25 add function Maps for package gvar; add function GetMaps for package gjson; improve MapToMap* functions for package gconv 2020-05-04 23:42:51 +08:00
13e2353729 fix lock issue in function search for package garray 2020-05-03 23:08:18 +08:00
90d19a84ce improve package garray/gset, adding function ContainsI 2020-05-03 22:52:04 +08:00
b4c0b95d8a add support for old version of gres 2020-05-03 22:16:12 +08:00
b7e8255066 add support for old version of gre 2020-05-03 22:08:08 +08:00
5f8e6ad9ed comment update for package ghttp 2020-05-01 03:31:04 +08:00
c8d253eb56 change binary content from hex string to base64 string for package gres 2020-05-01 02:35:24 +08:00
4814624cff change binary content from hex string to base64 string for package gres 2020-05-01 02:16:42 +08:00
cc67f3d388 improve package gcompress 2020-05-01 01:47:02 +08:00
f7c2a51c9f fix issue in zip feature for package gcompress; improve package gres 2020-05-01 00:18:45 +08:00
3db83e1159 improve package gtimer 2020-04-30 22:22:35 +08:00
3bb002796c donator update 2020-04-30 20:46:27 +08:00
45170bc53e add ClientMaxBodySize configuration for ghttp.Server 2020-04-30 20:37:09 +08:00
b79ff84c6f add struct slice conversion for request parameters for ghttp.Request; improve package gconv 2020-04-30 16:53:47 +08:00
938c46fec9 readme update 2020-04-29 19:33:14 +08:00
8a13d94526 improve Record.Struct for package gdb 2020-04-29 09:12:13 +08:00
1e844d505a comment update for package ghttp/gconv; readme update 2020-04-29 00:14:29 +08:00
a123a2c086 improve struct conversion of empty result/record for package gdb 2020-04-28 21:03:25 +08:00
1eeeeb853e improve unit testing case of error logging for ghttp.Server 2020-04-28 15:21:17 +08:00
6e7224e306 improve error handling for gconv.Struct/ghttp.Server; add NewSkip/NewfSkip function for package gerror 2020-04-28 15:04:07 +08:00
9e064e2651 donator updates 2020-04-27 23:34:22 +08:00
8d9dd17eac add Walk function for package gset; improve fields handling feature for package gdb 2020-04-27 21:18:42 +08:00
cf1d3d3d2b improve package gdebug; add more unit testing case for package gdb 2020-04-27 17:56:04 +08:00
9480ffcdc0 improve function SetPath/AddPath for package gview 2020-04-27 17:07:00 +08:00
5db10add4a fix issue in unnacessary quoting of fields in select statement of gdb.Model 2020-04-27 16:30:53 +08:00
fa66bf5d9d improve cache feature of package gdb.Model 2020-04-26 21:31:55 +08:00
f69da3ace1 add function Transaction for package gdb 2020-04-26 17:47:19 +08:00
e01bfa05c3 listening ports change for unit testing cases of ghttp.Server 2020-04-26 17:13:48 +08:00
231238c157 improve parameter handing for ghttp.Request 2020-04-26 17:08:07 +08:00
7edec099ab improve raw request/response content dump for ghttp.Client 2020-04-24 00:00:52 +08:00
83eb8be064 Merge pull request #609 from kirileec/master
add Raw* method in ClientResponse to get request and response string
2020-04-23 22:58:04 +08:00
7af30df494 Merge branch 'master' into master 2020-04-23 22:57:23 +08:00
f026686fda fix issue in dupicated expiration handling in response cookies og ghttp.Client 2020-04-23 21:12:32 +08:00
35a50b9c6c readme update 2020-04-23 21:06:42 +08:00
010e2f951a downgrade the required golang version from v1.13 to v1.11 2020-04-23 20:38:25 +08:00
f7f86ad65a downgrade the required golang version from v1.13 to v1.11 2020-04-23 20:25:59 +08:00
1e19f447d1 improve ghttp.Client in context handling 2020-04-23 20:23:23 +08:00
8cc378331d add one unit testing case for ghttp.Server 2020-04-23 20:10:10 +08:00
4721f68fd8 add performance testing result 2020-04-23 19:51:08 +08:00
0d11c0a1f8 add performance testing result 2020-04-23 19:41:34 +08:00
5076613a8f add performance testing result 2020-04-23 17:25:11 +08:00
c5a44daa65 add performance testing result 2020-04-23 17:23:57 +08:00
520970b71f add performance testing result 2020-04-23 17:19:08 +08:00
ebfb08ee3f add performance testing result 2020-04-23 17:18:15 +08:00
9e38b2cb90 add performance testing result 2020-04-23 17:14:11 +08:00
71b1f00dc5 improve package gdb/gstr/gvalid 2020-04-20 22:36:28 +08:00
9160bee1af change comments 2020-04-11 12:16:53 +08:00
e64fd088b9 fix when no response 2020-04-08 20:11:06 +08:00
15672e7a09 #591 add Raw* method in ClientResponse to get request and response string 2020-04-08 19:24:03 +08:00
266 changed files with 8189 additions and 3545 deletions

View File

@ -1,8 +1,9 @@
# MySQL数据库配置
[database]
# debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
MaxOpen = 100
#[database]
# [[database.default]]

View File

@ -1,16 +1,68 @@
package main
import (
"archive/zip"
"fmt"
"github.com/gogf/gf/encoding/gcompress"
"io"
"os"
"path/filepath"
"strings"
)
func main() {
err := gcompress.ZipPath(
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg`,
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg\encoding\gcompress\data.zip`,
"my-dir",
)
fmt.Println(err)
// srcFile could be a single file or a directory
func Zip(srcFile string, destZip string) error {
zipfile, err := os.Create(destZip)
if err != nil {
return err
}
defer zipfile.Close()
archive := zip.NewWriter(zipfile)
defer archive.Close()
filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile)+"/")
// header.Name = path
if info.IsDir() {
header.Name += "/"
} else {
header.Method = zip.Deflate
}
writer, err := archive.CreateHeader(header)
if err != nil {
return err
}
if !info.IsDir() {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
}
return err
})
return err
}
func main() {
src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test`
dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test.zip`
//src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD`
//dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD.zip`
fmt.Println(gcompress.ZipPath(src, dst))
//fmt.Println(Zip(src, dst))
}

View File

@ -9,7 +9,7 @@ import (
func Upload(r *ghttp.Request) {
saveDirPath := "/tmp/"
files := r.GetUploadFiles("upload-file")
if err := files.Save(saveDirPath); err != nil {
if _, err := files.Save(saveDirPath); err != nil {
r.Response.WriteExit(err)
}
r.Response.WriteExit("upload successfully")

View File

@ -2,12 +2,34 @@ package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"path/filepath"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
)
func SendXmlFile(gameId int, areaName string, filePath string) error {
path := filepath.FromSlash(filePath)
fmt.Println(path)
data := g.Map{
"gameName": gameId,
"area": areaName,
"file": "@file:" + path,
"contentType": "json",
}
if r, err := ghttp.Post("http://127.0.0.1:8199/upload", data); err != nil {
panic(err)
} else {
defer r.Close()
fmt.Println("ok")
}
return nil
}
func main() {
SendXmlFile(1, "xxx", "/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/net/ghttp/server/session.go")
return
path := "/home/john/Workspace/Go/github.com/gogf/gf/version.go"
r, e := ghttp.Post("http://127.0.0.1:8199/upload", "upload-file=@file:"+path)
if e != nil {

View File

@ -8,8 +8,8 @@ import (
// Upload uploads files to /tmp .
func Upload(r *ghttp.Request) {
saveDirPath := "/tmp/"
files := r.GetUploadFiles("upload-file")
if err := files.Save(saveDirPath); err != nil {
files := r.GetUploadFiles("file")
if _, err := files.Save(saveDirPath); err != nil {
r.Response.WriteExit(err)
}
r.Response.WriteExit("upload successfully")

View File

@ -5,21 +5,18 @@ import (
"github.com/gogf/gf/net/ghttp"
)
type User struct {
}
type User struct{}
func (c *User) Index(r *ghttp.Request) {
r.Response.Write("Index")
}
// 不符合规范,不会被注册
func (c *User) Test(r *ghttp.Request, value interface{}) {
func (c *User) Test(r *ghttp.Request) {
r.Response.Write("Test")
}
func main() {
s := g.Server()
s.BindObject("/user", new(User))
u := new(User)
s.Group("/", func(group *ghttp.RouterGroup) {
group.GET("/db-{table}/{id}", u, "Test")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,14 @@
package main
import (
"context"
"github.com/gogf/gf/os/glog"
)
func main() {
glog.SetCtxKeys("Trace-Id", "Span-Id", "Test")
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
glog.Ctx(ctx).Print(1, 2, 3)
}

View File

@ -1,18 +0,0 @@
[database]
debug = true
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
[redis]
default = "127.0.0.1:6379,0"
cache = "127.0.0.1:6379,1"
# Logger.
[logger]
Path = "/tmp/log/gf-app"
Level = "all"
Stdout = true
[viewer]
delimiters = ["${", "}"]
autoencode = true

View File

@ -1,5 +0,0 @@
package main
func main() {
}

View File

@ -1,11 +0,0 @@
package main
import (
"fmt"
"github.com/gogf/gf/net/ghttp"
)
func main() {
r := ghttp.PostContent("http://127.0.0.1:8199/test", `<doc><id>1</id><name>john</name><password1>123Abc!@#</password1><password2>123Abc!@#</password2></doc>`)
fmt.Println(r)
}

View File

@ -8,6 +8,9 @@ import (
func main() {
for i := 0; i < 100; i++ {
fmt.Println(grand.Rand(0, 99999))
fmt.Println(grand.S(16))
}
for i := 0; i < 100; i++ {
fmt.Println(grand.N(0, 99999999))
}
}

View File

@ -0,0 +1,13 @@
package main
import (
"fmt"
"github.com/gogf/gf/util/guid"
)
func main() {
for i := 0; i < 100; i++ {
s := guid.S()
fmt.Println(s, len(s))
}
}

View File

@ -0,0 +1,20 @@
package main
import (
"fmt"
"math"
"strconv"
)
func main() {
// 36*36^2+36*36+36
var s string
fmt.Println(strconv.ParseUint("zzz", 36, 3))
fmt.Println(1 << 1)
// MaxInt64
s = strconv.FormatUint(math.MaxUint64, 16)
fmt.Println(s, len(s))
// PID
s = strconv.FormatInt(1000000, 36)
fmt.Println(s, len(s))
}

View File

@ -0,0 +1,23 @@
package main
import (
"fmt"
"github.com/gogf/gf/util/guid"
)
func main() {
for i := 0; i < 100; i++ {
s := guid.S([]byte("123"))
fmt.Println(s, len(s))
}
fmt.Println()
for i := 0; i < 100; i++ {
s := guid.S([]byte("123"), []byte("456"))
fmt.Println(s, len(s))
}
fmt.Println()
for i := 0; i < 100; i++ {
s := guid.S([]byte("123"), []byte("456"), []byte("789"))
fmt.Println(s, len(s))
}
}

View File

@ -0,0 +1,13 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gvalid"
)
func main() {
g.I18n().SetLanguage("cn")
err := gvalid.Check("", "required", nil)
fmt.Println(err.String())
}

View File

@ -0,0 +1,14 @@
"gf.gvalid.required" = "字段不能为空"

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ cbuild
**/.DS_Store
.vscode/
go.sum
.example/other/

View File

@ -1,6 +1,8 @@
language: go
go:
- "1.11.x"
- "1.12.x"
- "1.13.x"
- "1.14.x"

View File

@ -56,7 +56,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|🚶|wechat|¥6.88| 喝杯冰阔落
|a*l|wechat|¥10.00| gf
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00|
|*包|wechat|¥9.99|
|[米司特包](https://github.com/misitebao)|wechat|¥9.99|
|重庆宝尔威科技|wechat|¥6.66|
|琦玉-QPT|wechat|¥6.66|
|sailsea|wechat|¥11.00|
@ -64,7 +64,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|*华|wechat|¥6.66| 感谢郭强的热心
|[Playhi](https://github.com/Playhi)|alipay|¥10.00|
|北京京纬互动科技|alipay|¥200.00|
|米司特包|wechat|¥99.99|
|[米司特包](https://github.com/misitebao)|wechat|¥99.99|
|金毛|alipay|¥100.00|
|1*1x|wechat|¥100.00|
|[ywanbing](https://github.com/ywanbing)|wechat|¥66.66|
@ -73,6 +73,24 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|蔡蔡|wechat|¥666.00| gf真强大让项目省心
|jack|wechat|¥100.00|
|sbilly|wechat|¥100.00| 祝好!
|[米司特包](https://github.com/misitebao)|wechat|¥166.66| 大佬加油!
|*秦|wechat|¥20.00| 给群主献上一杯咖啡...
|[zhuhuan12](https://gitee.com/zhuhuan12)|wechat|¥50.00|
|faddei|qq|¥9.99|
|*天|wechat|¥10.00|
|*.|wechat|¥20.00| 学生一个微薄之力,冲
|李勇|wechat|¥50.00|
|[米司特包](https://github.com/misitebao)|wechat|¥66.66|
|千年|wechat|¥1.08|
|[szzxing](https://github.com/szzxing)|wechat|¥50.00|
|伟客互联|wechat|¥66.66|
|sbilly|wechat|¥99.00|
|**阳|alipay|¥100.01|
|**亮|alipay|¥10.00|
|[mingzaily](https://github.com/mingzaily)|alipay|¥30.00|
|[seny0929](https://gitee.com/seny0929)|alipay|¥9.90|
|Fly的狐狸|alipay|¥100.00|
<img src="https://goframe.org/images/donate.png"/>

View File

@ -9,12 +9,13 @@
English | [简体中文](README_ZH.MD)
`GF(GoFrame)` is a modular, full-featured and production-ready application development framework
`GF(GoFrame)` is a modular, powerful, high-performance and production-ready application development framework
of golang. Providing a series of core components and dozens of practical modules, such as:
cache, logging, containers, timer, resource, validator, database orm, etc.
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
template, https, hooks, rewrites and many more features.
> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
# Installation
```
@ -27,9 +28,76 @@ require github.com/gogf/gf latest
# Limitation
```
golang version >= 1.13
golang version >= 1.11
```
# Packages
1. **Primary Package**
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
1. **Community Package**
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# Performance
Here's the most popular Golang frameworks and libraries performance testing result in `WEB Server`. Performance testing cases source codes are hosted at: https://github.com/gogf/gf-performance
## Environment
OS : Ubuntu 18.04 amd64
CPU : AMD A8-6600K x 4
MEM : 32GB
GO : v1.13.4
## Testing Tool
`ab`: Apache HTTP server benchmarking tool.
Command:
```
ab -t 10 -c 100 http://127.0.0.1:3000/hello
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
ab -t 10 -c 100 http://127.0.0.1:3000/json
```
The concurrency starts from `100` to `10000`.
> Run `5` times for each case of each project and pick up the best testing result.
## 1. Hello World
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
</tr>
</table>
## 2. Json Response
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
</tr>
</table>
# Documentation
* 中文官网: https://goframe.org
@ -43,20 +111,20 @@ golang version >= 1.13
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# License
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
# Known Users
Logos are not authorized to be shown due to trademark copyrights.
# Contributors
This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)].
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
# Donators
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](DONATOR.MD)?
@ -65,6 +133,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
# Thanks
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
@ -75,3 +144,7 @@ We appreciate any kind of sponsorship for `GF` development. If you've got some i

View File

@ -15,16 +15,17 @@
并提供了Web服务开发的系列核心组件Router、Cookie、Session、Middleware、服务注册、模板引擎等等
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
# 特点
* 模块化、松耦合设计;
* 模块丰富开箱即用;
* 简便易用易于维护;
* 社区活跃,大牛谦逊低调脾气好;
* 模块丰富开箱即用;
* 简便易用易于维护;
* 高代码质量、高单元测试覆盖率;
* 社区活跃,大牛谦逊低调脾气好;
* 详尽的开发文档及示例;
* 完善的本地中文化支持;
* 更适合企业及团队使用;
* 设计为团队及企业使用;
# 地址
- **主库**https://github.com/gogf/gf
@ -41,14 +42,78 @@ require github.com/gogf/gf latest
# 限制
```shell
golang版本 >= 1.13
golang版本 >= 1.11
```
# 模块
1. **核心模块**
`GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
1. **社区模块**
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
# 架构
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# 性能
以下是目前最流行的`WEB Server` Golang框架/类库性能测试结果。
性能测试用例源代码仓库: https://github.com/gogf/gf-performance
## 环境:
OS : Ubuntu 18.04 amd64
CPU : AMD A8-6600K x 4
MEM : 32GB
GO : v1.13.4
## 工具
`ab`: Apache HTTP server benchmarking tool.
测试命令:
```
ab -t 10 -c 100 http://127.0.0.1:3000/hello
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
ab -t 10 -c 100 http://127.0.0.1:3000/json
```
并发客户端数量从 `100` 递增到 `10000`。
> 每个项目的每个用例均运行`5`次,取最优的结果展示。
## 1. Hello World
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
</tr>
</table>
## 2. Json Response
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
</tr>
</table>
# 文档
@ -62,12 +127,16 @@ golang版本 >= 1.13
- WX交流群微信添加`389961817`备注`GF`
- 主库ISSUEhttps://github.com/gogf/gf/issues
> 建议通过阅读`Gorame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
# 协议
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
# 用户
由于商标版权缘故,未经厂商商务部授权允许无法用于宣传展示。
# 贡献
感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)].
@ -84,4 +153,5 @@ golang版本 >= 1.13
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。
# 感谢
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>

View File

@ -4,5 +4,5 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package garray provides concurrent-safe/unsafe arrays.
// Package garray provides most commonly used array containers which also support concurrent-safe/unsafe switch feature.
package garray

View File

@ -22,6 +22,8 @@ import (
)
// Array is a golang array with rich features.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type Array struct {
mu rwmutex.RWMutex
array []interface{}
@ -288,7 +290,7 @@ func (a *Array) PopRight() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return nil, false
}
value = a.array[index]
@ -473,18 +475,7 @@ func (a *Array) Contains(value interface{}) bool {
// or returns -1 if not exists.
func (a *Array) Search(value interface{}) int {
a.mu.RLock()
result := -1
for index, v := range a.array {
if v == value {
result = index
break
}
}
a.mu.RUnlock()
return result
}
func (a *Array) doSearch(value interface{}) int {
defer a.mu.RUnlock()
if len(a.array) == 0 {
return -1
}
@ -499,6 +490,7 @@ func (a *Array) doSearch(value interface{}) int {
}
// Unique uniques the array, clear repeated items.
// Example: [1,1,2,3,2] -> [1,2,3]
func (a *Array) Unique() *Array {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
@ -728,7 +720,8 @@ func (a *Array) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *Array) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a Array) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)

View File

@ -20,6 +20,8 @@ import (
)
// IntArray is a golang int array with rich features.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type IntArray struct {
mu rwmutex.RWMutex
array []int
@ -264,7 +266,7 @@ func (a *IntArray) PopRight() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return 0, false
}
value = a.array[index]
@ -489,6 +491,10 @@ func (a *IntArray) Contains(value int) bool {
// or returns -1 if not exists.
func (a *IntArray) Search(value int) int {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return -1
}
result := -1
for index, v := range a.array {
if v == value {
@ -496,11 +502,11 @@ func (a *IntArray) Search(value int) int {
break
}
}
a.mu.RUnlock()
return result
}
// Unique uniques the array, clear repeated items.
// Example: [1,1,2,3,2] -> [1,2,3]
func (a *IntArray) Unique() *IntArray {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
@ -713,7 +719,8 @@ func (a *IntArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *IntArray) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a IntArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)

View File

@ -22,6 +22,8 @@ import (
)
// StrArray is a golang string array with rich features.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type StrArray struct {
mu rwmutex.RWMutex
array []string
@ -252,7 +254,7 @@ func (a *StrArray) PopRight() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return "", false
}
value = a.array[index]
@ -473,13 +475,30 @@ func (a *StrArray) Contains(value string) bool {
return a.Search(value) != -1
}
// ContainsI checks whether a value exists in the array with case-insensitively.
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
func (a *StrArray) ContainsI(value string) bool {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return false
}
for _, v := range a.array {
if strings.EqualFold(v, value) {
return true
}
}
return false
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *StrArray) Search(value string) int {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return -1
}
a.mu.RLock()
result := -1
for index, v := range a.array {
if strings.Compare(v, value) == 0 {
@ -487,11 +506,11 @@ func (a *StrArray) Search(value string) int {
break
}
}
a.mu.RUnlock()
return result
}
// Unique uniques the array, clear repeated items.
// Example: [1,1,2,3,2] -> [1,2,3]
func (a *StrArray) Unique() *StrArray {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
@ -715,7 +734,8 @@ func (a *StrArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *StrArray) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a StrArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)

View File

@ -22,7 +22,10 @@ import (
)
// SortedArray is a golang sorted array with rich features.
// It's using increasing order in default.
// It is using increasing order in default, which can be changed by
// setting it a custom comparator.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type SortedArray struct {
mu rwmutex.RWMutex
array []interface{}
@ -122,7 +125,13 @@ func (a *SortedArray) Sort() *SortedArray {
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
// It's alias of function Append, see Append.
func (a *SortedArray) Add(values ...interface{}) *SortedArray {
return a.Append(values...)
}
// Append adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedArray) Append(values ...interface{}) *SortedArray {
if len(values) == 0 {
return a
}
@ -218,7 +227,7 @@ func (a *SortedArray) PopRight() (value interface{}, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return nil, false
}
value = a.array[index]
@ -425,13 +434,13 @@ func (a *SortedArray) Search(value interface{}) (index int) {
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
if len(a.array) == 0 {
return -1, -2
}
min := 0
max := len(a.array) - 1
mid := 0
@ -660,7 +669,8 @@ func (a *SortedArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *SortedArray) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a SortedArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)

View File

@ -19,7 +19,10 @@ import (
)
// SortedIntArray is a golang sorted int array with rich features.
// It's using increasing order in default.
// It is using increasing order in default, which can be changed by
// setting it a custom comparator.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type SortedIntArray struct {
mu rwmutex.RWMutex
array []int
@ -107,7 +110,13 @@ func (a *SortedIntArray) Sort() *SortedIntArray {
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
// It's alias of function Append, see Append.
func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
return a.Append(values...)
}
// Append adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedIntArray) Append(values ...int) *SortedIntArray {
if len(values) == 0 {
return a
}
@ -203,7 +212,7 @@ func (a *SortedIntArray) PopRight() (value int, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return 0, false
}
value = a.array[index]
@ -422,13 +431,13 @@ func (a *SortedIntArray) Search(value int) (index int) {
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
if len(a.array) == 0 {
return -1, -2
}
min := 0
max := len(a.array) - 1
mid := 0
@ -634,7 +643,8 @@ func (a *SortedIntArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a SortedIntArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)

View File

@ -12,6 +12,7 @@ import (
"github.com/gogf/gf/text/gstr"
"math"
"sort"
"strings"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
@ -19,7 +20,10 @@ import (
)
// SortedStrArray is a golang sorted string array with rich features.
// It's using increasing order in default.
// It is using increasing order in default, which can be changed by
// setting it a custom comparator.
// It contains a concurrent-safe/unsafe switch, which should be set
// when its initialization and cannot be changed then.
type SortedStrArray struct {
mu rwmutex.RWMutex
array []string
@ -92,7 +96,13 @@ func (a *SortedStrArray) Sort() *SortedStrArray {
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
// It's alias of function Append, see Append.
func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
return a.Append(values...)
}
// Append adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedStrArray) Append(values ...string) *SortedStrArray {
if len(values) == 0 {
return a
}
@ -188,7 +198,7 @@ func (a *SortedStrArray) PopRight() (value string, found bool) {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
if index <= 0 {
if index < 0 {
return "", false
}
value = a.array[index]
@ -392,6 +402,22 @@ func (a *SortedStrArray) Contains(value string) bool {
return a.Search(value) != -1
}
// ContainsI checks whether a value exists in the array with case-insensitively.
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
func (a *SortedStrArray) ContainsI(value string) bool {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return false
}
for _, v := range a.array {
if strings.EqualFold(v, value) {
return true
}
}
return false
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedStrArray) Search(value string) (index int) {
@ -407,13 +433,13 @@ func (a *SortedStrArray) Search(value string) (index int) {
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
if len(a.array) == 0 {
return -1, -2
}
min := 0
max := len(a.array) - 1
mid := 0
@ -630,7 +656,8 @@ func (a *SortedStrArray) String() string {
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
// Note that do not use pointer as its receiver here.
func (a SortedStrArray) MarshalJSON() ([]byte, error) {
a.mu.RLock()
defer a.mu.RUnlock()
return json.Marshal(a.array)

View File

@ -72,8 +72,50 @@ func Example_basic() {
// []
}
func Example_iterate() {
array := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
// Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order
// with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
array.Iterator(func(k int, v string) bool {
fmt.Println(k, v)
return true
})
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
array.IteratorDesc(func(k int, v string) bool {
fmt.Println(k, v)
return true
})
// Output:
// 0 a
// 1 b
// 2 c
// 2 c
// 1 b
// 0 a
}
func Example_reverse() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Reverse makes array with elements in reverse order.
fmt.Println(array.Reverse().Slice())
// Output:
// [9 8 7 6 5 4 3 2 1]
}
func Example_shuffle() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Shuffle randomly shuffles the array.
fmt.Println(array.Shuffle().Slice())
}
func Example_rand() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Randomly retrieve and return 2 items from the array.
// It does not delete the items from array.
@ -84,6 +126,26 @@ func Example_rand() {
fmt.Println(array.PopRand())
}
func Example_join() {
array := garray.NewFrom(g.Slice{"a", "b", "c", "d"})
fmt.Println(array.Join(","))
// Output:
// a,b,c,d
}
func Example_chunk() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
fmt.Println(array.Chunk(2))
// Output:
// [[1 2] [3 4] [5 6] [7 8] [9]]
}
func Example_popItem() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
@ -101,10 +163,38 @@ func Example_popItem() {
// [7 8]
}
func Example_walk() {
var array garray.StrArray
tables := g.SliceStr{"user", "user_detail"}
prefix := "gf_"
array.Append(tables...)
// Add prefix for given table names.
array.Walk(func(value string) string {
return prefix + value
})
fmt.Println(array.Slice())
// Output:
// [gf_user gf_user_detail]
}
func Example_contains() {
var array garray.StrArray
array.Append("a")
fmt.Println(array.Contains("a"))
fmt.Println(array.Contains("A"))
fmt.Println(array.ContainsI("A"))
// Output:
// true
// false
// true
}
func Example_mergeArray() {
array1 := garray.NewFrom([]interface{}{1, 2})
array2 := garray.NewFrom([]interface{}{3, 4})
slice1 := []interface{}{5, 6}
array1 := garray.NewFrom(g.Slice{1, 2})
array2 := garray.NewFrom(g.Slice{3, 4})
slice1 := g.Slice{5, 6}
slice2 := []int{7, 8}
slice3 := []string{"9", "0"}
fmt.Println(array1.Slice())

View File

@ -137,6 +137,65 @@ func TestArray_PopRands(t *testing.T) {
})
}
func TestArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
t.Assert(array.PopLefts(2), g.Slice{1, 2})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{3})
t.Assert(array.Len(), 0)
})
}
func TestArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewFrom(g.Slice{1, 2, 3})
t.Assert(array.PopRights(2), g.Slice{2, 3})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{1})
t.Assert(array.Len(), 0)
})
}
func TestArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.New()
@ -501,6 +560,7 @@ func TestArray_RLockFunc(t *testing.T) {
}
func TestArray_Json(t *testing.T) {
// pointer
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
a1 := garray.NewArrayFrom(s1)
@ -519,7 +579,26 @@ func TestArray_Json(t *testing.T) {
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// value.
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
a1 := *garray.NewArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.New()
err2 = json.Unmarshal(b2, &a2)
t.Assert(err2, nil)
t.Assert(a2.Slice(), s1)
var a3 garray.Array
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -532,6 +611,25 @@ func TestArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
// value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.Array
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)

View File

@ -230,6 +230,65 @@ func TestIntArray_Fill(t *testing.T) {
})
}
func TestIntArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestIntArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestIntArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
t.Assert(array.PopLefts(2), g.Slice{1, 2})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{3})
t.Assert(array.Len(), 0)
})
}
func TestIntArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
t.Assert(array.PopRights(2), g.Slice{2, 3})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{1})
t.Assert(array.Len(), 0)
})
}
func TestIntArray_Chunk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5}
@ -546,6 +605,7 @@ func TestIntArray_RLockFunc(t *testing.T) {
}
func TestIntArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
a1 := garray.NewIntArrayFrom(s1)
@ -563,7 +623,25 @@ func TestIntArray_Json(t *testing.T) {
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
a1 := *garray.NewIntArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewIntArray()
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s1)
var a3 garray.IntArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -576,6 +654,25 @@ func TestIntArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.IntArray
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)

View File

@ -64,6 +64,16 @@ func Test_StrArray_Basic(t *testing.T) {
})
}
func TestStrArray_ContainsI(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := garray.NewStrArray()
s.Append("a", "b", "C")
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
t.Assert(s.ContainsI("A"), true)
})
}
func TestStrArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect1 := []string{"0", "1", "2", "3"}
@ -119,6 +129,65 @@ func TestStrArray_PushAndPop(t *testing.T) {
})
}
func TestStrArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestStrArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestStrArray_PopLefts(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
t.Assert(array.PopLefts(2), g.Slice{"1", "2"})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{"3"})
t.Assert(array.Len(), 0)
})
}
func TestStrArray_PopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
t.Assert(array.PopRights(2), g.Slice{"2", "3"})
t.Assert(array.Len(), 1)
t.Assert(array.PopLefts(2), g.Slice{"1"})
t.Assert(array.Len(), 0)
})
}
func TestStrArray_PopLeftsAndPopRights(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArray()
@ -535,6 +604,7 @@ func TestStrArray_LockFunc(t *testing.T) {
}
func TestStrArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
a1 := garray.NewStrArrayFrom(s1)
@ -552,7 +622,25 @@ func TestStrArray_Json(t *testing.T) {
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
a1 := *garray.NewStrArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewStrArray()
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s1)
var a3 garray.StrArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -565,6 +653,25 @@ func TestStrArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, data["Scores"])
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.StrArray
}
data := g.Map{
"Name": "john",
"Scores": []string{"A+", "A", "A"},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)

View File

@ -148,34 +148,62 @@ func TestSortedArray_Remove(t *testing.T) {
func TestSortedArray_PopLeft(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1 := garray.NewSortedArrayFrom(
[]interface{}{"a", "d", "c", "b"},
gutil.ComparatorString,
)
i1, ok := array1.PopLeft()
t.Assert(ok, true)
t.Assert(gconv.String(i1), "a")
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"b", "c", "d"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestSortedArray_PopRight(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "b"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
array1 := garray.NewSortedArrayFrom(
[]interface{}{"a", "d", "c", "b"},
gutil.ComparatorString,
)
i1, ok := array1.PopRight()
t.Assert(ok, true)
t.Assert(gconv.String(i1), "d")
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "b", "c"})
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestSortedArray_PopRand(t *testing.T) {
@ -249,7 +277,6 @@ func TestSortedArray_PopLefts(t *testing.T) {
t.Assert(len(i2), 4)
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
t.Assert(array1.Len(), 0)
})
}
@ -267,7 +294,7 @@ func TestSortedArray_PopRights(t *testing.T) {
i2 := array1.PopRights(10)
t.Assert(len(i2), 4)
t.Assert(array1.Len(), 0)
})
}
@ -612,6 +639,7 @@ func TestSortedArray_Merge(t *testing.T) {
}
func TestSortedArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
s2 := []interface{}{"a", "b", "c", "d"}
@ -631,7 +659,27 @@ func TestSortedArray_Json(t *testing.T) {
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []interface{}{"a", "b", "d", "c"}
s2 := []interface{}{"a", "b", "c", "d"}
a1 := *garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedArray(gutil.ComparatorString)
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s2)
var a3 garray.SortedArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -663,6 +711,42 @@ func TestSortedArray_Json(t *testing.T) {
t.AssertIN(v, data["Scores"])
t.Assert(ok, true)
v, ok = user.Scores.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.SortedArray
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.AssertNE(user.Scores, nil)
t.Assert(user.Scores.Len(), 3)
v, ok := user.Scores.PopLeft()
t.AssertIN(v, data["Scores"])
t.Assert(ok, true)
v, ok = user.Scores.PopLeft()
t.AssertIN(v, data["Scores"])
t.Assert(ok, true)
v, ok = user.Scores.PopLeft()
t.AssertIN(v, data["Scores"])
t.Assert(ok, true)
v, ok = user.Scores.PopLeft()
t.Assert(v, nil)
t.Assert(ok, false)

View File

@ -133,6 +133,21 @@ func TestSortedIntArray_PopLeft(t *testing.T) {
t.Assert(array1.Len(), 3)
t.Assert(array1.Search(1), -1)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3})
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestSortedIntArray_PopRight(t *testing.T) {
@ -145,6 +160,23 @@ func TestSortedIntArray_PopRight(t *testing.T) {
t.Assert(array1.Len(), 3)
t.Assert(array1.Search(5), -1)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3})
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestSortedIntArray_PopRand(t *testing.T) {
@ -504,6 +536,7 @@ func TestSortedIntArray_Merge(t *testing.T) {
}
func TestSortedIntArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
s2 := []int{1, 2, 3, 4}
@ -522,7 +555,26 @@ func TestSortedIntArray_Json(t *testing.T) {
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 4, 3, 2}
s2 := []int{1, 2, 3, 4}
a1 := *garray.NewSortedIntArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedIntArray()
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s2)
var a3 garray.SortedIntArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -535,6 +587,25 @@ func TestSortedIntArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, []int{98, 99, 100})
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.SortedIntArray
}
data := g.Map{
"Name": "john",
"Scores": []int{99, 100, 98},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)

View File

@ -51,6 +51,16 @@ func TestSortedStrArray_SetArray(t *testing.T) {
})
}
func TestSortedStrArray_ContainsI(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := garray.NewSortedStrArray()
s.Append("a", "b", "C")
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
t.Assert(s.ContainsI("A"), true)
})
}
func TestSortedStrArray_Sort(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"a", "d", "c", "b"}
@ -127,6 +137,21 @@ func TestSortedStrArray_PopLeft(t *testing.T) {
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains("a"), false)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2", "3"})
v, ok := array.PopLeft()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopLeft()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopLeft()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestSortedStrArray_PopRight(t *testing.T) {
@ -139,6 +164,23 @@ func TestSortedStrArray_PopRight(t *testing.T) {
t.Assert(array1.Len(), 4)
t.Assert(array1.Contains("e"), false)
})
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2", "3"})
v, ok := array.PopRight()
t.Assert(v, 3)
t.Assert(ok, true)
t.Assert(array.Len(), 2)
v, ok = array.PopRight()
t.Assert(v, 2)
t.Assert(ok, true)
t.Assert(array.Len(), 1)
v, ok = array.PopRight()
t.Assert(v, 1)
t.Assert(ok, true)
t.Assert(array.Len(), 0)
})
}
func TestSortedStrArray_PopRand(t *testing.T) {
@ -200,6 +242,7 @@ func TestSortedStrArray_PopLefts(t *testing.T) {
s1 = array1.PopLefts(4)
t.Assert(len(s1), 3)
t.Assert(s1, []string{"c", "d", "e"})
t.Assert(array1.Len(), 0)
})
}
@ -513,6 +556,7 @@ func TestSortedStrArray_Merge(t *testing.T) {
}
func TestSortedStrArray_Json(t *testing.T) {
// array pointer
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
s2 := []string{"a", "b", "c", "d"}
@ -533,7 +577,28 @@ func TestSortedStrArray_Json(t *testing.T) {
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array value
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
s2 := []string{"a", "b", "c", "d"}
a1 := *garray.NewSortedStrArrayFrom(s1)
b1, err1 := json.Marshal(a1)
b2, err2 := json.Marshal(s1)
t.Assert(b1, b2)
t.Assert(err1, err2)
a2 := garray.NewSortedStrArray()
err1 = json.Unmarshal(b2, &a2)
t.Assert(a2.Slice(), s2)
t.Assert(a2.Interfaces(), s2)
var a3 garray.SortedStrArray
err := json.Unmarshal(b2, &a3)
t.Assert(err, nil)
t.Assert(a3.Slice(), s1)
t.Assert(a3.Interfaces(), s1)
})
// array pointer
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
@ -546,6 +611,25 @@ func TestSortedStrArray_Json(t *testing.T) {
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)
t.Assert(user.Name, data["Name"])
t.Assert(user.Scores, []string{"A", "A", "A+"})
})
// array value
gtest.C(t, func(t *gtest.T) {
type User struct {
Name string
Scores garray.SortedStrArray
}
data := g.Map{
"Name": "john",
"Scores": []string{"A+", "A", "A"},
}
b, err := json.Marshal(data)
t.Assert(err, nil)
user := new(User)
err = json.Unmarshal(b, user)
t.Assert(err, nil)

View File

@ -1,70 +0,0 @@
// Copyright 2017 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 gchan provides graceful channel for no panic operations.
//
// It's safe to call Chan.Push/Close functions repeatedly.
package gchan
import (
"errors"
"github.com/gogf/gf/container/gtype"
)
// Graceful channel.
type Chan struct {
channel chan interface{}
closed *gtype.Bool
}
// New creates a graceful channel with given <limit>.
func New(limit int) *Chan {
return &Chan{
channel: make(chan interface{}, limit),
closed: gtype.NewBool(),
}
}
// Push pushes <value> to channel.
// It is safe to be called repeatedly.
func (c *Chan) Push(value interface{}) error {
if c.closed.Val() {
return errors.New("channel is closed")
}
c.channel <- value
return nil
}
// Pop pops value from channel.
// If there's no value in channel, it would block to wait.
// If the channel is closed, it will return a nil value immediately.
func (c *Chan) Pop() interface{} {
return <-c.channel
}
// Close closes the channel.
// It is safe to be called repeatedly.
func (c *Chan) Close() {
if !c.closed.Set(true) {
close(c.channel)
}
}
// See Len.
func (c *Chan) Size() int {
return c.Len()
}
// Len returns the length of the channel.
func (c *Chan) Len() int {
return len(c.channel)
}
// Cap returns the capacity of the channel.
func (c *Chan) Cap() int {
return cap(c.channel)
}

View File

@ -1,30 +0,0 @@
// 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 gchan_test
import (
"fmt"
"github.com/gogf/gf/container/gchan"
)
func Example_basic() {
n := 10
c := gchan.New(n)
for i := 0; i < n; i++ {
c.Push(i)
}
fmt.Println(c.Len(), c.Cap())
for i := 0; i < n; i++ {
fmt.Print(c.Pop())
}
c.Close()
// Output:
//10 10
//0123456789
}

View File

@ -1,47 +0,0 @@
package gchan_test
import (
"errors"
"testing"
"github.com/gogf/gf/container/gchan"
"github.com/gogf/gf/test/gtest"
)
func Test_Gchan(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
ch := gchan.New(10)
t.Assert(ch.Cap(), 10)
t.Assert(ch.Push(1), nil)
t.Assert(ch.Len(), 1)
t.Assert(ch.Size(), 1)
ch.Pop()
t.Assert(ch.Len(), 0)
t.Assert(ch.Size(), 0)
ch.Close()
t.Assert(ch.Push(1), errors.New("channel is closed"))
ch = gchan.New(0)
ch1 := gchan.New(0)
go func() {
var i = 0
for {
v := ch.Pop()
if v == nil {
ch1.Push(i)
break
}
t.Assert(v, i)
i++
}
}()
for index := 0; index < 10; index++ {
ch.Push(index)
}
ch.Close()
t.Assert(ch1.Pop(), 10)
ch1.Close()
})
}

View File

@ -5,25 +5,26 @@
// You can obtain one at https://github.com/gogf/gf.
//
// Package glist provides a concurrent-safe/unsafe doubly linked list.
// Package glist provides most commonly used doubly linked list container which also supports concurrent-safe/unsafe switch feature.
package glist
import (
"bytes"
"container/list"
"encoding/json"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/internal/rwmutex"
)
type (
// List is a doubly linked list containing a concurrent-safe/unsafe switch.
// The switch should be set when its initialization and cannot be changed then.
List struct {
mu rwmutex.RWMutex
list list.List
list *list.List
}
// Element the item type of the list.
Element = list.Element
)
@ -31,7 +32,7 @@ type (
func New(safe ...bool) *List {
return &List{
mu: rwmutex.Create(safe...),
list: list.List{},
list: list.New(),
}
}
@ -39,7 +40,7 @@ func New(safe ...bool) *List {
// The parameter <safe> is used to specify whether using list in concurrent-safety,
// which is false in default.
func NewFrom(array []interface{}, safe ...bool) *List {
l := list.List{}
l := list.New()
for _, v := range array {
l.PushBack(v)
}
@ -52,6 +53,9 @@ func NewFrom(array []interface{}, safe ...bool) *List {
// PushFront inserts a new element <e> with value <v> at the front of list <l> and returns <e>.
func (l *List) PushFront(v interface{}) (e *Element) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
}
e = l.list.PushFront(v)
l.mu.Unlock()
return
@ -60,6 +64,9 @@ func (l *List) PushFront(v interface{}) (e *Element) {
// PushBack inserts a new element <e> with value <v> at the back of list <l> and returns <e>.
func (l *List) PushBack(v interface{}) (e *Element) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
}
e = l.list.PushBack(v)
l.mu.Unlock()
return
@ -68,6 +75,9 @@ func (l *List) PushBack(v interface{}) (e *Element) {
// PushFronts inserts multiple new elements with values <values> at the front of list <l>.
func (l *List) PushFronts(values []interface{}) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
}
for _, v := range values {
l.list.PushFront(v)
}
@ -77,6 +87,9 @@ func (l *List) PushFronts(values []interface{}) {
// PushBacks inserts multiple new elements with values <values> at the back of list <l>.
func (l *List) PushBacks(values []interface{}) {
l.mu.Lock()
if l.list == nil {
l.list = list.New()
}
for _, v := range values {
l.list.PushBack(v)
}
@ -86,20 +99,28 @@ func (l *List) PushBacks(values []interface{}) {
// PopBack removes the element from back of <l> and returns the value of the element.
func (l *List) PopBack() (value interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
return
}
if e := l.list.Back(); e != nil {
value = l.list.Remove(e)
}
l.mu.Unlock()
return
}
// PopFront removes the element from front of <l> and returns the value of the element.
func (l *List) PopFront() (value interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
return
}
if e := l.list.Front(); e != nil {
value = l.list.Remove(e)
}
l.mu.Unlock()
return
}
@ -107,6 +128,11 @@ func (l *List) PopFront() (value interface{}) {
// and returns values of the removed elements as slice.
func (l *List) PopBacks(max int) (values []interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
return
}
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
@ -117,7 +143,6 @@ func (l *List) PopBacks(max int) (values []interface{}) {
values[i] = l.list.Remove(l.list.Back())
}
}
l.mu.Unlock()
return
}
@ -125,6 +150,11 @@ func (l *List) PopBacks(max int) (values []interface{}) {
// and returns values of the removed elements as slice.
func (l *List) PopFronts(max int) (values []interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
return
}
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
@ -135,7 +165,6 @@ func (l *List) PopFronts(max int) (values []interface{}) {
values[i] = l.list.Remove(l.list.Front())
}
}
l.mu.Unlock()
return
}
@ -154,6 +183,10 @@ func (l *List) PopFrontAll() []interface{} {
// FrontAll copies and returns values of all elements from front of <l> as slice.
func (l *List) FrontAll() (values []interface{}) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
@ -161,13 +194,16 @@ func (l *List) FrontAll() (values []interface{}) {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
}
// BackAll copies and returns values of all elements from back of <l> as slice.
func (l *List) BackAll() (values []interface{}) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
@ -175,43 +211,54 @@ func (l *List) BackAll() (values []interface{}) {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
}
// FrontValue returns value of the first element of <l> or nil if the list is empty.
func (l *List) FrontValue() (value interface{}) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
if e := l.list.Front(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
}
// BackValue returns value of the last element of <l> or nil if the list is empty.
func (l *List) BackValue() (value interface{}) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
if e := l.list.Back(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
}
// Front returns the first element of list <l> or nil if the list is empty.
func (l *List) Front() (e *Element) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
e = l.list.Front()
l.mu.RUnlock()
return
}
// Back returns the last element of list <l> or nil if the list is empty.
func (l *List) Back() (e *Element) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
e = l.list.Back()
l.mu.RUnlock()
return
}
@ -219,8 +266,11 @@ func (l *List) Back() (e *Element) {
// The complexity is O(1).
func (l *List) Len() (length int) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length = l.list.Len()
l.mu.RUnlock()
return
}
@ -234,8 +284,11 @@ func (l *List) Size() int {
// The element and <p> must not be nil.
func (l *List) MoveBefore(e, p *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.MoveBefore(e, p)
l.mu.Unlock()
}
// MoveAfter moves element <e> to its new position after <p>.
@ -243,8 +296,11 @@ func (l *List) MoveBefore(e, p *Element) {
// The element and <p> must not be nil.
func (l *List) MoveAfter(e, p *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.MoveAfter(e, p)
l.mu.Unlock()
}
// MoveToFront moves element <e> to the front of list <l>.
@ -252,8 +308,11 @@ func (l *List) MoveAfter(e, p *Element) {
// The element must not be nil.
func (l *List) MoveToFront(e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.MoveToFront(e)
l.mu.Unlock()
}
// MoveToBack moves element <e> to the back of list <l>.
@ -261,8 +320,11 @@ func (l *List) MoveToFront(e *Element) {
// The element must not be nil.
func (l *List) MoveToBack(e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.MoveToBack(e)
l.mu.Unlock()
}
// PushBackList inserts a copy of an other list at the back of list <l>.
@ -273,8 +335,11 @@ func (l *List) PushBackList(other *List) {
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushBackList(&other.list)
l.mu.Unlock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.PushBackList(other.list)
}
// PushFrontList inserts a copy of an other list at the front of list <l>.
@ -285,8 +350,11 @@ func (l *List) PushFrontList(other *List) {
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushFrontList(&other.list)
l.mu.Unlock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
l.list.PushFrontList(other.list)
}
// InsertAfter inserts a new element <e> with value <v> immediately after <p> and returns <e>.
@ -294,8 +362,11 @@ func (l *List) PushFrontList(other *List) {
// The <p> must not be nil.
func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
e = l.list.InsertAfter(v, p)
l.mu.Unlock()
return
}
@ -304,8 +375,11 @@ func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
// The <p> must not be nil.
func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
e = l.list.InsertBefore(v, p)
l.mu.Unlock()
return
}
@ -314,25 +388,31 @@ func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
// The element must not be nil.
func (l *List) Remove(e *Element) (value interface{}) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
value = l.list.Remove(e)
l.mu.Unlock()
return
}
// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
func (l *List) Removes(es []*Element) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
for _, e := range es {
l.list.Remove(e)
}
l.mu.Unlock()
return
}
// RemoveAll removes all elements from list <l>.
func (l *List) RemoveAll() {
l.mu.Lock()
l.list = list.List{}
l.list = list.New()
l.mu.Unlock()
}
@ -345,14 +425,19 @@ func (l *List) Clear() {
func (l *List) RLockFunc(f func(list *list.List)) {
l.mu.RLock()
defer l.mu.RUnlock()
f(&l.list)
if l.list != nil {
f(l.list)
}
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (l *List) LockFunc(f func(list *list.List)) {
l.mu.Lock()
defer l.mu.Unlock()
f(&l.list)
if l.list == nil {
l.list = list.New()
}
f(l.list)
}
// Iterator is alias of IteratorAsc.
@ -365,6 +450,9 @@ func (l *List) Iterator(f func(e *Element) bool) {
func (l *List) IteratorAsc(f func(e *Element) bool) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
@ -380,6 +468,9 @@ func (l *List) IteratorAsc(f func(e *Element) bool) {
func (l *List) IteratorDesc(f func(e *Element) bool) {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return
}
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
@ -394,17 +485,14 @@ func (l *List) IteratorDesc(f func(e *Element) bool) {
func (l *List) Join(glue string) string {
l.mu.RLock()
defer l.mu.RUnlock()
if l.list == nil {
return ""
}
buffer := bytes.NewBuffer(nil)
length := l.list.Len()
if length > 0 {
s := ""
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
s = gconv.String(e.Value)
if gstr.IsNumeric(s) {
buffer.WriteString(s)
} else {
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
}
buffer.WriteString(gconv.String(e.Value))
if i != length-1 {
buffer.WriteString(glue)
}
@ -427,6 +515,9 @@ func (l *List) MarshalJSON() ([]byte, error) {
func (l *List) UnmarshalJSON(b []byte) error {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
var array []interface{}
if err := json.Unmarshal(b, &array); err != nil {
return err
@ -439,6 +530,9 @@ func (l *List) UnmarshalJSON(b []byte) error {
func (l *List) UnmarshalValue(value interface{}) (err error) {
l.mu.Lock()
defer l.mu.Unlock()
if l.list == nil {
l.list = list.New()
}
var array []interface{}
switch value.(type) {
case string, []byte:

View File

@ -10,6 +10,7 @@ import (
"container/list"
"fmt"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/glist"
)
@ -96,5 +97,30 @@ func Example_iterate() {
//10987654321
//12345678910
//10987654321
//[1,2,3,4,5,"M",7,8,9,10]
//[1,2,3,4,5,M,7,8,9,10]
}
func Example_popItem() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopBack())
fmt.Println(l.PopBacks(2))
fmt.Println(l.PopFront())
fmt.Println(l.PopFronts(2))
// Output:
// 9
// [8 7]
// 1
// [2 3]
}
func Example_join() {
var l glist.List
l.PushBacks(g.Slice{"a", "b", "c", "d"})
fmt.Println(l.Join(","))
// Output:
// a,b,c,d
}

View File

@ -15,7 +15,6 @@ import (
"testing"
)
// 检查链表长度
func checkListLen(t *gtest.T, l *List, len int) bool {
if n := l.Len(); n != len {
t.Errorf("l.Len() = %d, want %d", n, len)
@ -24,7 +23,6 @@ func checkListLen(t *gtest.T, l *List, len int) bool {
return true
}
// 检查指针地址
func checkListPointers(t *gtest.T, l *List, es []*Element) {
if !checkListLen(t, l, len(es)) {
return
@ -605,6 +603,17 @@ func TestList_Removes(t *testing.T) {
})
}
func TestList_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
t.Assert(l.PopBack(), 9)
t.Assert(l.PopBacks(2), []interface{}{8, 7})
t.Assert(l.PopFront(), 1)
t.Assert(l.PopFronts(2), []interface{}{2, 3})
})
}
func TestList_Clear(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := New()
@ -672,15 +681,15 @@ func TestList_Iterator(t *testing.T) {
func TestList_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
t.Assert(l.Join(","), `1,2,"a","\"b\"","\\c"`)
t.Assert(l.Join("."), `1.2."a"."\"b\""."\\c"`)
t.Assert(l.Join(","), `1,2,a,"b",\c`)
t.Assert(l.Join("."), `1.2.a."b".\c`)
})
}
func TestList_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
t.Assert(l.String(), `[1,2,"a","\"b\"","\\c"]`)
t.Assert(l.String(), `[1,2,a,"b",\c]`)
})
}

View File

@ -4,7 +4,7 @@
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
// Package gmap provides concurrent-safe/unsafe map containers.
// Package gmap provides most commonly used map container which also support concurrent-safe/unsafe switch feature.
package gmap
type (

View File

@ -98,6 +98,7 @@ func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *AnyAnyMap) FilterEmpty() {
m.mu.Lock()
defer m.mu.Unlock()
@ -108,6 +109,17 @@ func (m *AnyAnyMap) FilterEmpty() {
}
}
// FilterNil deletes all key-value pair of which the value is nil.
func (m *AnyAnyMap) FilterNil() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsNil(v) {
delete(m.data, k)
}
}
}
// Set sets key-value to the hash map.
func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
m.mu.Lock()

View File

@ -98,6 +98,7 @@ func (m *IntAnyMap) MapCopy() map[int]interface{} {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *IntAnyMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {
@ -108,6 +109,17 @@ func (m *IntAnyMap) FilterEmpty() {
m.mu.Unlock()
}
// FilterNil deletes all key-value pair of which the value is nil.
func (m *IntAnyMap) FilterNil() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsNil(v) {
delete(m.data, k)
}
}
}
// Set sets key-value to the hash map.
func (m *IntAnyMap) Set(key int, val interface{}) {
m.mu.Lock()

View File

@ -96,6 +96,7 @@ func (m *IntIntMap) MapCopy() map[int]int {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *IntIntMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {

View File

@ -96,6 +96,7 @@ func (m *IntStrMap) MapCopy() map[int]string {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *IntStrMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {

View File

@ -92,6 +92,7 @@ func (m *StrAnyMap) MapCopy() map[string]interface{} {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *StrAnyMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {
@ -102,6 +103,17 @@ func (m *StrAnyMap) FilterEmpty() {
m.mu.Unlock()
}
// FilterNil deletes all key-value pair of which the value is nil.
func (m *StrAnyMap) FilterNil() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsNil(v) {
delete(m.data, k)
}
}
}
// Set sets key-value to the hash map.
func (m *StrAnyMap) Set(key string, val interface{}) {
m.mu.Lock()

View File

@ -96,6 +96,7 @@ func (m *StrIntMap) MapCopy() map[string]int {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *StrIntMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {

View File

@ -97,6 +97,7 @@ func (m *StrStrMap) MapCopy() map[string]string {
}
// FilterEmpty deletes all key-value pair of which the value is empty.
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
func (m *StrStrMap) FilterEmpty() {
m.mu.Lock()
for k, v := range m.data {

View File

@ -1,7 +1,14 @@
// Copyright 2019 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/gmap"
)
@ -59,13 +66,97 @@ func Example_normalBasic() {
m.Clear()
fmt.Println(m.Size())
}
func Example_keysValues() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Keys())
fmt.Println(m.Values())
// May Output:
// [k1 k2 k3 k4]
// [v2 v3 v4 v1]
}
func Example_flip() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
})
m.Flip()
fmt.Println(m.Map())
// May Output:
// map[v1:k1 v2:k2]
}
func Example_pop() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Pop())
fmt.Println(m.Pops(2))
fmt.Println(m.Size())
// May Output:
// k1 v1
// map[k2:v2 k4:v4]
// 1
}
func Example_filter() {
m1 := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m2 := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m1.FilterEmpty()
m2.FilterNil()
fmt.Println(m1.Map())
fmt.Println(m2.Map())
// Output:
// map[k4:1]
// map[k1: k3:0 k4:1]
}
func Example_setIfNotExist() {
var m gmap.Map
fmt.Println(m.SetIfNotExist("k1", "v1"))
fmt.Println(m.SetIfNotExist("k1", "v1"))
fmt.Println(m.Map())
// Output:
// true
// false
// map[k1:v1]
}
func Example_normalMerge() {
m1 := gmap.New()
m2 := gmap.New()
var m1, m2 gmap.Map
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
m1.Merge(&m2)
fmt.Println(m1.Map())
// May Output:
// map[key1:val1 key2:val2]
}

View File

@ -9,12 +9,12 @@ package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"testing"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/test/gtest"
"time"
)
func Test_AnyAnyMap_Var(t *testing.T) {
@ -218,6 +218,18 @@ func Test_AnyAnyMap_FilterEmpty(t *testing.T) {
t.Assert(m.Get(1), nil)
t.Assert(m.Get(2), 2)
})
gtest.C(t, func(t *gtest.T) {
m := gmap.NewAnyAnyMap()
m.Set(1, 0)
m.Set("time1", time.Time{})
m.Set("time2", time.Now())
t.Assert(m.Get(1), 0)
t.Assert(m.Get("time1"), time.Time{})
m.FilterEmpty()
t.Assert(m.Get(1), nil)
t.Assert(m.Get("time1"), nil)
t.AssertNE(m.Get("time2"), nil)
})
}
func Test_AnyAnyMap_Json(t *testing.T) {

View File

@ -61,12 +61,12 @@ func (set *Set) Iterator(f func(v interface{}) bool) {
}
// Add adds one or multiple items to the set.
func (set *Set) Add(item ...interface{}) {
func (set *Set) Add(items ...interface{}) {
set.mu.Lock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
for _, v := range item {
for _, v := range items {
set.data[v] = struct{}{}
}
set.mu.Unlock()
@ -452,6 +452,18 @@ func (set *Set) Pops(size int) []interface{} {
return array
}
// Walk applies a user supplied function <f> to every item of set.
func (set *Set) Walk(f func(item interface{}) interface{}) *Set {
set.mu.Lock()
defer set.mu.Unlock()
m := make(map[interface{}]struct{}, len(set.data))
for k, v := range set.data {
m[f(k)] = v
}
set.data = m
return set
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (set *Set) MarshalJSON() ([]byte, error) {
return json.Marshal(set.Slice())

View File

@ -412,6 +412,18 @@ func (set *IntSet) Pops(size int) []int {
return array
}
// Walk applies a user supplied function <f> to every item of set.
func (set *IntSet) Walk(f func(item int) int) *IntSet {
set.mu.Lock()
defer set.mu.Unlock()
m := make(map[int]struct{}, len(set.data))
for k, v := range set.data {
m[f(k)] = v
}
set.data = m
return set
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (set *IntSet) MarshalJSON() ([]byte, error) {
return json.Marshal(set.Slice())

View File

@ -13,6 +13,7 @@ import (
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"strings"
)
type StrSet struct {
@ -139,6 +140,19 @@ func (set *StrSet) Contains(item string) bool {
return ok
}
// ContainsI checks whether a value exists in the set with case-insensitively.
// Note that it internally iterates the whole set to do the comparison with case-insensitively.
func (set *StrSet) ContainsI(item string) bool {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.data {
if strings.EqualFold(k, item) {
return true
}
}
return false
}
// Remove deletes <item> from set.
func (set *StrSet) Remove(item string) {
set.mu.Lock()
@ -426,6 +440,18 @@ func (set *StrSet) Pops(size int) []string {
return array
}
// Walk applies a user supplied function <f> to every item of set.
func (set *StrSet) Walk(f func(item string) string) *StrSet {
set.mu.Lock()
defer set.mu.Unlock()
m := make(map[string]struct{}, len(set.data))
for k, v := range set.data {
m[f(k)] = v
}
set.data = m
return set
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (set *StrSet) MarshalJSON() ([]byte, error) {
return json.Marshal(set.Slice())

View File

@ -0,0 +1,119 @@
// Copyright 2020 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 gm file,
// You can obtain one at https://github.com/gogf/gf.
package gset_test
import (
"fmt"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/frame/g"
)
func Example_intersectDiffUnionComplement() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
fmt.Println(s3.Intersect(s1).Slice())
fmt.Println(s3.Diff(s1).Slice())
fmt.Println(s1.Union(s2).Slice())
fmt.Println(s1.Complement(s3).Slice())
// May Output:
// [2 3 1]
// [5 6 7 4]
// [6 1 2 3 4 5]
// [4 5 6 7]
}
func Example_isSubsetOf() {
var s1, s2 gset.Set
s1.Add(g.Slice{1, 2, 3}...)
s2.Add(g.Slice{2, 3}...)
fmt.Println(s1.IsSubsetOf(&s2))
fmt.Println(s2.IsSubsetOf(&s1))
// Output:
// false
// true
}
func Example_addIfNotExist() {
var set gset.Set
fmt.Println(set.AddIfNotExist(1))
fmt.Println(set.AddIfNotExist(1))
fmt.Println(set.Slice())
// Output:
// true
// false
// [1]
}
func Example_pop() {
var set gset.Set
set.Add(1, 2, 3, 4)
fmt.Println(set.Pop())
fmt.Println(set.Pops(2))
fmt.Println(set.Size())
// May Output:
// 1
// [2 3]
// 1
}
func Example_join() {
var set gset.Set
set.Add("a", "b", "c", "d")
fmt.Println(set.Join(","))
// May Output:
// a,b,c,d
}
func Example_contains() {
var set gset.StrSet
set.Add("a")
fmt.Println(set.Contains("a"))
fmt.Println(set.Contains("A"))
fmt.Println(set.ContainsI("A"))
// Output:
// true
// false
// true
}
func Example_Contains() {
var set gset.StrSet
set.Add("a")
fmt.Println(set.Contains("a"))
fmt.Println(set.Contains("A"))
fmt.Println(set.ContainsI("A"))
// Output:
// true
// false
// true
}
func Example_walk() {
var (
set gset.StrSet
names = g.SliceStr{"user", "user_detail"}
prefix = "gf_"
)
set.Add(names...)
// Add prefix for given table names.
set.Walk(func(item string) string {
return prefix + item
})
fmt.Println(set.Slice())
// May Output:
// [gf_user gf_user_detail]
}

View File

@ -389,6 +389,19 @@ func TestSet_AddIfNotExistFunc(t *testing.T) {
})
}
func TestSet_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var set gset.Set
set.Add(g.Slice{1, 2}...)
set.Walk(func(item interface{}) interface{} {
return gconv.Int(item) + 10
})
t.Assert(set.Size(), 2)
t.Assert(set.Contains(11), true)
t.Assert(set.Contains(12), true)
})
}
func TestSet_AddIfNotExistFuncLock(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)

View File

@ -377,6 +377,19 @@ func TestIntSet_Json(t *testing.T) {
})
}
func TestIntSet_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var set gset.IntSet
set.Add(g.SliceInt{1, 2}...)
set.Walk(func(item int) int {
return item + 10
})
t.Assert(set.Size(), 2)
t.Assert(set.Contains(11), true)
t.Assert(set.Contains(12), true)
})
}
func TestIntSet_UnmarshalValue(t *testing.T) {
type V struct {
Name string

View File

@ -62,6 +62,16 @@ func TestStrSet_Basic(t *testing.T) {
})
}
func TestStrSet_ContainsI(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("a", "b", "C")
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
t.Assert(s.ContainsI("A"), true)
})
}
func TestStrSet_Iterator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
@ -413,6 +423,24 @@ func TestStrSet_Json(t *testing.T) {
})
}
func TestStrSet_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
set gset.StrSet
names = g.SliceStr{"user", "user_detail"}
prefix = "gf_"
)
set.Add(names...)
// Add prefix for given table names.
set.Walk(func(item string) string {
return prefix + item
})
t.Assert(set.Size(), 2)
t.Assert(set.Contains("gf_user"), true)
t.Assert(set.Contains("gf_user_detail"), true)
})
}
func TestStrSet_UnmarshalValue(t *testing.T) {
type V struct {
Name string

View File

@ -235,100 +235,6 @@ func (v *Var) GTime(format ...string) *gtime.Time {
return gconv.GTime(v.Val(), format...)
}
// Map converts <v> to map[string]interface{}.
func (v *Var) Map(tags ...string) map[string]interface{} {
return gconv.Map(v.Val(), tags...)
}
// MapStrStr converts <v> to map[string]string.
func (v *Var) MapStrStr(tags ...string) map[string]string {
return gconv.MapStrStr(v.Val(), tags...)
}
// MapStrVar converts <v> to map[string]*Var.
func (v *Var) MapStrVar(tags ...string) map[string]*Var {
m := v.Map(tags...)
if len(m) > 0 {
vMap := make(map[string]*Var)
for k, v := range m {
vMap[k] = New(v)
}
return vMap
}
return nil
}
// MapDeep converts <v> to map[string]interface{} recursively.
func (v *Var) MapDeep(tags ...string) map[string]interface{} {
return gconv.MapDeep(v.Val(), tags...)
}
// MapDeep converts <v> to map[string]string recursively.
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
return gconv.MapStrStrDeep(v.Val(), tags...)
}
// MapStrVarDeep converts <v> to map[string]*Var recursively.
func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
m := v.MapDeep(tags...)
if len(m) > 0 {
vMap := make(map[string]*Var)
for k, v := range m {
vMap[k] = New(v)
}
return vMap
}
return nil
}
// Struct maps value of <v> to <pointer>.
// The parameter <pointer> should be a pointer to a struct instance.
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
return gconv.Struct(v.Val(), pointer, mapping...)
}
// Struct maps value of <v> to <pointer> recursively.
// The parameter <pointer> should be a pointer to a struct instance.
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error {
return gconv.StructDeep(v.Val(), pointer, mapping...)
}
// Structs converts <v> to given struct slice.
func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.Structs(v.Val(), pointer, mapping...)
}
// StructsDeep converts <v> to given struct slice recursively.
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.StructsDeep(v.Val(), pointer, mapping...)
}
// MapToMap converts map type variable <params> to another map type variable <pointer>.
// The elements of <pointer> should be type of struct/*struct.
func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMap(v.Val(), pointer, mapping...)
}
// MapToMapDeep recursively converts map type variable <params> to another map type variable <pointer>.
// The elements of <pointer> should be type of struct/*struct.
func (v *Var) MapToMapDeep(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMapDeep(v.Val(), pointer, mapping...)
}
// MapToMaps converts map type variable <params> to another map type variable <pointer>.
// The elements of <pointer> should be type of []struct/[]*struct.
func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMaps(v.Val(), pointer, mapping...)
}
// MapToMapsDeep recursively converts map type variable <params> to another map type variable <pointer>.
// The elements of <pointer> should be type of []struct/[]*struct.
func (v *Var) MapToMapsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMapsDeep(v.Val(), pointer, mapping...)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (v *Var) MarshalJSON() ([]byte, error) {
return json.Marshal(v.Val())

View File

@ -0,0 +1,87 @@
// Copyright 2020 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 gvar
import "github.com/gogf/gf/util/gconv"
// Map converts and returns <v> as map[string]interface{}.
func (v *Var) Map(tags ...string) map[string]interface{} {
return gconv.Map(v.Val(), tags...)
}
// MapStrStr converts and returns <v> as map[string]string.
func (v *Var) MapStrStr(tags ...string) map[string]string {
return gconv.MapStrStr(v.Val(), tags...)
}
// MapStrVar converts and returns <v> as map[string]*Var.
func (v *Var) MapStrVar(tags ...string) map[string]*Var {
m := v.Map(tags...)
if len(m) > 0 {
vMap := make(map[string]*Var, len(m))
for k, v := range m {
vMap[k] = New(v)
}
return vMap
}
return nil
}
// MapDeep converts and returns <v> as map[string]interface{} recursively.
func (v *Var) MapDeep(tags ...string) map[string]interface{} {
return gconv.MapDeep(v.Val(), tags...)
}
// MapDeep converts and returns <v> as map[string]string recursively.
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
return gconv.MapStrStrDeep(v.Val(), tags...)
}
// MapStrVarDeep converts and returns <v> as map[string]*Var recursively.
func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
m := v.MapDeep(tags...)
if len(m) > 0 {
vMap := make(map[string]*Var, len(m))
for k, v := range m {
vMap[k] = New(v)
}
return vMap
}
return nil
}
// Maps converts and returns <v> as map[string]string.
// See gconv.Maps.
func (v *Var) Maps(tags ...string) []map[string]interface{} {
return gconv.Maps(v.Val(), tags...)
}
// MapToMap converts any map type variable <params> to another map type variable <pointer>.
// See gconv.MapToMap.
func (v *Var) MapToMap(pointer interface{}) (err error) {
return gconv.MapToMap(v.Val(), pointer)
}
// MapToMapDeep converts any map type variable <params> to another map type variable
// <pointer> recursively.
// See gconv.MapToMapDeep.
func (v *Var) MapToMapDeep(pointer interface{}) (err error) {
return gconv.MapToMapDeep(v.Val(), pointer)
}
// MapToMaps converts any map type variable <params> to another map type variable <pointer>.
// See gconv.MapToMaps.
func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMaps(v.Val(), pointer, mapping...)
}
// MapToMapsDeep converts any map type variable <params> to another map type variable
// <pointer> recursively.
// See gconv.MapToMapsDeep.
func (v *Var) MapToMapsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMapsDeep(v.Val(), pointer, mapping...)
}

View File

@ -0,0 +1,33 @@
// Copyright 2020 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 gvar
import "github.com/gogf/gf/util/gconv"
// Struct maps value of <v> to <pointer>.
// The parameter <pointer> should be a pointer to a struct instance.
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
return gconv.Struct(v.Val(), pointer, mapping...)
}
// Struct maps value of <v> to <pointer> recursively.
// The parameter <pointer> should be a pointer to a struct instance.
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error {
return gconv.StructDeep(v.Val(), pointer, mapping...)
}
// Structs converts and returns <v> as given struct slice.
func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.Structs(v.Val(), pointer, mapping...)
}
// StructsDeep converts and returns <v> as given struct slice recursively.
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.StructsDeep(v.Val(), pointer, mapping...)
}

View File

@ -9,14 +9,10 @@ package gvar_test
import (
"bytes"
"encoding/binary"
"encoding/json"
"github.com/gogf/gf/util/gconv"
"math"
"testing"
"time"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/test/gtest"
)
@ -305,90 +301,6 @@ func Test_Duration(t *testing.T) {
})
}
func Test_Map(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := g.Map{
"k1": "v1",
"k2": "v2",
}
objOne := gvar.New(m, true)
t.Assert(objOne.Map()["k1"], m["k1"])
t.Assert(objOne.Map()["k2"], m["k2"])
})
}
func Test_Struct(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type StTest struct {
Test int
}
Kv := make(map[string]int, 1)
Kv["Test"] = 100
testObj := &StTest{}
objOne := gvar.New(Kv, true)
objOne.Struct(testObj)
t.Assert(testObj.Test, Kv["Test"])
})
gtest.C(t, func(t *gtest.T) {
type StTest struct {
Test int8
}
o := &StTest{}
v := gvar.New(g.Slice{"Test", "-25"})
v.Struct(o)
t.Assert(o.Test, -25)
})
}
func Test_Json(t *testing.T) {
// Marshal
gtest.C(t, func(t *gtest.T) {
s := "i love gf"
v := gvar.New(s)
b1, err1 := json.Marshal(v)
b2, err2 := json.Marshal(s)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
gtest.C(t, func(t *gtest.T) {
s := int64(math.MaxInt64)
v := gvar.New(s)
b1, err1 := json.Marshal(v)
b2, err2 := json.Marshal(s)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
// Unmarshal
gtest.C(t, func(t *gtest.T) {
s := "i love gf"
v := gvar.New(nil)
b, err := json.Marshal(s)
t.Assert(err, nil)
err = json.Unmarshal(b, v)
t.Assert(err, nil)
t.Assert(v.String(), s)
})
gtest.C(t, func(t *gtest.T) {
var v gvar.Var
s := "i love gf"
b, err := json.Marshal(s)
t.Assert(err, nil)
err = json.Unmarshal(b, &v)
t.Assert(err, nil)
t.Assert(v.String(), s)
})
}
func Test_UnmarshalValue(t *testing.T) {
type V struct {
Name string

View File

@ -0,0 +1,59 @@
// Copyright 2020 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 gvar_test
import (
"encoding/json"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/test/gtest"
"math"
"testing"
)
func Test_Json(t *testing.T) {
// Marshal
gtest.C(t, func(t *gtest.T) {
s := "i love gf"
v := gvar.New(s)
b1, err1 := json.Marshal(v)
b2, err2 := json.Marshal(s)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
gtest.C(t, func(t *gtest.T) {
s := int64(math.MaxInt64)
v := gvar.New(s)
b1, err1 := json.Marshal(v)
b2, err2 := json.Marshal(s)
t.Assert(err1, err2)
t.Assert(b1, b2)
})
// Unmarshal
gtest.C(t, func(t *gtest.T) {
s := "i love gf"
v := gvar.New(nil)
b, err := json.Marshal(s)
t.Assert(err, nil)
err = json.Unmarshal(b, v)
t.Assert(err, nil)
t.Assert(v.String(), s)
})
gtest.C(t, func(t *gtest.T) {
var v gvar.Var
s := "i love gf"
b, err := json.Marshal(s)
t.Assert(err, nil)
err = json.Unmarshal(b, &v)
t.Assert(err, nil)
t.Assert(v.String(), s)
})
}

View File

@ -0,0 +1,26 @@
// Copyright 2020 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 gvar_test
import (
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/test/gtest"
"testing"
)
func Test_Map(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := g.Map{
"k1": "v1",
"k2": "v2",
}
objOne := gvar.New(m, true)
t.Assert(objOne.Map()["k1"], m["k1"])
t.Assert(objOne.Map()["k2"], m["k2"])
})
}

View File

@ -0,0 +1,69 @@
// Copyright 2020 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 gvar_test
import (
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/test/gtest"
"testing"
)
func Test_MapToMap(t *testing.T) {
// map[int]int -> map[string]string
// empty original map.
gtest.C(t, func(t *gtest.T) {
m1 := g.MapIntInt{}
m2 := g.MapStrStr{}
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
t.Assert(len(m1), len(m2))
})
// map[int]int -> map[string]string
gtest.C(t, func(t *gtest.T) {
m1 := g.MapIntInt{
1: 100,
2: 200,
}
m2 := g.MapStrStr{}
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
t.Assert(m2["1"], m1[1])
t.Assert(m2["2"], m1[2])
})
// map[string]interface{} -> map[string]string
gtest.C(t, func(t *gtest.T) {
m1 := g.Map{
"k1": "v1",
"k2": "v2",
}
m2 := g.MapStrStr{}
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
t.Assert(m2["k1"], m1["k1"])
t.Assert(m2["k2"], m1["k2"])
})
// map[string]string -> map[string]interface{}
gtest.C(t, func(t *gtest.T) {
m1 := g.MapStrStr{
"k1": "v1",
"k2": "v2",
}
m2 := g.Map{}
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
t.Assert(m2["k1"], m1["k1"])
t.Assert(m2["k2"], m1["k2"])
})
// map[string]interface{} -> map[interface{}]interface{}
gtest.C(t, func(t *gtest.T) {
m1 := g.MapStrStr{
"k1": "v1",
"k2": "v2",
}
m2 := g.MapAnyAny{}
t.Assert(gvar.New(m1).MapToMap(&m2), nil)
t.Assert(m2["k1"], m1["k1"])
t.Assert(m2["k2"], m1["k2"])
})
}

View File

@ -0,0 +1,42 @@
// Copyright 2020 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 gvar_test
import (
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/test/gtest"
"testing"
)
func Test_Struct(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type StTest struct {
Test int
}
Kv := make(map[string]int, 1)
Kv["Test"] = 100
testObj := &StTest{}
objOne := gvar.New(Kv, true)
objOne.Struct(testObj)
t.Assert(testObj.Test, Kv["Test"])
})
gtest.C(t, func(t *gtest.T) {
type StTest struct {
Test int8
}
o := &StTest{}
v := gvar.New(g.Slice{"Test", "-25"})
v.Struct(o)
t.Assert(o.Test, -25)
})
}

View File

@ -64,6 +64,7 @@ type DB interface {
// Transaction.
Begin() (*TX, error)
Transaction(f func(tx *TX) error) (err error)
Insert(table string, data interface{}, batch ...int) (sql.Result, error)
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error)

View File

@ -203,9 +203,6 @@ func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) e
if err != nil {
return err
}
if len(one) == 0 {
return ErrNoRows
}
return one.Struct(pointer)
}
@ -216,9 +213,6 @@ func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{})
if err != nil {
return err
}
if len(all) == 0 {
return ErrNoRows
}
return all.Structs(pointer)
}
@ -310,6 +304,34 @@ func (c *Core) Begin() (*TX, error) {
}
}
// Transaction wraps the transaction logic using function <f>.
// It rollbacks the transaction and returns the error from function <f> if
// it returns non-nil error. It commits the transaction and returns nil if
// function <f> returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function <f>
// as it is automatically handled by this function.
func (c *Core) Transaction(f func(tx *TX) error) (err error) {
var tx *TX
tx, err = c.DB.Begin()
if err != nil {
return err
}
defer func() {
if err != nil {
if e := tx.Rollback(); e != nil {
err = e
}
} else {
if e := tx.Commit(); e != nil {
err = e
}
}
}()
err = f(tx)
return
}
// Insert does "INSERT INTO ..." statement for the table.
// If there's already one unique record of the data in the table, it returns error.
//
@ -507,7 +529,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
listMap[i] = DataToMapDeep(rv.Index(i).Interface())
}
case reflect.Map, reflect.Struct:
listMap = List{DataToMapDeep(list)}
listMap = List{DataToMapDeep(v)}
default:
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
}
@ -712,9 +734,11 @@ func (c *Core) rowsToResult(rows *sql.Rows) (Result, error) {
columnTypes[k] = v.DatabaseTypeName()
columnNames[k] = v.Name()
}
values := make([]sql.RawBytes, len(columnNames))
records := make(Result, 0)
scanArgs := make([]interface{}, len(values))
var (
values = make([]sql.RawBytes, len(columnNames))
records = make(Result, 0)
scanArgs = make([]interface{}, len(values))
)
for i := range values {
scanArgs[i] = &values[i]
}

View File

@ -39,10 +39,10 @@ type ConfigNode struct {
DryRun bool // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
Weight int // (Optional) Weight for load balance calculating, it's useless if there's just one node.
Charset string // (Optional, "utf8mb4" in default) Custom charset when operating on database.
LinkInfo string // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
MaxIdleConnCount int // (Optional) Max idle connection configuration for underlying connection pool.
MaxOpenConnCount int // (Optional) Max open connection configuration for underlying connection pool.
MaxConnLifetime time.Duration // (Optional) Max connection TTL configuration for underlying connection pool.
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
MaxIdleConnCount int `json:"maxidle"` // (Optional) Max idle connection configuration for underlying connection pool.
MaxOpenConnCount int `json:"maxopen"` // (Optional) Max open connection configuration for underlying connection pool.
MaxConnLifetime time.Duration `json:"maxlifetime"` // (Optional) Max connection TTL configuration for underlying connection pool.
}
// configs is internal used configuration object.

View File

@ -83,12 +83,15 @@ func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) {
return
}
// TableFields retrieves and returns the fields information of specified table of current schema.
// TableFields retrieves and returns the fields information of specified table of current
// schema.
//
// Note that it returns a map containing the field name and its corresponding fields.
// As a map is unsorted, the TableField struct has a "Index" field marks its sequence in the fields.
// As a map is unsorted, the TableField struct has a "Index" field marks its sequence in
// the fields.
//
// It's using cache feature to enhance the performance, which is never expired util the process restarts.
// It's using cache feature to enhance the performance, which is never expired util the
// process restarts.
func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)

View File

@ -206,15 +206,16 @@ func GetPrimaryKey(pointer interface{}) string {
// GetPrimaryKeyCondition returns a new where condition by primary field name.
// The optional parameter <where> is like follows:
// 123
// []int{1, 2, 3}
// "john"
// []string{"john", "smith"}
// g.Map{"id": g.Slice{1,2,3}}
// g.Map{"id": 1, "name": "john"}
// 123 => primary=123
// []int{1, 2, 3} => primary IN(1,2,3)
// "john" => primary='john'
// []string{"john", "smith"} => primary IN('john','smith')
// g.Map{"id": g.Slice{1,2,3}} => id IN(1,2,3)
// g.Map{"id": 1, "name": "john"} => id=1 AND name='john'
// etc.
//
// Note that it returns the given <where> parameter directly if there's the <primary> is empty.
// Note that it returns the given <where> parameter directly if the <primary> is empty
// or length of <where> > 1.
func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondition []interface{}) {
if len(where) == 0 {
return nil
@ -231,6 +232,7 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi
}
switch kind {
case reflect.Map, reflect.Struct:
// Ignore the parameter <primary>.
break
default:
@ -246,6 +248,9 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi
// The internal handleArguments function might be called twice during the SQL procedure,
// but do not worry about it, it's safe and efficient.
func formatSql(sql string, args []interface{}) (newQuery string, newArgs []interface{}) {
sql = gstr.Trim(sql)
sql = gstr.Replace(sql, "\n", " ")
sql, _ = gregex.ReplaceString(`\s{2,}`, ` `, sql)
return handleArguments(sql, args)
}
@ -406,11 +411,15 @@ func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key
// underlying driver.
func handleArguments(sql string, args []interface{}) (newSql string, newArgs []interface{}) {
newSql = sql
// insertHolderCount is used to calculate the inserting position for the '?' holder.
insertHolderCount := 0
// Handles the slice arguments.
if len(args) > 0 {
for index, arg := range args {
rv := reflect.ValueOf(arg)
kind := rv.Kind()
var (
rv = reflect.ValueOf(arg)
kind = rv.Kind()
)
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
@ -426,17 +435,25 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
for i := 0; i < rv.Len(); i++ {
newArgs = append(newArgs, rv.Index(i).Interface())
}
// It the '?' holder count equals the length of the slice,
// If the '?' holder count equals the length of the slice,
// it does not implement the arguments splitting logic.
// Eg: db.Query("SELECT ?+?", g.Slice{1, 2})
if len(args) == 1 && gstr.Count(newSql, "?") == rv.Len() {
break
}
// counter is used to finding the inserting position for the '?' holder.
counter := 0
var (
counter = 0
replaced = false
)
newSql, _ = gregex.ReplaceStringFunc(`\?`, newSql, func(s string) string {
if replaced {
return s
}
counter++
if counter == index+1 {
if counter == index+insertHolderCount+1 {
replaced = true
insertHolderCount += rv.Len() - 1
return "?" + strings.Repeat(",?", rv.Len()-1)
}
return s

View File

@ -18,19 +18,26 @@ import (
// If the parameter <duration> = 0, which means it never expires.
// If the parameter <duration> > 0, which means it expires after <duration>.
//
// The optional parameter <name> is used to bind a name to the cache, which means you can later
// control the cache like changing the <duration> or clearing the cache with specified <name>.
// The optional parameter <name> is used to bind a name to the cache, which means you can
// later control the cache like changing the <duration> or clearing the cache with specified
// <name>.
//
// Note that, the cache feature is disabled if the model is operating on a transaction.
// Note that, the cache feature is disabled if the model is performing select statement
// on a transaction.
func (m *Model) Cache(duration time.Duration, name ...string) *Model {
model := m.getModel()
model.cacheDuration = duration
if len(name) > 0 {
model.cacheName = name[0]
}
// It does not support cache on transaction.
if model.tx == nil {
model.cacheEnabled = true
}
model.cacheEnabled = true
return model
}
// checkAndRemoveCache checks and removes the cache in insert/update/delete statement if
// cache feature is enabled.
func (m *Model) checkAndRemoveCache() {
if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 {
m.db.GetCache().Remove(m.cacheName)
}
}

View File

@ -6,7 +6,9 @@
package gdb
import "github.com/gogf/gf/util/gconv"
import (
"strings"
)
// Where sets the condition statement for the model. The parameter <where> can be type of
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
@ -46,7 +48,8 @@ func (m *Model) Having(having interface{}, args ...interface{}) *Model {
// WherePri does the same logic as Model.Where except that if the parameter <where>
// is a single condition like int/string/float/slice, it treats the condition as the primary
// key value. That is, if primary key is "id" and given <where> parameter as "123", the
// WherePri function treats it as "id=123", but Model.Where treats it as string "123".
// WherePri function treats the condition as "id=123", but Model.Where treats the condition
// as string "123".
func (m *Model) WherePri(where interface{}, args ...interface{}) *Model {
if len(args) > 0 {
return m.Where(where, args...)
@ -98,9 +101,9 @@ func (m *Model) GroupBy(groupBy string) *Model {
}
// Order sets the "ORDER BY" statement for the model.
func (m *Model) Order(orderBy string) *Model {
func (m *Model) Order(orderBy ...string) *Model {
model := m.getModel()
model.orderBy = m.db.QuoteString(orderBy)
model.orderBy = m.db.QuoteString(strings.Join(orderBy, " "))
return model
}
@ -154,28 +157,3 @@ func (m *Model) Page(page, limit int) *Model {
func (m *Model) ForPage(page, limit int) *Model {
return m.Page(page, limit)
}
// getAll does the query from database.
func (m *Model) getAll(sql string, args ...interface{}) (result Result, err error) {
cacheKey := ""
// Retrieve from cache.
if m.cacheEnabled {
cacheKey = m.cacheName
if len(cacheKey) == 0 {
cacheKey = sql + "/" + gconv.String(args)
}
if v := m.db.GetCache().Get(cacheKey); v != nil {
return v.(Result), nil
}
}
result, err = m.db.DoGetAll(m.getLink(false), sql, m.mergeArguments(args)...)
// Cache the result.
if len(cacheKey) > 0 && err == nil {
if m.cacheDuration < 0 {
m.db.GetCache().Remove(cacheKey)
} else {
m.db.GetCache().Set(cacheKey, result, m.cacheDuration)
}
}
return result, err
}

View File

@ -12,14 +12,10 @@ import (
"github.com/gogf/gf/os/gtime"
)
// Unscoped enables/disables the soft deleting feature.
func (m *Model) Unscoped(unscoped ...bool) *Model {
// Unscoped disables the soft deleting feature.
func (m *Model) Unscoped() *Model {
model := m.getModel()
if len(unscoped) > 0 {
model.unscoped = unscoped[0]
} else {
model.unscoped = true
}
model.unscoped = true
return model
}

View File

@ -7,12 +7,13 @@
package gdb
import (
"github.com/gogf/gf/container/garray"
"fmt"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/text/gstr"
)
// Filter marks filtering the fields which does not exist in the fields of the operated table.
// Note that this function supports only single table operations.
func (m *Model) Filter() *Model {
if gstr.Contains(m.tables, " ") {
panic("function Filter supports only single table operations")
@ -30,25 +31,36 @@ func (m *Model) Fields(fields string) *Model {
}
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
// Note that this function supports only single table operations.
func (m *Model) FieldsEx(fields string) *Model {
if gstr.Contains(m.tables, " ") {
panic("function FieldsEx supports only single table operations")
}
tableFields, err := m.db.TableFields(m.tables)
if err != nil {
panic(err)
}
if len(tableFields) == 0 {
panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
}
model := m.getModel()
model.fieldsEx = fields
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
if m, err := m.db.TableFields(m.tables); err == nil {
model.fields = ""
for k, _ := range m {
if fieldsExSet.Contains(k) {
continue
}
if len(model.fields) > 0 {
model.fields += ","
}
model.fields += k
}
fieldsArray := make([]string, len(tableFields))
for k, v := range tableFields {
fieldsArray[v.Index] = k
}
model.fields = ""
for _, k := range fieldsArray {
if fieldsExSet.Contains(k) {
continue
}
if len(model.fields) > 0 {
model.fields += ","
}
model.fields += k
}
model.fields = model.db.QuoteString(model.fields)
return model
}
@ -59,14 +71,26 @@ func (m *Model) FieldsStr(prefix ...string) string {
if len(prefix) > 0 {
prefixStr = prefix[0]
}
if m, err := m.db.TableFields(m.tables); err == nil {
fieldsArray := garray.NewStrArraySize(len(m), len(m))
for _, field := range m {
fieldsArray.Set(field.Index, prefixStr+field.Name)
}
return fieldsArray.Join(",")
tableFields, err := m.db.TableFields(m.tables)
if err != nil {
panic(err)
}
return ""
if len(tableFields) == 0 {
panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
}
fieldsArray := make([]string, len(tableFields))
for k, v := range tableFields {
fieldsArray[v.Index] = k
}
newFields := ""
for _, k := range fieldsArray {
if len(newFields) > 0 {
newFields += ","
}
newFields += prefixStr + k
}
newFields = m.db.QuoteString(newFields)
return newFields
}
// FieldsExStr retrieves and returns fields which are not in parameter <fields> from the table,
@ -78,17 +102,28 @@ func (m *Model) FieldsExStr(fields string, prefix ...string) string {
if len(prefix) > 0 {
prefixStr = prefix[0]
}
if m, err := m.db.TableFields(m.tables); err == nil {
fieldsArray := garray.NewStrArraySize(len(m), len(m))
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
for _, field := range m {
if fieldsExSet.Contains(field.Name) {
continue
}
fieldsArray.Set(field.Index, prefixStr+field.Name)
}
fieldsArray.FilterEmpty()
return fieldsArray.Join(",")
tableFields, err := m.db.TableFields(m.tables)
if err != nil {
panic(err)
}
return ""
if len(tableFields) == 0 {
panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
}
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
fieldsArray := make([]string, len(tableFields))
for k, v := range tableFields {
fieldsArray[v.Index] = k
}
newFields := ""
for _, k := range fieldsArray {
if fieldsExSet.Contains(k) {
continue
}
if len(newFields) > 0 {
newFields += ","
}
newFields += prefixStr + k
}
newFields = m.db.QuoteString(newFields)
return newFields
}

View File

@ -7,7 +7,6 @@
package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/util/gconv"
"reflect"
@ -42,10 +41,12 @@ func (m *Model) All(where ...interface{}) (Result, error) {
}
conditionWhere += softDeletingCondition
}
return m.getAll(
// DO NOT quote the m.fields where, in case of fields like:
// DISTINCT t.user_id uid
return m.doGetAll(
fmt.Sprintf(
"SELECT %s FROM %s%s",
m.db.QuoteString(m.fields),
m.fields,
m.tables,
conditionWhere+conditionExtra,
),
@ -169,9 +170,6 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
if err != nil {
return err
}
if len(one) == 0 {
return sql.ErrNoRows
}
return one.Struct(pointer)
}
@ -196,9 +194,6 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
if err != nil {
return err
}
if len(all) == 0 {
return sql.ErrNoRows
}
return all.Structs(pointer)
}
@ -249,7 +244,9 @@ func (m *Model) Count(where ...interface{}) (int, error) {
}
countFields := "COUNT(1)"
if m.fields != "" && m.fields != "*" {
countFields = fmt.Sprintf(`COUNT(%s)`, m.db.QuoteString(m.fields))
// DO NOT quote the m.fields here, in case of fields like:
// DISTINCT t.user_id uid
countFields = fmt.Sprintf(`COUNT(%s)`, m.fields)
}
var (
softDeletingCondition = m.getConditionForSoftDeleting()
@ -268,7 +265,7 @@ func (m *Model) Count(where ...interface{}) (int, error) {
if len(m.groupBy) > 0 {
s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s)
}
list, err := m.getAll(s, conditionArgs...)
list, err := m.doGetAll(s, conditionArgs...)
if err != nil {
return 0, err
}
@ -340,3 +337,28 @@ func (m *Model) FindScan(pointer interface{}, where ...interface{}) error {
}
return m.Scan(pointer)
}
// doGetAll does the select statement on the database.
func (m *Model) doGetAll(sql string, args ...interface{}) (result Result, err error) {
cacheKey := ""
// Retrieve from cache.
if m.cacheEnabled && m.tx == nil {
cacheKey = m.cacheName
if len(cacheKey) == 0 {
cacheKey = sql + "/" + gconv.String(args)
}
if v := m.db.GetCache().Get(cacheKey); v != nil {
return v.(Result), nil
}
}
result, err = m.db.DoGetAll(m.getLink(false), sql, m.mergeArguments(args)...)
// Cache the result.
if cacheKey != "" && err == nil {
if m.cacheDuration < 0 {
m.db.GetCache().Remove(cacheKey)
} else {
m.db.GetCache().Set(cacheKey, result, m.cacheDuration)
}
}
return result, err
}

View File

@ -25,39 +25,39 @@ const (
// If there's no field name for storing creating time, it returns an empty string.
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
func (m *Model) getSoftFieldNameCreate(table ...string) string {
name := ""
tableName := ""
if len(table) > 0 {
name = table[0]
tableName = table[0]
} else {
name = m.getPrimaryTableName()
tableName = m.getPrimaryTableName()
}
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_CREATE)
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_CREATE)
}
// getSoftFieldNameUpdate checks and returns the field name for record updating time.
// If there's no field name for storing updating time, it returns an empty string.
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
func (m *Model) getSoftFieldNameUpdate(table ...string) (field string) {
name := ""
tableName := ""
if len(table) > 0 {
name = table[0]
tableName = table[0]
} else {
name = m.getPrimaryTableName()
tableName = m.getPrimaryTableName()
}
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_UPDATE)
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_UPDATE)
}
// getSoftFieldNameDelete checks and returns the field name for record deleting time.
// If there's no field name for storing deleting time, it returns an empty string.
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
func (m *Model) getSoftFieldNameDelete(table ...string) (field string) {
name := ""
tableName := ""
if len(table) > 0 {
name = table[0]
tableName = table[0]
} else {
name = m.getPrimaryTableName()
tableName = m.getPrimaryTableName()
}
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_DELETE)
return m.getSoftFieldName(tableName, gSOFT_FIELD_NAME_DELETE)
}
// getSoftFieldName retrieves and returns the field name of the table for possible key.

View File

@ -63,10 +63,18 @@ func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
if r.IsZero() {
continue
}
case *time.Time:
if r.IsZero() {
continue
}
case gtime.Time:
if r.IsZero() {
continue
}
case *gtime.Time:
if r.IsZero() {
continue
}
}
tempMap[k] = v
}
@ -80,6 +88,9 @@ func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
charL, charR = m.db.GetChars()
chars = charL + charR
)
set.Walk(func(item string) string {
return gstr.Trim(item, chars)
})
for k := range data {
k = gstr.Trim(k, chars)
if !set.Contains(k) {
@ -143,13 +154,6 @@ func (m *Model) getPrimaryKey() string {
return ""
}
// checkAndRemoveCache checks and remove the cache if necessary.
func (m *Model) checkAndRemoveCache() {
if m.cacheEnabled && m.cacheDuration < 0 && len(m.cacheName) > 0 {
m.db.GetCache().Remove(m.cacheName)
}
}
// formatCondition formats where arguments of the model and returns a new condition sql and its arguments.
// Note that this function does not change any attribute value of the <m>.
//

View File

@ -25,22 +25,47 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
t = strings.ToLower(t)
switch t {
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
case
"binary",
"varbinary",
"blob",
"tinyblob",
"mediumblob",
"longblob":
return fieldValue
case "int", "tinyint", "small_int", "smallint", "medium_int", "mediumint":
case
"int",
"tinyint",
"small_int",
"smallint",
"medium_int",
"mediumint",
"serial":
if gstr.ContainsI(fieldType, "unsigned") {
gconv.Uint(string(fieldValue))
}
return gconv.Int(string(fieldValue))
case "big_int", "bigint":
case
"big_int",
"bigint",
"bigserial":
if gstr.ContainsI(fieldType, "unsigned") {
gconv.Uint64(string(fieldValue))
}
return gconv.Int64(string(fieldValue))
case "float", "double", "decimal":
case "real":
return gconv.Float32(string(fieldValue))
case
"float",
"double",
"decimal",
"money",
"numeric",
"smallmoney":
return gconv.Float64(string(fieldValue))
case "bit":
@ -61,17 +86,19 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
t, _ := gtime.StrToTime(string(fieldValue))
return t.Format("Y-m-d")
case "datetime", "timestamp":
case
"datetime",
"timestamp":
t, _ := gtime.StrToTime(string(fieldValue))
return t.String()
default:
// Auto detect field type, using key match.
switch {
case strings.Contains(t, "text") || strings.Contains(t, "char"):
case strings.Contains(t, "text") || strings.Contains(t, "char") || strings.Contains(t, "character"):
return string(fieldValue)
case strings.Contains(t, "float") || strings.Contains(t, "double"):
case strings.Contains(t, "float") || strings.Contains(t, "double") || strings.Contains(t, "numeric"):
return gconv.Float64(string(fieldValue))
case strings.Contains(t, "bool"):

View File

@ -8,10 +8,11 @@ package gdb
import (
"database/sql"
"errors"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/encoding/gparser"
"github.com/gogf/gf/util/gconv"
"reflect"
)
// Json converts <r> to JSON format content.
@ -42,10 +43,32 @@ func (r Record) GMap() *gmap.StrAnyMap {
// Struct converts <r> to a struct.
// Note that the parameter <pointer> should be type of *struct/**struct.
//
// Note that it returns sql.ErrNoRows if <r> is empty.
func (r Record) Struct(pointer interface{}) error {
if r == nil {
// If the record is empty, it returns error.
if r.IsEmpty() {
return sql.ErrNoRows
}
// Special handling for parameter type: reflect.Value
if _, ok := pointer.(reflect.Value); ok {
return mapToStruct(r.Map(), pointer)
}
var (
reflectValue = reflect.ValueOf(pointer)
reflectKind = reflectValue.Kind()
)
if reflectKind != reflect.Ptr {
return errors.New("parameter should be type of *struct/**struct")
}
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
if reflectKind == reflect.Invalid {
return errors.New("parameter is an invalid pointer, maybe nil")
}
if reflectKind != reflect.Ptr && reflectKind != reflect.Struct {
return errors.New("parameter should be type of *struct/**struct")
}
return mapToStruct(r.Map(), pointer)
}

View File

@ -6,36 +6,22 @@
package gdb
import (
"database/sql"
"github.com/gogf/gf/encoding/gparser"
)
// Deprecated.
func (r Record) ToJson() string {
content, _ := gparser.VarToJson(r.Map())
return string(content)
return r.Json()
}
// Deprecated.
func (r Record) ToXml(rootTag ...string) string {
content, _ := gparser.VarToXml(r.Map(), rootTag...)
return string(content)
return r.Xml(rootTag...)
}
// Deprecated.
func (r Record) ToMap() Map {
m := make(map[string]interface{})
for k, v := range r {
m[k] = v.Val()
}
return m
return r.Map()
}
// Deprecated.
func (r Record) ToStruct(pointer interface{}) error {
if r == nil {
return sql.ErrNoRows
}
return mapToStruct(r.Map(), pointer)
return r.Struct(pointer)
}

View File

@ -148,27 +148,48 @@ func (r Result) RecordKeyUint(key string) map[uint]Record {
// Structs converts <r> to struct slice.
// Note that the parameter <pointer> should be type of *[]struct/*[]*struct.
func (r Result) Structs(pointer interface{}) (err error) {
l := len(r)
if l == 0 {
return sql.ErrNoRows
var (
reflectValue = reflect.ValueOf(pointer)
reflectKind = reflectValue.Kind()
)
if reflectKind != reflect.Ptr {
return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
}
t := reflect.TypeOf(pointer)
if t.Kind() != reflect.Ptr {
return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind())
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
}
array := reflect.MakeSlice(t.Elem(), l, l)
itemType := array.Index(0).Type()
for i := 0; i < l; i++ {
if itemType.Kind() == reflect.Ptr {
length := len(r)
if length == 0 {
// The pointed slice is not empty.
if reflectValue.Len() > 0 {
// It here checks if it has struct item, which is already initialized.
// It then returns error to warn the developer its empty and no conversion.
if v := reflectValue.Index(0); v.Kind() != reflect.Ptr {
return sql.ErrNoRows
}
}
// Do nothing for empty struct slice.
return nil
}
var (
reflectType = reflect.TypeOf(pointer)
array = reflect.MakeSlice(reflectType.Elem(), length, length)
itemType = array.Index(0).Type()
itemKind = itemType.Kind()
)
for i := 0; i < length; i++ {
if itemKind == reflect.Ptr {
e := reflect.New(itemType.Elem()).Elem()
if err = r[i].Struct(e); err != nil {
return err
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
}
array.Index(i).Set(e.Addr())
} else {
e := reflect.New(itemType).Elem()
if err = r[i].Struct(e); err != nil {
return err
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
}
array.Index(i).Set(e)
}

View File

@ -6,128 +6,52 @@
package gdb
import (
"database/sql"
"fmt"
"reflect"
"github.com/gogf/gf/encoding/gparser"
)
// Deprecated.
func (r Result) ToJson() string {
content, _ := gparser.VarToJson(r.List())
return string(content)
return r.Json()
}
// Deprecated.
func (r Result) ToXml(rootTag ...string) string {
content, _ := gparser.VarToXml(r.List(), rootTag...)
return string(content)
return r.Xml(rootTag...)
}
// Deprecated.
func (r Result) ToList() List {
l := make(List, len(r))
for k, v := range r {
l[k] = v.Map()
}
return l
return r.List()
}
// Deprecated.
func (r Result) ToStringMap(key string) map[string]Map {
m := make(map[string]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.String()] = item.Map()
}
}
return m
return r.MapKeyStr(key)
}
// Deprecated.
func (r Result) ToIntMap(key string) map[int]Map {
m := make(map[int]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Int()] = item.Map()
}
}
return m
return r.MapKeyInt(key)
}
// Deprecated.
func (r Result) ToUintMap(key string) map[uint]Map {
m := make(map[uint]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Uint()] = item.Map()
}
}
return m
return r.MapKeyUint(key)
}
// Deprecated.
func (r Result) ToStringRecord(key string) map[string]Record {
m := make(map[string]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.String()] = item
}
}
return m
return r.RecordKeyStr(key)
}
// Deprecated.
func (r Result) ToIntRecord(key string) map[int]Record {
m := make(map[int]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Int()] = item
}
}
return m
return r.RecordKeyInt(key)
}
// Deprecated.
func (r Result) ToUintRecord(key string) map[uint]Record {
m := make(map[uint]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Uint()] = item
}
}
return m
return r.RecordKeyUint(key)
}
// Deprecated.
func (r Result) ToStructs(pointer interface{}) (err error) {
l := len(r)
if l == 0 {
return sql.ErrNoRows
}
t := reflect.TypeOf(pointer)
if t.Kind() != reflect.Ptr {
return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind())
}
array := reflect.MakeSlice(t.Elem(), l, l)
itemType := array.Index(0).Type()
for i := 0; i < l; i++ {
if itemType.Kind() == reflect.Ptr {
e := reflect.New(itemType.Elem()).Elem()
if err = r[i].Struct(e); err != nil {
return err
}
array.Index(i).Set(e.Addr())
} else {
e := reflect.New(itemType).Elem()
if err = r[i].Struct(e); err != nil {
return err
}
array.Index(i).Set(e)
}
}
reflect.ValueOf(pointer).Elem().Set(array)
return nil
return r.Structs(pointer)
}

View File

@ -1,98 +0,0 @@
// Copyright 2019 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 gdb_test
import (
"testing"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/test/gtest"
)
func Test_Model_Inherit_Insert(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type Base struct {
Id int `json:"id"`
Uid int `json:"uid"`
CreateTime string `json:"create_time"`
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
}
result, err := db.Table(table).Filter().Data(User{
Passport: "john-test",
Password: "123456",
Nickname: "John",
Base: Base{
Id: 100,
Uid: 100,
CreateTime: gtime.Now().String(),
},
}).Insert()
t.Assert(err, nil)
n, _ := result.RowsAffected()
t.Assert(n, 1)
value, err := db.Table(table).Fields("passport").Where("id=100").Value()
t.Assert(err, nil)
t.Assert(value.String(), "john-test")
})
}
func Test_Model_Inherit_MapToStruct(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type Ids struct {
Id int `json:"id"`
Uid int `json:"uid"`
}
type Base struct {
Ids
CreateTime string `json:"create_time"`
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
}
data := g.Map{
"id": 100,
"uid": 101,
"passport": "t1",
"password": "123456",
"nickname": "T1",
"create_time": gtime.Now().String(),
}
result, err := db.Table(table).Filter().Data(data).Insert()
t.Assert(err, nil)
n, _ := result.RowsAffected()
t.Assert(n, 1)
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
user := new(User)
t.Assert(one.Struct(user), nil)
t.Assert(user.Id, data["id"])
t.Assert(user.Passport, data["passport"])
t.Assert(user.Password, data["password"])
t.Assert(user.Nickname, data["nickname"])
t.Assert(user.CreateTime, data["create_time"])
})
}

View File

@ -0,0 +1,40 @@
// 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 gdb_test
import (
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
)
func Example_transaction() {
db.Transaction(func(tx *gdb.TX) error {
// user
result, err := tx.Insert("user", g.Map{
"passport": "john",
"password": "12345678",
"nickname": "JohnGuo",
})
if err != nil {
return err
}
// user_detail
id, err := result.LastInsertId()
if err != nil {
return err
}
_, err = tx.Insert("user_detail", g.Map{
"uid": id,
"site": "https://johng.cn",
"true_name": "GuoQiang",
})
if err != nil {
return err
}
return nil
})
}

View File

@ -820,7 +820,7 @@ func Test_Model_Structs(t *testing.T) {
}
var users []*User
err := db.Table(table).Where("id<0").Structs(&users)
t.Assert(err, sql.ErrNoRows)
t.Assert(err, nil)
})
}
@ -905,12 +905,14 @@ func Test_Model_Scan(t *testing.T) {
NickName string
CreateTime *gtime.Time
}
user := new(User)
users := new([]*User)
var (
user = new(User)
users = new([]*User)
)
err1 := db.Table(table).Where("id < 0").Scan(user)
err2 := db.Table(table).Where("id < 0").Scan(users)
t.Assert(err1, sql.ErrNoRows)
t.Assert(err2, sql.ErrNoRows)
t.Assert(err2, nil)
})
}
@ -1021,15 +1023,6 @@ func Test_Model_Where(t *testing.T) {
t.AssertGT(len(result), 0)
t.Assert(result["id"].Int(), 3)
})
gtest.C(t, func(t *gtest.T) {
result, err := db.Table(table).Where(g.Map{
"id": g.Slice{1, 2, 3},
"passport": g.Slice{"user_2", "user_3"},
}).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One()
t.Assert(err, nil)
t.AssertGT(len(result), 0)
t.Assert(result["id"].Int(), 3)
})
gtest.C(t, func(t *gtest.T) {
result, err := db.Table(table).Where("id=3", g.Slice{}).One()
t.Assert(err, nil)
@ -1341,7 +1334,7 @@ func Test_Model_WherePri(t *testing.T) {
}).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One()
t.Assert(err, nil)
t.AssertGT(len(result), 0)
t.Assert(result["id"].Int(), 3)
t.Assert(result["id"].Int(), 2)
})
gtest.C(t, func(t *gtest.T) {
result, err := db.Table(table).WherePri("id=3", g.Slice{}).One()
@ -1831,6 +1824,32 @@ func Test_Model_Option_Where(t *testing.T) {
})
}
func Test_Model_Where_MultiSliceArguments(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
r, err := db.Table(table).Where(g.Map{
"id": g.Slice{1, 2, 3, 4},
"passport": g.Slice{"user_2", "user_3", "user_4"},
"nickname": g.Slice{"name_2", "name_4"},
"id >= 4": nil,
}).All()
t.Assert(err, nil)
t.Assert(len(r), 1)
t.Assert(r[0]["id"], 4)
})
gtest.C(t, func(t *gtest.T) {
result, err := db.Table(table).Where(g.Map{
"id": g.Slice{1, 2, 3},
"passport": g.Slice{"user_2", "user_3"},
}).Or("nickname=?", g.Slice{"name_4"}).And("id", 3).One()
t.Assert(err, nil)
t.AssertGT(len(result), 0)
t.Assert(result["id"].Int(), 2)
})
}
func Test_Model_FieldsEx(t *testing.T) {
table := createInitTable()
defer dropTable(table)
@ -1870,8 +1889,8 @@ func Test_Model_FieldsStr(t *testing.T) {
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
t.Assert(db.Table(table).FieldsStr(), "id,passport,password,nickname,create_time")
t.Assert(db.Table(table).FieldsStr("a."), "a.id,a.passport,a.password,a.nickname,a.create_time")
t.Assert(db.Table(table).FieldsStr(), "`id`,`passport`,`password`,`nickname`,`create_time`")
t.Assert(db.Table(table).FieldsStr("a."), "`a`.`id`,`a`.`passport`,`a`.`password`,`a`.`nickname`,`a`.`create_time`")
})
}
@ -1880,8 +1899,8 @@ func Test_Model_FieldsExStr(t *testing.T) {
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
t.Assert(db.Table(table).FieldsExStr("create_time,nickname"), "id,passport,password")
t.Assert(db.Table(table).FieldsExStr("create_time,nickname", "a."), "a.id,a.passport,a.password")
t.Assert(db.Table(table).FieldsExStr("create_time,nickname"), "`id`,`passport`,`password`")
t.Assert(db.Table(table).FieldsExStr("create_time,nickname", "a."), "`a`.`id`,`a`.`passport`,`a`.`password`")
})
}
@ -2213,6 +2232,63 @@ func Test_Model_Cache(t *testing.T) {
t.Assert(err, nil)
t.Assert(one["passport"], "user_200")
})
// transaction.
gtest.C(t, func(t *gtest.T) {
// make cache for id 3
one, err := db.Table(table).Cache(time.Second, "test3").FindOne(3)
t.Assert(err, nil)
t.Assert(one["passport"], "user_3")
r, err := db.Table(table).Data("passport", "user_300").Cache(time.Second, "test3").WherePri(3).Update()
t.Assert(err, nil)
n, err := r.RowsAffected()
t.Assert(err, nil)
t.Assert(n, 1)
err = db.Transaction(func(tx *gdb.TX) error {
one, err := tx.Table(table).Cache(time.Second, "test3").FindOne(3)
t.Assert(err, nil)
t.Assert(one["passport"], "user_300")
return nil
})
t.Assert(err, nil)
one, err = db.Table(table).Cache(time.Second, "test3").FindOne(3)
t.Assert(err, nil)
t.Assert(one["passport"], "user_3")
})
gtest.C(t, func(t *gtest.T) {
// make cache for id 4
one, err := db.Table(table).Cache(time.Second, "test4").FindOne(4)
t.Assert(err, nil)
t.Assert(one["passport"], "user_4")
r, err := db.Table(table).Data("passport", "user_400").Cache(time.Second, "test3").WherePri(4).Update()
t.Assert(err, nil)
n, err := r.RowsAffected()
t.Assert(err, nil)
t.Assert(n, 1)
err = db.Transaction(func(tx *gdb.TX) error {
// Cache feature disabled.
one, err := tx.Table(table).Cache(time.Second, "test4").FindOne(4)
t.Assert(err, nil)
t.Assert(one["passport"], "user_400")
// Update the cache.
r, err := tx.Table(table).Data("passport", "user_4000").
Cache(-1, "test4").WherePri(4).Update()
t.Assert(err, nil)
n, err := r.RowsAffected()
t.Assert(err, nil)
t.Assert(n, 1)
return nil
})
t.Assert(err, nil)
// Read from db.
one, err = db.Table(table).Cache(time.Second, "test4").FindOne(4)
t.Assert(err, nil)
t.Assert(one["passport"], "user_4000")
})
}
func Test_Model_Having(t *testing.T) {
@ -2240,3 +2316,58 @@ func Test_Model_Having(t *testing.T) {
t.Assert(len(all), 1)
})
}
func Test_Model_Distinct(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table, "t").Fields("distinct t.id").Where("id > 1").Having("id > 8").All()
t.Assert(err, nil)
t.Assert(len(all), 2)
})
}
func Test_Model_Min_Max(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
value, err := db.Table(table, "t").Fields("min(t.id)").Where("id > 1").Value()
t.Assert(err, nil)
t.Assert(value.Int(), 2)
})
gtest.C(t, func(t *gtest.T) {
value, err := db.Table(table, "t").Fields("max(t.id)").Where("id > 1").Value()
t.Assert(err, nil)
t.Assert(value.Int(), 10)
})
}
func Test_Model_NullField(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport *string
}
data := g.Map{
"id": 1,
"passport": nil,
}
result, err := db.Table(table).Data(data).Insert()
t.Assert(err, nil)
n, _ := result.RowsAffected()
t.Assert(n, 1)
one, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
var user *User
err = one.Struct(&user)
t.Assert(err, nil)
t.Assert(user.Id, data["id"])
t.Assert(user.Passport, data["passport"])
})
}

View File

@ -0,0 +1,324 @@
// Copyright 2019 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 gdb_test
import (
"testing"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/test/gtest"
)
func Test_Model_Inherit_Insert(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type Base struct {
Id int `json:"id"`
Uid int `json:"uid"`
CreateTime string `json:"create_time"`
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
}
result, err := db.Table(table).Filter().Data(User{
Passport: "john-test",
Password: "123456",
Nickname: "John",
Base: Base{
Id: 100,
Uid: 100,
CreateTime: gtime.Now().String(),
},
}).Insert()
t.Assert(err, nil)
n, _ := result.RowsAffected()
t.Assert(n, 1)
value, err := db.Table(table).Fields("passport").Where("id=100").Value()
t.Assert(err, nil)
t.Assert(value.String(), "john-test")
})
}
func Test_Model_Inherit_MapToStruct(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type Ids struct {
Id int `json:"id"`
Uid int `json:"uid"`
}
type Base struct {
Ids
CreateTime string `json:"create_time"`
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
}
data := g.Map{
"id": 100,
"uid": 101,
"passport": "t1",
"password": "123456",
"nickname": "T1",
"create_time": gtime.Now().String(),
}
result, err := db.Table(table).Filter().Data(data).Insert()
t.Assert(err, nil)
n, _ := result.RowsAffected()
t.Assert(n, 1)
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
user := new(User)
t.Assert(one.Struct(user), nil)
t.Assert(user.Id, data["id"])
t.Assert(user.Passport, data["passport"])
t.Assert(user.Password, data["password"])
t.Assert(user.Nickname, data["nickname"])
t.Assert(user.CreateTime, data["create_time"])
})
}
func Test_Struct_Pointer_Attribute(t *testing.T) {
table := createInitTable()
defer dropTable(table)
type User struct {
Id *int
Passport *string
Password *string
Nickname string
}
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
user := new(User)
err = one.Struct(user)
t.Assert(err, nil)
t.Assert(*user.Id, 1)
t.Assert(*user.Passport, "user_1")
t.Assert(*user.Password, "pass_1")
t.Assert(user.Nickname, "name_1")
})
gtest.C(t, func(t *gtest.T) {
user := new(User)
err := db.Table(table).Struct(user, "id=1")
t.Assert(err, nil)
t.Assert(*user.Id, 1)
t.Assert(*user.Passport, "user_1")
t.Assert(*user.Password, "pass_1")
t.Assert(user.Nickname, "name_1")
})
gtest.C(t, func(t *gtest.T) {
var user *User
err := db.Table(table).Struct(&user, "id=1")
t.Assert(err, nil)
t.Assert(*user.Id, 1)
t.Assert(*user.Passport, "user_1")
t.Assert(*user.Password, "pass_1")
t.Assert(user.Nickname, "name_1")
})
}
func Test_Structs_Pointer_Attribute(t *testing.T) {
table := createInitTable()
defer dropTable(table)
type User struct {
Id *int
Passport *string
Password *string
Nickname string
}
// All
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).All("id < 3")
t.Assert(err, nil)
users := make([]User, 0)
err = one.Structs(&users)
t.Assert(err, nil)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
t.Assert(*users[0].Passport, "user_1")
t.Assert(*users[0].Password, "pass_1")
t.Assert(users[0].Nickname, "name_1")
})
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).All("id < 3")
t.Assert(err, nil)
users := make([]*User, 0)
err = one.Structs(&users)
t.Assert(err, nil)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
t.Assert(*users[0].Passport, "user_1")
t.Assert(*users[0].Password, "pass_1")
t.Assert(users[0].Nickname, "name_1")
})
gtest.C(t, func(t *gtest.T) {
var users []User
one, err := db.Table(table).All("id < 3")
t.Assert(err, nil)
err = one.Structs(&users)
t.Assert(err, nil)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
t.Assert(*users[0].Passport, "user_1")
t.Assert(*users[0].Password, "pass_1")
t.Assert(users[0].Nickname, "name_1")
})
gtest.C(t, func(t *gtest.T) {
var users []*User
one, err := db.Table(table).All("id < 3")
t.Assert(err, nil)
err = one.Structs(&users)
t.Assert(err, nil)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
t.Assert(*users[0].Passport, "user_1")
t.Assert(*users[0].Password, "pass_1")
t.Assert(users[0].Nickname, "name_1")
})
// Structs
gtest.C(t, func(t *gtest.T) {
users := make([]User, 0)
err := db.Table(table).Structs(&users, "id < 3")
t.Assert(err, nil)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
t.Assert(*users[0].Passport, "user_1")
t.Assert(*users[0].Password, "pass_1")
t.Assert(users[0].Nickname, "name_1")
})
gtest.C(t, func(t *gtest.T) {
users := make([]*User, 0)
err := db.Table(table).Structs(&users, "id < 3")
t.Assert(err, nil)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
t.Assert(*users[0].Passport, "user_1")
t.Assert(*users[0].Password, "pass_1")
t.Assert(users[0].Nickname, "name_1")
})
gtest.C(t, func(t *gtest.T) {
var users []User
err := db.Table(table).Structs(&users, "id < 3")
t.Assert(err, nil)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
t.Assert(*users[0].Passport, "user_1")
t.Assert(*users[0].Password, "pass_1")
t.Assert(users[0].Nickname, "name_1")
})
gtest.C(t, func(t *gtest.T) {
var users []*User
err := db.Table(table).Structs(&users, "id < 3")
t.Assert(err, nil)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
t.Assert(*users[0].Passport, "user_1")
t.Assert(*users[0].Password, "pass_1")
t.Assert(users[0].Nickname, "name_1")
})
}
func Test_Struct_Empty(t *testing.T) {
table := createTable()
defer dropTable(table)
type User struct {
Id int
Passport string
Password string
Nickname string
}
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
user := new(User)
t.AssertNE(one.Struct(user), nil)
})
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
var user *User
t.AssertNE(one.Struct(&user), nil)
})
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
var user *User
t.AssertNE(one.Struct(user), nil)
})
}
func Test_Structs_Empty(t *testing.T) {
table := createTable()
defer dropTable(table)
type User struct {
Id int
Passport string
Password string
Nickname string
}
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
users := make([]User, 0)
t.Assert(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
users := make([]User, 10)
t.AssertNE(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
var users []User
t.Assert(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
users := make([]*User, 0)
t.Assert(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
users := make([]*User, 10)
t.Assert(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
var users []*User
t.Assert(all.Structs(&users), nil)
})
}

View File

@ -7,7 +7,9 @@
package gdb_test
import (
"errors"
"fmt"
"github.com/gogf/gf/database/gdb"
"testing"
"github.com/gogf/gf/frame/g"
@ -300,7 +302,6 @@ func Test_TX_Replace(t *testing.T) {
t.Assert(value.String(), "name_1")
}
})
}
func Test_TX_Save(t *testing.T) {
@ -713,5 +714,53 @@ func Test_TX_Delete(t *testing.T) {
t.AssertNE(n, 0)
}
})
}
func Test_Transaction(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
err := db.Transaction(func(tx *gdb.TX) error {
if _, err := tx.Replace(table, g.Map{
"id": 1,
"passport": "USER_1",
"password": "PASS_1",
"nickname": "NAME_1",
"create_time": gtime.Now().String(),
}); err != nil {
t.Error(err)
}
return errors.New("error")
})
t.AssertNE(err, nil)
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
gtest.Error(err)
} else {
t.Assert(value.String(), "name_1")
}
})
gtest.C(t, func(t *gtest.T) {
err := db.Transaction(func(tx *gdb.TX) error {
if _, err := tx.Replace(table, g.Map{
"id": 1,
"passport": "USER_1",
"password": "PASS_1",
"nickname": "NAME_1",
"create_time": gtime.Now().String(),
}); err != nil {
t.Error(err)
}
return nil
})
t.Assert(err, nil)
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
gtest.Error(err)
} else {
t.Assert(value.String(), "NAME_1")
}
})
}

View File

@ -210,11 +210,15 @@ func (r *Redis) Do(command string, args ...interface{}) (interface{}, error) {
return conn.Do(command, args...)
}
// DoVar returns value from Do as gvar.Var.
// DoVar returns value from Do as *gvar.Var.
func (r *Redis) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
v, err := r.Do(command, args...)
if result, ok := v.([]byte); ok {
return gvar.New(gconv.UnsafeBytesToStr(result)), err
}
// It treats all returned slice as string slice.
if result, ok := v.([]interface{}); ok {
return gvar.New(gconv.Strings(result)), err
}
return gvar.New(v), err
}

View File

@ -6,9 +6,44 @@
package gredis
import "github.com/gogf/gf/container/gvar"
import (
"encoding/json"
"github.com/gogf/gf/container/gvar"
"reflect"
)
// DoVar returns value from Do as gvar.Var.
// Do sends a command to the server and returns the received reply.
// It uses json.Marshal for struct/slice/map type values before committing them to redis.
func (c *Conn) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
var (
reflectValue reflect.Value
reflectKind reflect.Kind
)
for k, v := range args {
reflectValue = reflect.ValueOf(v)
reflectKind = reflectValue.Kind()
if reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
case
reflect.Struct,
reflect.Map,
reflect.Slice,
reflect.Array:
// Ignore slice type of: []byte.
if _, ok := v.([]byte); !ok {
if args[k], err = json.Marshal(v); err != nil {
return nil, err
}
}
}
}
return c.Conn.Do(commandName, args...)
}
// DoVar retrieves and returns the result from command as gvar.Var.
func (c *Conn) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
v, err := c.Do(command, args...)
return gvar.New(v), err

View File

@ -0,0 +1,89 @@
// Copyright 2020 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 gredis_test
import (
"fmt"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
)
func Example_autoMarshalUnmarshalMap() {
var (
err error
result *gvar.Var
key = "user"
data = g.Map{
"id": 10000,
"name": "john",
}
)
_, err = g.Redis().Do("SET", key, data)
if err != nil {
panic(err)
}
result, err = g.Redis().DoVar("GET", key)
if err != nil {
panic(err)
}
fmt.Println(result.Map())
}
func Example_autoMarshalUnmarshalStruct() {
type User struct {
Id int
Name string
}
var (
err error
result *gvar.Var
key = "user"
user = &User{
Id: 10000,
Name: "john",
}
)
_, err = g.Redis().Do("SET", key, user)
if err != nil {
panic(err)
}
result, err = g.Redis().DoVar("GET", key)
if err != nil {
panic(err)
}
var user2 *User
if err = result.Struct(&user2); err != nil {
panic(err)
}
fmt.Println(user2.Id, user2.Name)
}
func Example_hashSet() {
var (
err error
result *gvar.Var
key = "user"
)
_, err = g.Redis().Do("HSET", key, "id", 10000)
if err != nil {
panic(err)
}
_, err = g.Redis().Do("HSET", key, "name", "john")
if err != nil {
panic(err)
}
result, err = g.Redis().DoVar("HGETALL", key)
if err != nil {
panic(err)
}
fmt.Println(result.Map())
// May Output:
// map[id:10000 name:john]
}

View File

@ -8,7 +8,7 @@ package gredis_test
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/guuid"
"github.com/gogf/gf/util/guid"
"testing"
"time"
@ -234,7 +234,7 @@ func Test_Bool(t *testing.T) {
func Test_Int(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
redis := gredis.New(config)
key := guuid.New()
key := guid.S()
defer redis.Do("DEL", key)
_, err := redis.Do("SET", key, 1)
@ -249,7 +249,7 @@ func Test_Int(t *testing.T) {
func Test_HSet(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
redis := gredis.New(config)
key := guuid.New()
key := guid.S()
defer redis.Do("DEL", key)
_, err := redis.Do("HSET", key, "name", "john")
@ -261,14 +261,14 @@ func Test_HSet(t *testing.T) {
})
}
func Test_HGetAll(t *testing.T) {
func Test_HGetAll1(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var err error
redis := gredis.New(config)
key := guuid.New()
key := guid.S()
defer redis.Do("DEL", key)
_, err = redis.Do("HSET", key, "id", "100")
_, err = redis.Do("HSET", key, "id", 100)
t.Assert(err, nil)
_, err = redis.Do("HSET", key, "name", "john")
t.Assert(err, nil)
@ -281,3 +281,61 @@ func Test_HGetAll(t *testing.T) {
})
})
}
func Test_HGetAll2(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var (
err error
key = guid.S()
redis = gredis.New(config)
)
defer redis.Do("DEL", key)
_, err = redis.Do("HSET", key, "id", 100)
t.Assert(err, nil)
_, err = redis.Do("HSET", key, "name", "john")
t.Assert(err, nil)
result, err := redis.DoVar("HGETALL", key)
t.Assert(err, nil)
t.Assert(gconv.Uint(result.MapStrVar()["id"]), 100)
t.Assert(result.MapStrVar()["id"].Uint(), 100)
})
}
func Test_Auto_Marshal(t *testing.T) {
var (
err error
redis = gredis.New(config)
key = guid.S()
)
defer redis.Do("DEL", key)
type User struct {
Id int
Name string
}
gtest.C(t, func(t *gtest.T) {
user := &User{
Id: 10000,
Name: "john",
}
_, err = redis.Do("SET", key, user)
t.Assert(err, nil)
r, err := redis.DoVar("GET", key)
t.Assert(err, nil)
t.Assert(r.Map(), g.MapStrAny{
"Id": user.Id,
"Name": user.Name,
})
var user2 *User
t.Assert(r.Struct(&user2), nil)
t.Assert(user2.Id, user.Id)
t.Assert(user2.Name, user.Name)
})
}

View File

@ -58,6 +58,10 @@ func StackWithFilters(filters []string, skip ...int) string {
pc, file, line, ok = runtime.Caller(i)
}
if ok {
// Filter empty file.
if file == "" {
continue
}
// GOROOT filter.
if goRootForFilter != "" &&
len(file) >= len(goRootForFilter) &&

Some files were not shown because too many files have changed in this diff Show More