mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
add condition and order-by feature for with tag feature for package gdb
This commit is contained in:
@ -55,11 +55,13 @@ type apiTableName interface {
|
||||
}
|
||||
|
||||
const (
|
||||
OrmTagForStruct = "orm"
|
||||
OrmTagForUnique = "unique"
|
||||
OrmTagForPrimary = "primary"
|
||||
OrmTagForTable = "table"
|
||||
OrmTagForWith = "with"
|
||||
OrmTagForStruct = "orm"
|
||||
OrmTagForUnique = "unique"
|
||||
OrmTagForPrimary = "primary"
|
||||
OrmTagForTable = "table"
|
||||
OrmTagForWith = "with"
|
||||
OrmTagForWithWhere = "where"
|
||||
OrmTagForWithOrder = "order"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -88,28 +88,20 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
}
|
||||
for _, field := range fieldMap {
|
||||
var (
|
||||
withTag string
|
||||
ormTag = field.Tag(OrmTagForStruct)
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith),
|
||||
ormTag,
|
||||
)
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
parsedTagOutput = m.parseWithTagInFieldStruct(field)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
withTag = match[1]
|
||||
}
|
||||
if withTag == "" {
|
||||
if parsedTagOutput.With == "" {
|
||||
continue
|
||||
}
|
||||
if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) {
|
||||
continue
|
||||
}
|
||||
array := gstr.SplitAndTrim(withTag, "=")
|
||||
array := gstr.SplitAndTrim(parsedTagOutput.With, "=")
|
||||
if len(array) == 1 {
|
||||
// It supports using only one column name
|
||||
// if both tables associates using the same column name.
|
||||
array = append(array, withTag)
|
||||
array = append(array, parsedTagOutput.With)
|
||||
}
|
||||
var (
|
||||
model *Model
|
||||
@ -129,7 +121,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`cannot find the related value for attribute name "%s" of with tag "%s"`,
|
||||
relatedAttrName, withTag,
|
||||
relatedAttrName, parsedTagOutput.With,
|
||||
)
|
||||
}
|
||||
bindToReflectValue := field.Value
|
||||
@ -154,6 +146,12 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
} else {
|
||||
model = model.With(m.withArray...)
|
||||
}
|
||||
if parsedTagOutput.Where != "" {
|
||||
model = model.Where(parsedTagOutput.Where)
|
||||
}
|
||||
if parsedTagOutput.Order != "" {
|
||||
model = model.Order(parsedTagOutput.Order)
|
||||
}
|
||||
|
||||
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).Scan(bindToReflectValue)
|
||||
if err != nil {
|
||||
@ -201,28 +199,20 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
|
||||
for fieldName, field := range fieldMap {
|
||||
var (
|
||||
withTag string
|
||||
ormTag = field.Tag(OrmTagForStruct)
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith),
|
||||
ormTag,
|
||||
)
|
||||
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
|
||||
parsedTagOutput = m.parseWithTagInFieldStruct(field)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
withTag = match[1]
|
||||
}
|
||||
if withTag == "" {
|
||||
if parsedTagOutput.With == "" {
|
||||
continue
|
||||
}
|
||||
if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) {
|
||||
continue
|
||||
}
|
||||
array := gstr.SplitAndTrim(withTag, "=")
|
||||
array := gstr.SplitAndTrim(parsedTagOutput.With, "=")
|
||||
if len(array) == 1 {
|
||||
// It supports using only one column name
|
||||
// if both tables associates using the same column name.
|
||||
array = append(array, withTag)
|
||||
array = append(array, parsedTagOutput.With)
|
||||
}
|
||||
var (
|
||||
model *Model
|
||||
@ -242,7 +232,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
return gerror.NewCodef(
|
||||
gerror.CodeInvalidParameter,
|
||||
`cannot find the related value for attribute name "%s" of with tag "%s"`,
|
||||
relatedAttrName, withTag,
|
||||
relatedAttrName, parsedTagOutput.With,
|
||||
)
|
||||
}
|
||||
|
||||
@ -260,11 +250,58 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
} else {
|
||||
model = model.With(m.withArray...)
|
||||
}
|
||||
if parsedTagOutput.Where != "" {
|
||||
model = model.Where(parsedTagOutput.Where)
|
||||
}
|
||||
if parsedTagOutput.Order != "" {
|
||||
model = model.Order(parsedTagOutput.Order)
|
||||
}
|
||||
|
||||
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, withTag)
|
||||
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, parsedTagOutput.With)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type parseWithTagInFieldStructOutput struct {
|
||||
With string
|
||||
Where string
|
||||
Order string
|
||||
}
|
||||
|
||||
func (m *Model) parseWithTagInFieldStruct(field *structs.Field) (output parseWithTagInFieldStructOutput) {
|
||||
var (
|
||||
match []string
|
||||
ormTag = field.Tag(OrmTagForStruct)
|
||||
)
|
||||
// with tag.
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:\s*([^,]+),{0,1}`, OrmTagForWith),
|
||||
ormTag,
|
||||
)
|
||||
if len(match) > 1 {
|
||||
output.With = match[1]
|
||||
}
|
||||
if len(match) > 2 {
|
||||
output.Where = gstr.Trim(match[2])
|
||||
}
|
||||
// where string.
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:.+,\s*%s:\s*([^,]+),{0,1}`, OrmTagForWith, OrmTagForWithWhere),
|
||||
ormTag,
|
||||
)
|
||||
if len(match) > 1 {
|
||||
output.Where = gstr.Trim(match[1])
|
||||
}
|
||||
// order string.
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:.+,\s*%s:\s*([^,]+),{0,1}`, OrmTagForWith, OrmTagForWithOrder),
|
||||
ormTag,
|
||||
)
|
||||
if len(match) > 1 {
|
||||
output.Order = gstr.Trim(match[1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -738,150 +738,141 @@ PRIMARY KEY (id)
|
||||
})
|
||||
}
|
||||
|
||||
//func Test_Table_Relation_WithAllCondition_List(t *testing.T) {
|
||||
// var (
|
||||
// tableUser = "user"
|
||||
// tableUserDetail = "user_detail"
|
||||
// tableUserScores = "user_scores"
|
||||
// )
|
||||
// if _, err := db.Exec(fmt.Sprintf(`
|
||||
//CREATE TABLE IF NOT EXISTS %s (
|
||||
//id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
//name varchar(45) NOT NULL,
|
||||
//PRIMARY KEY (id)
|
||||
//) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
// `, tableUser)); err != nil {
|
||||
// gtest.Error(err)
|
||||
// }
|
||||
// defer dropTable(tableUser)
|
||||
//
|
||||
// if _, err := db.Exec(fmt.Sprintf(`
|
||||
//CREATE TABLE IF NOT EXISTS %s (
|
||||
//uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
//address varchar(45) NOT NULL,
|
||||
//PRIMARY KEY (uid)
|
||||
//) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
// `, tableUserDetail)); err != nil {
|
||||
// gtest.Error(err)
|
||||
// }
|
||||
// defer dropTable(tableUserDetail)
|
||||
//
|
||||
// if _, err := db.Exec(fmt.Sprintf(`
|
||||
//CREATE TABLE IF NOT EXISTS %s (
|
||||
//id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
//uid int(10) unsigned NOT NULL,
|
||||
//score int(10) unsigned NOT NULL,
|
||||
//PRIMARY KEY (id)
|
||||
//) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
// `, tableUserScores)); err != nil {
|
||||
// gtest.Error(err)
|
||||
// }
|
||||
// defer dropTable(tableUserScores)
|
||||
//
|
||||
// type UserDetail struct {
|
||||
// gmeta.Meta `orm:"table:user_detail"`
|
||||
// Uid int `json:"uid"`
|
||||
// Address string `json:"address"`
|
||||
// }
|
||||
//
|
||||
// type UserScores struct {
|
||||
// gmeta.Meta `orm:"table:user_scores"`
|
||||
// Id int `json:"id"`
|
||||
// Uid int `json:"uid"`
|
||||
// Score int `json:"score"`
|
||||
// }
|
||||
//
|
||||
// type User struct {
|
||||
// gmeta.Meta `orm:"table:user"`
|
||||
// Id int `json:"id"`
|
||||
// Name string `json:"name"`
|
||||
// UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
// UserScores []*UserScores `orm:"with:uid=id, score>1 and score<5"`
|
||||
// }
|
||||
//
|
||||
// // Initialize the data.
|
||||
// var err error
|
||||
// for i := 1; i <= 5; i++ {
|
||||
// // User.
|
||||
// _, err = db.Insert(tableUser, g.Map{
|
||||
// "id": i,
|
||||
// "name": fmt.Sprintf(`name_%d`, i),
|
||||
// })
|
||||
// gtest.Assert(err, nil)
|
||||
// // Detail.
|
||||
// _, err = db.Insert(tableUserDetail, g.Map{
|
||||
// "uid": i,
|
||||
// "address": fmt.Sprintf(`address_%d`, i),
|
||||
// })
|
||||
// gtest.Assert(err, nil)
|
||||
// // Scores.
|
||||
// for j := 1; j <= 5; j++ {
|
||||
// _, err = db.Insert(tableUserScores, g.Map{
|
||||
// "uid": i,
|
||||
// "score": j,
|
||||
// })
|
||||
// gtest.Assert(err, nil)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// db.SetDebug(true)
|
||||
// defer db.SetDebug(false)
|
||||
//
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// var users []*User
|
||||
// err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
// t.AssertNil(err)
|
||||
// t.Assert(len(users), 2)
|
||||
// t.Assert(users[0].Id, 3)
|
||||
// t.Assert(users[0].Name, "name_3")
|
||||
// t.AssertNE(users[0].UserDetail, nil)
|
||||
// t.Assert(users[0].UserDetail.Uid, 3)
|
||||
// t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
// t.Assert(len(users[0].UserScores), 5)
|
||||
// t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[0].Score, 1)
|
||||
// t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[4].Score, 5)
|
||||
//
|
||||
// t.Assert(users[1].Id, 4)
|
||||
// t.Assert(users[1].Name, "name_4")
|
||||
// t.AssertNE(users[1].UserDetail, nil)
|
||||
// t.Assert(users[1].UserDetail.Uid, 4)
|
||||
// t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
// t.Assert(len(users[1].UserScores), 5)
|
||||
// t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[0].Score, 1)
|
||||
// t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[4].Score, 5)
|
||||
// })
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// var users []User
|
||||
// err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
// t.AssertNil(err)
|
||||
// t.Assert(len(users), 2)
|
||||
// t.Assert(users[0].Id, 3)
|
||||
// t.Assert(users[0].Name, "name_3")
|
||||
// t.AssertNE(users[0].UserDetail, nil)
|
||||
// t.Assert(users[0].UserDetail.Uid, 3)
|
||||
// t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
// t.Assert(len(users[0].UserScores), 5)
|
||||
// t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[0].Score, 1)
|
||||
// t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[4].Score, 5)
|
||||
//
|
||||
// t.Assert(users[1].Id, 4)
|
||||
// t.Assert(users[1].Name, "name_4")
|
||||
// t.AssertNE(users[1].UserDetail, nil)
|
||||
// t.Assert(users[1].UserDetail.Uid, 4)
|
||||
// t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
// t.Assert(len(users[1].UserScores), 5)
|
||||
// t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[0].Score, 1)
|
||||
// t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[4].Score, 5)
|
||||
// })
|
||||
//}
|
||||
func Test_Table_Relation_WithAllCondition_List(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id, where:uid > 3"`
|
||||
UserScores []*UserScores `orm:"with:uid=id, where:score>1 and score<5, order:score desc"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.Assert(users[0].UserDetail, nil)
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 3)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 4)
|
||||
t.Assert(users[1].UserScores[2].Uid, 4)
|
||||
t.Assert(users[1].UserScores[2].Score, 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.Assert(users[0].UserDetail, nil)
|
||||
|
||||
t.Assert(len(users[0].UserScores), 3)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 4)
|
||||
t.Assert(users[0].UserScores[2].Uid, 3)
|
||||
t.Assert(users[0].UserScores[2].Score, 2)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 3)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 4)
|
||||
t.Assert(users[1].UserScores[2].Uid, 4)
|
||||
t.Assert(users[1].UserScores[2].Score, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Embedded(t *testing.T) {
|
||||
var (
|
||||
|
||||
Reference in New Issue
Block a user