优化 hasone/many不用反射

This commit is contained in:
xing 2023-05-19 23:14:58 +08:00
parent 6caf07b575
commit 4242d850ff
4 changed files with 183 additions and 133 deletions

View File

@ -1,18 +1,19 @@
package model package model
import "context"
type QueryCondition struct { type QueryCondition struct {
Where ParseWhere Where ParseWhere
From string From string
Fields string Fields string
Group string Group string
Order SqlBuilder Order SqlBuilder
Join SqlBuilder Join SqlBuilder
Having SqlBuilder Having SqlBuilder
Limit int Limit int
Offset int Offset int
In [][]any In [][]any
Relation map[string]*QueryCondition RelationFn []func() (bool, bool, *QueryCondition, func() (func(any) []any, func(any, any) error, any, Relationship))
WithJoin bool
} }
func Conditions(fns ...Condition) *QueryCondition { func Conditions(fns ...Condition) *QueryCondition {
@ -87,17 +88,16 @@ func In(in ...[]any) Condition {
} }
} }
func With(tableTag string, q *QueryCondition) Condition { func WithCtx(ctx *context.Context) Condition {
return func(c *QueryCondition) { return func(c *QueryCondition) {
if c.Relation == nil { *ctx = context.WithValue(*ctx, "ancestorsQueryCondition", c)
c.Relation = map[string]*QueryCondition{}
}
c.Relation[tableTag] = q
} }
} }
func WithJoin(isJoin bool) Condition { func WithFn(getVal, isJoin bool, q *QueryCondition, fn func() (func(any) []any, func(any, any) error, any, Relationship)) Condition {
return func(c *QueryCondition) { return func(c *QueryCondition) {
c.WithJoin = isJoin c.RelationFn = append(c.RelationFn, func() (bool, bool, *QueryCondition, func() (func(any) []any, func(any, any) error, any, Relationship)) {
return getVal, isJoin, q, fn
})
} }
} }

View File

@ -3,6 +3,7 @@ package model
import ( import (
"context" "context"
"database/sql" "database/sql"
"errors"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/app/pkg/models" "github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/safety" "github.com/fthvgb1/wp-go/safety"
@ -14,31 +15,74 @@ import (
) )
type post struct { type post struct {
Id uint64 `gorm:"column:ID" db:"ID" json:"ID" form:"ID"` Id uint64 `gorm:"column:ID" db:"ID" json:"ID" form:"ID"`
PostAuthor uint64 `gorm:"column:post_author" db:"post_author" json:"post_author" form:"post_author"` PostAuthor uint64 `gorm:"column:post_author" db:"post_author" json:"post_author" form:"post_author"`
PostDate time.Time `gorm:"column:post_date" db:"post_date" json:"post_date" form:"post_date"` PostDate time.Time `gorm:"column:post_date" db:"post_date" json:"post_date" form:"post_date"`
PostDateGmt time.Time `gorm:"column:post_date_gmt" db:"post_date_gmt" json:"post_date_gmt" form:"post_date_gmt"` PostDateGmt time.Time `gorm:"column:post_date_gmt" db:"post_date_gmt" json:"post_date_gmt" form:"post_date_gmt"`
PostContent string `gorm:"column:post_content" db:"post_content" json:"post_content" form:"post_content"` PostContent string `gorm:"column:post_content" db:"post_content" json:"post_content" form:"post_content"`
PostTitle string `gorm:"column:post_title" db:"post_title" json:"post_title" form:"post_title"` PostTitle string `gorm:"column:post_title" db:"post_title" json:"post_title" form:"post_title"`
PostExcerpt string `gorm:"column:post_excerpt" db:"post_excerpt" json:"post_excerpt" form:"post_excerpt"` PostExcerpt string `gorm:"column:post_excerpt" db:"post_excerpt" json:"post_excerpt" form:"post_excerpt"`
PostStatus string `gorm:"column:post_status" db:"post_status" json:"post_status" form:"post_status"` PostStatus string `gorm:"column:post_status" db:"post_status" json:"post_status" form:"post_status"`
CommentStatus string `gorm:"column:comment_status" db:"comment_status" json:"comment_status" form:"comment_status"` CommentStatus string `gorm:"column:comment_status" db:"comment_status" json:"comment_status" form:"comment_status"`
PingStatus string `gorm:"column:ping_status" db:"ping_status" json:"ping_status" form:"ping_status"` PingStatus string `gorm:"column:ping_status" db:"ping_status" json:"ping_status" form:"ping_status"`
PostPassword string `gorm:"column:post_password" db:"post_password" json:"post_password" form:"post_password"` PostPassword string `gorm:"column:post_password" db:"post_password" json:"post_password" form:"post_password"`
PostName string `gorm:"column:post_name" db:"post_name" json:"post_name" form:"post_name"` PostName string `gorm:"column:post_name" db:"post_name" json:"post_name" form:"post_name"`
ToPing string `gorm:"column:to_ping" db:"to_ping" json:"to_ping" form:"to_ping"` ToPing string `gorm:"column:to_ping" db:"to_ping" json:"to_ping" form:"to_ping"`
Pinged string `gorm:"column:pinged" db:"pinged" json:"pinged" form:"pinged"` Pinged string `gorm:"column:pinged" db:"pinged" json:"pinged" form:"pinged"`
PostModified time.Time `gorm:"column:post_modified" db:"post_modified" json:"post_modified" form:"post_modified"` PostModified time.Time `gorm:"column:post_modified" db:"post_modified" json:"post_modified" form:"post_modified"`
PostModifiedGmt time.Time `gorm:"column:post_modified_gmt" db:"post_modified_gmt" json:"post_modified_gmt" form:"post_modified_gmt"` PostModifiedGmt time.Time `gorm:"column:post_modified_gmt" db:"post_modified_gmt" json:"post_modified_gmt" form:"post_modified_gmt"`
PostContentFiltered string `gorm:"column:post_content_filtered" db:"post_content_filtered" json:"post_content_filtered" form:"post_content_filtered"` PostContentFiltered string `gorm:"column:post_content_filtered" db:"post_content_filtered" json:"post_content_filtered" form:"post_content_filtered"`
PostParent uint64 `gorm:"column:post_parent" db:"post_parent" json:"post_parent" form:"post_parent"` PostParent uint64 `gorm:"column:post_parent" db:"post_parent" json:"post_parent" form:"post_parent"`
Guid string `gorm:"column:guid" db:"guid" json:"guid" form:"guid"` Guid string `gorm:"column:guid" db:"guid" json:"guid" form:"guid"`
MenuOrder int `gorm:"column:menu_order" db:"menu_order" json:"menu_order" form:"menu_order"` MenuOrder int `gorm:"column:menu_order" db:"menu_order" json:"menu_order" form:"menu_order"`
PostType string `gorm:"column:post_type" db:"post_type" json:"post_type" form:"post_type"` PostType string `gorm:"column:post_type" db:"post_type" json:"post_type" form:"post_type"`
PostMimeType string `gorm:"column:post_mime_type" db:"post_mime_type" json:"post_mime_type" form:"post_mime_type"` PostMimeType string `gorm:"column:post_mime_type" db:"post_mime_type" json:"post_mime_type" form:"post_mime_type"`
CommentCount int64 `gorm:"column:comment_count" db:"comment_count" json:"comment_count" form:"comment_count"` CommentCount int64 `gorm:"column:comment_count" db:"comment_count" json:"comment_count" form:"comment_count"`
User *user `table:"wp_users user" foreignKey:"ID" local:"post_author" relation:"hasOne"` User *user
PostMeta *[]models.PostMeta `table:"wp_postmeta meta" foreignKey:"post_id" local:"ID" relation:"hasMany"` PostMeta *[]models.PostMeta
}
func PostAuthor() (func(any) []any, func(posts, users any) error, any, Relationship) {
var u user
return func(a any) []any {
return []any{a.(*post).PostAuthor}
}, func(posts, users any) error {
u := users.(*user)
postss, ok := posts.(*post)
if !ok {
postsss, ok := posts.(*[]post)
if !ok {
return errors.New("无法识别post的类型")
}
for i := 0; i < len(*postsss); i++ {
(*postsss)[i].User = u
}
}
postss.User = u
return nil
}, &u, Relationship{
RelationType: "hasOne",
Table: "wp_users user",
ForeignKey: "ID",
Local: "post_author",
}
}
func PostMetas() (func(any) []any, func(posts, users any) error, any, Relationship) {
var u []models.PostMeta
return func(a any) []any {
return []any{a.(*post).Id}
}, func(posts, meta any) error {
postss := posts.(*post)
u := meta.(*[]models.PostMeta)
postss.PostMeta = u
return nil
}, &u, Relationship{
RelationType: "hasMany",
Table: "wp_postmeta meta",
ForeignKey: "post_id",
Local: "ID",
}
} }
type TermRelationships struct { type TermRelationships struct {
@ -339,17 +383,15 @@ func TestGets2(t *testing.T) {
t.Run("hasOne", func(t *testing.T) { t.Run("hasOne", func(t *testing.T) {
{ {
q := Conditions( q := Conditions(
Where(SqlBuilder{{"id = 190"}}), Where(SqlBuilder{{"posts.id = 190"}}),
With("user", Conditions( WithCtx(&ctx),
WithFn(true, true, Conditions(
Fields("ID,user_login,user_pass"), Fields("ID,user_login,user_pass"),
)), ), PostAuthor),
Fields("posts.*"), Fields("posts.*"),
From("wp_posts posts"), From("wp_posts posts"),
With("meta", Conditions( WithFn(false, true, nil, PostMetas),
WithJoin(true),
)),
) )
ctx = context.WithValue(ctx, "ancestorsQueryCondition", q)
got, err := Gets[post](ctx, q) got, err := Gets[post](ctx, q)
_ = got _ = got
if err != nil { if err != nil {
@ -357,6 +399,26 @@ func TestGets2(t *testing.T) {
} }
} }
}) })
t.Run("hasOne", func(t *testing.T) {
{
q := Conditions(
Where(SqlBuilder{{"posts.id", "in", ""}}),
In([]any{190, 2978}),
WithCtx(&ctx),
WithFn(true, true, Conditions(
Fields("ID,user_login,user_pass"),
), PostAuthor),
Fields("posts.*"),
From("wp_posts posts"),
WithFn(false, false, nil, PostMetas),
)
got, err := Finds[post](ctx, q)
_ = got
if err != nil {
t.Errorf("err:%v", err)
}
}
})
} }
func TestFirstOne(t *testing.T) { func TestFirstOne(t *testing.T) {

View File

@ -27,12 +27,16 @@ func FindFromDB[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (r
func finds[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (r []T, err error) { func finds[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (r []T, err error) {
setTable[T](q) setTable[T](q)
sq, args, err := BuildQuerySql(q) if len(q.RelationFn) < 1 {
if err != nil { sq, args, er := BuildQuerySql(q)
if err != nil {
err = er
return
}
err = db.Select(ctx, &r, sq, args...)
return return
} }
err = db.Select(ctx, &r, sq, args...) err = parseRelation(true, db, ctx, &r, q)
return return
} }
@ -304,7 +308,7 @@ func GetsFromDB[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (T,
func gets[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (r T, err error) { func gets[T Model](db dbQuery, ctx context.Context, q *QueryCondition) (r T, err error) {
setTable[T](q) setTable[T](q)
if len(q.Relation) < 1 { if len(q.RelationFn) < 1 {
s, args, er := BuildQuerySql(q) s, args, er := BuildQuerySql(q)
if er != nil { if er != nil {
err = er err = er

View File

@ -2,9 +2,9 @@ package model
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper"
"reflect"
"strings" "strings"
) )
@ -14,89 +14,73 @@ func setTable[T Model](q *QueryCondition) {
} }
} }
type Relationship struct {
RelationType string
Table string
ForeignKey string
Local string
On string
}
func Relation(db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) { func Relation(db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) {
var fn []func() var fn []func()
var fns []func() error var fns []func() error
t := reflect.TypeOf(r).Elem() for _, f := range q.RelationFn {
v := reflect.ValueOf(r).Elem() getVal, isJoin, qq, ff := f()
for tableTag, relation := range q.Relation { idFn, assignment, rr, ship := ff()
if tableTag == "" { if isJoin {
continue fn = append(fn, func() {
} tables := strings.Split(ship.Table, " ")
tableTag := tableTag
relation := relation
for i := 0; i < t.NumField(); i++ {
i := i
tag := t.Field(i).Tag
table, ok := tag.Lookup("table")
if !ok || table == "" {
continue
}
tables := strings.Split(table, " ")
if tables[len(tables)-1] != tableTag {
continue
}
foreignKey := tag.Get("foreignKey")
if foreignKey == "" {
continue
}
localKey := tag.Get("local")
if localKey == "" {
continue
}
if relation == nil {
relation = &QueryCondition{
Fields: "*",
}
}
relation.From = table
id := ""
j := 0
for ; j < t.NumField(); j++ {
vvv, ok := t.Field(j).Tag.Lookup("db")
if ok && vvv == tag.Get("local") {
break
}
}
if relation.WithJoin {
from := strings.Split(q.From, " ") from := strings.Split(q.From, " ")
fn = append(fn, func() { on := ""
qq := helper.GetContextVal(ctx, "ancestorsQueryCondition", q) if ship.On != "" {
qq.Join = append(q.Join, SqlBuilder{ on = fmt.Sprintf("and %s", on)
{"left join", table, fmt.Sprintf("%s.%s=%s.%s", tables[len(tables)-1], foreignKey, from[len(from)-1], localKey)},
}...)
})
}
fns = append(fns, func() error {
{
var w any = relation.Where
if w == nil {
w = SqlBuilder{}
}
ww, ok := w.(SqlBuilder)
if ok {
id = fmt.Sprintf("%v", v.Field(j).Interface())
ww = append(ww, SqlBuilder{{
foreignKey, "=", id, "int",
}}...)
relation.Where = ww
}
} }
var err error qq := helper.GetContextVal(ctx, "ancestorsQueryCondition", q)
vv := reflect.New(v.Field(i).Type().Elem()).Interface() qq.Join = append(qq.Join, []string{
switch tag.Get("relation") { "left join", ship.Table, fmt.Sprintf("%s.%s=%s.%s %s", tables[len(tables)-1], ship.ForeignKey, from[len(from)-1], ship.Local, on)})
case "hasOne":
err = parseRelation(false, db, ctx, vv, relation)
case "hasMany":
err = parseRelation(true, db, ctx, vv, relation)
}
if err != nil {
return err
}
v.Field(i).Set(reflect.ValueOf(vv))
return nil
}) })
} }
if !getVal {
continue
}
fns = append(fns, func() error {
var err error
{
if qq == nil {
qq = &QueryCondition{
Fields: "*",
}
}
var w any = qq.Where
if w == nil {
w = SqlBuilder{}
}
ww, ok := w.(SqlBuilder)
if ok {
ww = append(ww, SqlBuilder{{
ship.ForeignKey, "in", "",
}}...)
qq.In = [][]any{idFn(r)}
qq.Where = ww
}
if qq.From == "" {
qq.From = ship.Table
}
}
// todo finds的情况
switch ship.RelationType {
case "hasOne":
err = parseRelation(false, db, ctx, rr, qq)
case "hasMany":
err = parseRelation(true, db, ctx, rr, qq)
}
if err != nil && err != sql.ErrNoRows {
return err
}
err = assignment(r, rr)
return err
})
} }
return fn, fns return fn, fns
} }