1.support dynamic registration model
2.support aggregete func
This commit is contained in:
parent
c1b7fa5381
commit
30dbf8fc3a
@ -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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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"`
|
||||||
|
|||||||
@ -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
|
||||||
|
}
|
||||||
@ -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{}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user