add condition and order-by feature for with tag feature for package gdb

This commit is contained in:
John Guo
2021-08-02 00:38:56 +08:00
parent f02372cf58
commit fddc21670a
3 changed files with 208 additions and 178 deletions

View File

@ -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 (

View File

@ -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
}

View File

@ -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 (