优化 hasone/many不用反射
This commit is contained in:
parent
6caf07b575
commit
4242d850ff
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user