1.support dynamic registration model

2.support aggregete func
This commit is contained in:
AllenX2018 2021-01-04 16:29:03 +08:00
parent c1b7fa5381
commit 30dbf8fc3a
6 changed files with 124 additions and 26 deletions

View File

@ -948,9 +948,10 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
val := reflect.ValueOf(container) val := reflect.ValueOf(container)
ind := reflect.Indirect(val) ind := reflect.Indirect(val)
errTyp := true unregister := true
one := true one := true
isPtr := true isPtr := true
name := ""
if val.Kind() == reflect.Ptr { if val.Kind() == reflect.Ptr {
fn := "" fn := ""
@ -963,19 +964,17 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
case reflect.Struct: case reflect.Struct:
isPtr = false isPtr = false
fn = getFullName(typ) fn = getFullName(typ)
name = getTableName(reflect.New(typ))
} }
} else { } else {
fn = getFullName(ind.Type()) fn = getFullName(ind.Type())
name = getTableName(ind)
} }
errTyp = fn != mi.fullName unregister = fn != mi.fullName
} }
if errTyp { if unregister {
if one { RegisterModel(container)
panic(fmt.Errorf("wrong object type `%s` for rows scan, need *%s", val.Type(), mi.fullName))
} else {
panic(fmt.Errorf("wrong object type `%s` for rows scan, need *[]*%s or *[]%s", val.Type(), mi.fullName, mi.fullName))
}
} }
rlimit := qs.limit rlimit := qs.limit
@ -1040,6 +1039,9 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
if qs.distinct { if qs.distinct {
sqlSelect += " DISTINCT" sqlSelect += " DISTINCT"
} }
if qs.aggregate != "" {
sels = qs.aggregate
}
query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s%s", query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s%s",
sqlSelect, sels, Q, mi.table, Q, sqlSelect, sels, Q, mi.table, Q,
specifyIndexes, join, where, groupBy, orderBy, limit) specifyIndexes, join, where, groupBy, orderBy, limit)
@ -1064,16 +1066,20 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi
} }
} }
defer rs.Close()
slice := ind
if unregister {
mi, _ = modelCache.get(name)
tCols = mi.fields.dbcols
colsNum = len(tCols)
}
refs := make([]interface{}, colsNum) refs := make([]interface{}, colsNum)
for i := range refs { for i := range refs {
var ref interface{} var ref interface{}
refs[i] = &ref refs[i] = &ref
} }
defer rs.Close()
slice := ind
var cnt int64 var cnt int64
for rs.Next() { for rs.Next() {
if one && cnt == 0 || !one { if one && cnt == 0 || !one {

View File

@ -332,10 +332,6 @@ end:
// register register models to model cache // register register models to model cache
func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, models ...interface{}) (err error) { func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, models ...interface{}) (err error) {
if mc.done {
err = fmt.Errorf("register must be run before BootStrap")
return
}
for _, model := range models { for _, model := range models {
val := reflect.ValueOf(model) val := reflect.ValueOf(model)
@ -352,7 +348,9 @@ func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, m
err = fmt.Errorf("<orm.RegisterModel> only allow ptr model struct, it looks you use two reference to the struct `%s`", typ) err = fmt.Errorf("<orm.RegisterModel> only allow ptr model struct, it looks you use two reference to the struct `%s`", typ)
return return
} }
if val.Elem().Kind() == reflect.Slice {
val = reflect.New(val.Elem().Type().Elem())
}
table := getTableName(val) table := getTableName(val)
if prefixOrSuffixStr != "" { if prefixOrSuffixStr != "" {
@ -371,8 +369,7 @@ func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, m
} }
if _, ok := mc.get(table); ok { if _, ok := mc.get(table); ok {
err = fmt.Errorf("<orm.RegisterModel> table name `%s` repeat register, must be unique\n", table) return nil
return
} }
mi := newModelInfo(val) mi := newModelInfo(val)
@ -389,12 +386,6 @@ func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, m
} }
} }
} }
if mi.fields.pk == nil {
err = fmt.Errorf("<orm.RegisterModel> `%s` needs a primary key field, default is to use 'id' if not set\n", name)
return
}
} }
mi.table = table mi.table = table

View File

@ -255,6 +255,22 @@ func NewTM() *TM {
return obj return obj
} }
type DeptInfo struct {
ID int `orm:"column(id)"`
Created time.Time `orm:"auto_now_add"`
DeptName string
EmployeeName string
Salary int
}
type UnregisterModel struct {
ID int `orm:"column(id)"`
Created time.Time `orm:"auto_now_add"`
DeptName string
EmployeeName string
Salary int
}
type User struct { type User struct {
ID int `orm:"column(id)"` ID int `orm:"column(id)"`
UserName string `orm:"size(30);unique"` UserName string `orm:"size(30);unique"`

View File

@ -79,6 +79,7 @@ type querySet struct {
orm *ormBase orm *ormBase
ctx context.Context ctx context.Context
forContext bool forContext bool
aggregate string
} }
var _ QuerySeter = new(querySet) var _ QuerySeter = new(querySet)
@ -323,3 +324,9 @@ func newQuerySet(orm *ormBase, mi *modelInfo) QuerySeter {
o.orm = orm o.orm = orm
return o return o
} }
// aggregate func
func (o querySet) Aggregate(s string) QuerySeter {
o.aggregate = s
return &o
}

View File

@ -205,6 +205,7 @@ func TestSyncDb(t *testing.T) {
RegisterModel(new(Index)) RegisterModel(new(Index))
RegisterModel(new(StrPk)) RegisterModel(new(StrPk))
RegisterModel(new(TM)) RegisterModel(new(TM))
RegisterModel(new(DeptInfo))
err := RunSyncdb("default", true, Debug) err := RunSyncdb("default", true, Debug)
throwFail(t, err) throwFail(t, err)
@ -232,6 +233,7 @@ func TestRegisterModels(t *testing.T) {
RegisterModel(new(Index)) RegisterModel(new(Index))
RegisterModel(new(StrPk)) RegisterModel(new(StrPk))
RegisterModel(new(TM)) RegisterModel(new(TM))
RegisterModel(new(DeptInfo))
BootStrap() BootStrap()
@ -333,6 +335,73 @@ func TestTM(t *testing.T) {
throwFail(t, AssertIs(recTM.TMPrecision2.String(), "2020-08-07 02:07:04.1235 +0000 UTC")) throwFail(t, AssertIs(recTM.TMPrecision2.String(), "2020-08-07 02:07:04.1235 +0000 UTC"))
} }
func TestUnregisterModel(t *testing.T) {
data := []*DeptInfo{
{
DeptName: "A",
EmployeeName: "A1",
Salary: 1000,
},
{
DeptName: "A",
EmployeeName: "A2",
Salary: 2000,
},
{
DeptName: "B",
EmployeeName: "B1",
Salary: 2000,
},
{
DeptName: "B",
EmployeeName: "B2",
Salary: 4000,
},
{
DeptName: "B",
EmployeeName: "B3",
Salary: 3000,
},
}
qs := dORM.QueryTable("dept_info")
i, _ := qs.PrepareInsert()
for _, d := range data {
_, err := i.Insert(d)
if err != nil {
throwFail(t, err)
}
}
f := func() {
var res []UnregisterModel
n, err := dORM.QueryTable("dept_info").All(&res)
throwFail(t, err)
throwFail(t, AssertIs(n, 5))
throwFail(t, AssertIs(res[0].EmployeeName, "A1"))
type Sum struct {
DeptName string
Total int
}
var sun []Sum
qs.Aggregate("dept_name,sum(salary) as total").GroupBy("dept_name").OrderBy("dept_name").All(&sun)
throwFail(t, AssertIs(sun[0].DeptName, "A"))
throwFail(t, AssertIs(sun[0].Total, 3000))
type Max struct {
DeptName string
Max float64
}
var max []Max
qs.Aggregate("dept_name,max(salary) as max").GroupBy("dept_name").OrderBy("dept_name").All(&max)
throwFail(t, AssertIs(max[1].DeptName, "B"))
throwFail(t, AssertIs(max[1].Max, 4000))
}
for i := 0; i < 5; i++ {
f()
}
}
func TestNullDataTypes(t *testing.T) { func TestNullDataTypes(t *testing.T) {
d := DataNull{} d := DataNull{}

View File

@ -405,6 +405,15 @@ type QuerySeter interface {
// Found int // Found int
// } // }
RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error)
// aggregate func.
// for example:
// type result struct {
// DeptName string
// Total int
// }
// var res []result
// o.QueryTable("dept_info").Aggregate("dept_name,sum(salary) as total").GroupBy("dept_name").All(&res)
Aggregate(s string) QuerySeter
} }
// QueryM2Mer model to model query struct // QueryM2Mer model to model query struct