优化 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
import "context"
type QueryCondition struct {
Where ParseWhere
From string
Fields string
Group string
Order SqlBuilder
Join SqlBuilder
Having SqlBuilder
Limit int
Offset int
In [][]any
Relation map[string]*QueryCondition
WithJoin bool
Where ParseWhere
From string
Fields string
Group string
Order SqlBuilder
Join SqlBuilder
Having SqlBuilder
Limit int
Offset int
In [][]any
RelationFn []func() (bool, bool, *QueryCondition, func() (func(any) []any, func(any, any) error, any, Relationship))
}
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) {
if c.Relation == nil {
c.Relation = map[string]*QueryCondition{}
}
c.Relation[tableTag] = q
*ctx = context.WithValue(*ctx, "ancestorsQueryCondition", c)
}
}
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) {
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 (
"context"
"database/sql"
"errors"
"fmt"
"github.com/fthvgb1/wp-go/app/pkg/models"
"github.com/fthvgb1/wp-go/safety"
@ -14,31 +15,74 @@ import (
)
type post struct {
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
PostMeta *[]models.PostMeta `table:"wp_postmeta meta" foreignKey:"post_id" local:"ID" relation:"hasMany"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
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"`
CommentCount int64 `gorm:"column:comment_count" db:"comment_count" json:"comment_count" form:"comment_count"`
User *user
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 {
@ -339,17 +383,15 @@ func TestGets2(t *testing.T) {
t.Run("hasOne", func(t *testing.T) {
{
q := Conditions(
Where(SqlBuilder{{"id = 190"}}),
With("user", Conditions(
Where(SqlBuilder{{"posts.id = 190"}}),
WithCtx(&ctx),
WithFn(true, true, Conditions(
Fields("ID,user_login,user_pass"),
)),
), PostAuthor),
Fields("posts.*"),
From("wp_posts posts"),
With("meta", Conditions(
WithJoin(true),
)),
WithFn(false, true, nil, PostMetas),
)
ctx = context.WithValue(ctx, "ancestorsQueryCondition", q)
got, err := Gets[post](ctx, q)
_ = got
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) {

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) {
setTable[T](q)
sq, args, err := BuildQuerySql(q)
if err != nil {
if len(q.RelationFn) < 1 {
sq, args, er := BuildQuerySql(q)
if err != nil {
err = er
return
}
err = db.Select(ctx, &r, sq, args...)
return
}
err = db.Select(ctx, &r, sq, args...)
err = parseRelation(true, db, ctx, &r, q)
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) {
setTable[T](q)
if len(q.Relation) < 1 {
if len(q.RelationFn) < 1 {
s, args, er := BuildQuerySql(q)
if er != nil {
err = er

View File

@ -2,9 +2,9 @@ package model
import (
"context"
"database/sql"
"fmt"
"github.com/fthvgb1/wp-go/helper"
"reflect"
"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) {
var fn []func()
var fns []func() error
t := reflect.TypeOf(r).Elem()
v := reflect.ValueOf(r).Elem()
for tableTag, relation := range q.Relation {
if tableTag == "" {
continue
}
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 {
for _, f := range q.RelationFn {
getVal, isJoin, qq, ff := f()
idFn, assignment, rr, ship := ff()
if isJoin {
fn = append(fn, func() {
tables := strings.Split(ship.Table, " ")
from := strings.Split(q.From, " ")
fn = append(fn, func() {
qq := helper.GetContextVal(ctx, "ancestorsQueryCondition", q)
qq.Join = append(q.Join, SqlBuilder{
{"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
}
on := ""
if ship.On != "" {
on = fmt.Sprintf("and %s", on)
}
var err error
vv := reflect.New(v.Field(i).Type().Elem()).Interface()
switch tag.Get("relation") {
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
qq := helper.GetContextVal(ctx, "ancestorsQueryCondition", q)
qq.Join = append(qq.Join, []string{
"left join", ship.Table, fmt.Sprintf("%s.%s=%s.%s %s", tables[len(tables)-1], ship.ForeignKey, from[len(from)-1], ship.Local, on)})
})
}
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
}