From e453dd996ebdd1029ba49adbf7eae72df5f5133c Mon Sep 17 00:00:00 2001 From: xing Date: Sun, 6 Nov 2022 12:04:41 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- helper/func.go | 16 ++++- helper/func_test.go | 36 +++++++++++ models/model.go | 17 +++++ models/query_test.go | 144 ++++++++++++++++++++++++++++++++++++++----- 4 files changed, 198 insertions(+), 15 deletions(-) diff --git a/helper/func.go b/helper/func.go index 5f6c116..75ec66d 100644 --- a/helper/func.go +++ b/helper/func.go @@ -222,7 +222,7 @@ func SliceReduce[T, R any](arr []T, fn func(T, R) R, r R) R { } func SliceReverse[T any](arr []T) []T { - var r []T + var r = make([]T, 0, len(arr)) for i := len(arr); i > 0; i-- { r = append(r, arr[i-1]) } @@ -308,3 +308,17 @@ func Max[T IntNumber | ~float64 | ~float32](a ...T) T { } return max } + +func SliceChunk[T any](arr []T, size int) [][]T { + var r [][]T + i := 0 + for { + if len(arr) <= size+i { + r = append(r, arr[i:]) + break + } + r = append(r, arr[i:i+size]) + i += size + } + return r +} diff --git a/helper/func_test.go b/helper/func_test.go index 0649b66..6414789 100644 --- a/helper/func_test.go +++ b/helper/func_test.go @@ -708,3 +708,39 @@ func TestMax(t *testing.T) { }) } } + +func TestSliceChunk(t *testing.T) { + type args struct { + arr []int + size int + } + tests := []struct { + name string + args args + want [][]int + }{ + { + name: "t1", + args: args{ + arr: RangeSlice(1, 7, 1), + size: 2, + }, + want: [][]int{{1, 2}, {3, 4}, {5, 6}, {7}}, + }, + { + name: "t2", + args: args{ + arr: RangeSlice(1, 8, 1), + size: 2, + }, + want: [][]int{{1, 2}, {3, 4}, {5, 6}, {7, 8}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := SliceChunk(tt.args.arr, tt.args.size); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SliceChunk() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/models/model.go b/models/model.go index 28a7f50..9185620 100644 --- a/models/model.go +++ b/models/model.go @@ -73,6 +73,23 @@ func (w SqlBuilder) parseType(ss []string, args *[]any) error { return nil } +// ParseWhere 解析为where条件,支持3种风格,具体用法参照query_test中的 Find 的测试方法 +// +// 1. 2个为一组 {{"field1","value1"},{"field2","value2"}} => where field1='value1' and field2='value2' +// +// 2. 3个或4个为一组 {{"field","operator","value"[,"int|float"]}} => where field operator 'string'|int|float +// +// {{"a",">","1","int"}} => where 'a'> 1 +// +// {{ "a",">","1"}} => where 'a'>'1' +// +// 另外如果是操作符为in的话为 {{"field","in",""}} => where field in (?,..) in的条件传给 in参数 +// +// 3. 5的倍数为一组{{"and|or","field","operator","value","int|float"}}会忽然掉第一组的and|or +// +// {{"and","field","=","value1","","and","field","=","value2",""}} => where (field = 'value1' and field = 'value2') +// +// {{"and","field","=","num1","int","or","field","=","num2","int"}} => where (field = num1 or field = num2') func (w SqlBuilder) ParseWhere(in *[][]any) (string, []any, error) { var s strings.Builder args := make([]any, 0, len(w)) diff --git a/models/query_test.go b/models/query_test.go index 40363eb..39290de 100644 --- a/models/query_test.go +++ b/models/query_test.go @@ -29,17 +29,112 @@ func TestFind(t *testing.T) { limit int in [][]any } + type posts struct { + wp.Posts + N int `db:"n"` + } tests := []struct { name string args args - wantR []wp.Posts + wantR []posts wantErr bool }{ - {}, + { + name: "in,orderBy", + args: args{ + where: SqlBuilder{{ + "post_status", "publish", + }, {"ID", "in", ""}}, + fields: "*", + group: "", + order: SqlBuilder{{"ID", "desc"}}, + join: nil, + having: nil, + limit: 0, + in: [][]any{{1, 2, 3, 4}}, + }, + wantR: func() []posts { + r, err := Select[posts]("select * from " + posts{}.Table() + " where post_status='publish' and ID in (1,2,3,4) order by ID desc") + if err != nil { + panic(err) + } + return r + }(), + wantErr: false, + }, + { + name: "or", + args: args{ + where: SqlBuilder{{ + "and", "ID", "=", "1", "int", + }, {"or", "ID", "=", "2", "int"}}, + fields: "*", + group: "", + order: nil, + join: nil, + having: nil, + limit: 0, + in: nil, + }, + wantR: func() []posts { + r, err := Select[posts]("select * from " + posts{}.Table() + " where (ID=1 or ID=2)") + if err != nil { + panic(err) + } + return r + }(), + }, + { + name: "group,having", + args: args{ + where: SqlBuilder{ + {"ID", "<", "1000", "int"}, + }, + fields: "post_status,count(*) n", + group: "post_status", + order: nil, + join: nil, + having: SqlBuilder{ + {"n", ">", "1"}, + }, + limit: 0, + in: nil, + }, + wantR: func() []posts { + r, err := Select[posts]("select post_status,count(*) n from " + wp.Posts{}.Table() + " where ID<1000 group by post_status having n>1") + if err != nil { + panic(err) + } + return r + }(), + }, + { + name: "or、多个in", + args: args{ + where: SqlBuilder{ + {"and", "ID", "in", "", "", "or", "ID", "in", "", ""}, + {"or", "post_status", "=", "publish", "", "and", "post_status", "=", "closed", ""}, + }, + fields: "*", + group: "", + order: nil, + join: nil, + having: nil, + limit: 0, + in: [][]any{{1, 2, 3}, {4, 5, 6}}, + }, + wantR: func() []posts { + r, err := Select[posts]("select * from " + posts{}.Table() + " where (ID in (1,2,3) or ID in (4,5,6)) or (post_status='publish' and post_status='closed')") + if err != nil { + panic(err) + } + return r + }(), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - gotR, err := Find[wp.Posts](tt.args.where, tt.args.fields, tt.args.group, tt.args.order, tt.args.join, tt.args.having, tt.args.limit, tt.args.in...) + gotR, err := Find[posts](tt.args.where, tt.args.fields, tt.args.group, tt.args.order, tt.args.join, tt.args.having, tt.args.limit, tt.args.in...) if (err != nil) != tt.wantErr { t.Errorf("Find() error = %v, wantErr %v", err, tt.wantErr) return @@ -55,10 +150,7 @@ func TestFindOneById(t *testing.T) { type args struct { id int } - r, err := Get[wp.Posts]("select * from "+wp.Posts{}.Table()+" where ID=?", 1) - if err != nil { - panic(err) - } + tests := []struct { name string args args @@ -70,7 +162,13 @@ func TestFindOneById(t *testing.T) { args: args{ 1, }, - want: r, + want: func() wp.Posts { + r, err := Get[wp.Posts]("select * from "+wp.Posts{}.Table()+" where ID=?", 1) + if err != nil { + panic(err) + } + return r + }(), wantErr: false, }, } @@ -95,10 +193,6 @@ func TestFirstOne(t *testing.T) { order SqlBuilder in [][]any } - r, err := Get[wp.Posts]("select * from " + wp.Posts{}.Table() + " where post_status='publish' order by ID desc") - if err != nil { - panic(err) - } tests := []struct { name string args args @@ -114,7 +208,13 @@ func TestFirstOne(t *testing.T) { in: nil, }, wantErr: false, - want: r, + want: func() wp.Posts { + r, err := Get[wp.Posts]("select * from " + wp.Posts{}.Table() + " where post_status='publish' order by ID desc limit 1") + if err != nil { + panic(err) + } + return r + }(), }, } for _, tt := range tests { @@ -170,7 +270,23 @@ func TestLastOne(t *testing.T) { want wp.Posts wantErr bool }{ - {}, + { + name: "t1", + args: args{ + where: SqlBuilder{{ + "post_status", "publish", + }}, + fields: "*", + in: nil, + }, + want: func() wp.Posts { + r, err := Get[wp.Posts]("select * from " + wp.Posts{}.Table() + " where post_status='publish' order by " + wp.Posts{}.PrimaryKey() + " desc limit 1") + if err != nil { + panic(err) + } + return r + }(), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {