db middle table

This commit is contained in:
xing 2023-05-24 20:41:24 +08:00
parent 9580678b55
commit d5a546c01a
4 changed files with 122 additions and 25 deletions

View File

@ -88,6 +88,7 @@ func (w SqlBuilder) ParseWhere(in *[][]any) (string, []any, error) {
args = append(args, ss[1]) args = append(args, ss[1])
case 3, 4: case 3, 4:
w.parseWhereField(ss, &s) w.parseWhereField(ss, &s)
s.WriteString(" ")
s.WriteString(ss[1]) s.WriteString(ss[1])
if w.parseIn(ss, &s, &c, &args, in) { if w.parseIn(ss, &s, &c, &args, in) {
s.WriteString(" and ") s.WriteString(" and ")

View File

@ -39,6 +39,7 @@ type post struct {
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 User *user
PostMeta *[]models.PostMeta PostMeta *[]models.PostMeta
TermTaxonomy *[]models.TermTaxonomy
} }
type TermRelationships struct { type TermRelationships struct {

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"github.com/fthvgb1/wp-go/helper" "github.com/fthvgb1/wp-go/helper"
"github.com/fthvgb1/wp-go/helper/slice" "github.com/fthvgb1/wp-go/helper/slice"
str "github.com/fthvgb1/wp-go/helper/strings"
"golang.org/x/exp/constraints" "golang.org/x/exp/constraints"
"strings" "strings"
) )
@ -36,25 +37,88 @@ type Relationship struct {
ForeignKey string ForeignKey string
Local string Local string
On string On string
Middle *Relationship
}
func parseBeforeJoin(qq *QueryCondition, ship Relationship) {
var fromTable, foreignKey, local string
if ship.Middle != nil {
parseBeforeJoin(qq, *ship.Middle)
fromTable = ship.Middle.Table
foreignKey = ship.ForeignKey
local = ship.Local
} else {
fromTable = qq.From
if ship.RelationType == HasMany {
foreignKey = ship.Local
local = ship.ForeignKey
} else {
foreignKey = ship.ForeignKey
local = ship.Local
}
}
tables := strings.Split(ship.Table, " ")
from := strings.Split(fromTable, " ")
on := ""
if ship.On != "" {
on = fmt.Sprintf("and %s", on)
}
qq.Join = append(qq.Join, []string{
"left join", ship.Table,
fmt.Sprintf("%s.%s=%s.%s %s",
tables[len(tables)-1], foreignKey, from[len(from)-1], local, on,
)})
}
func parseAfterJoin(ids [][]any, qq *QueryCondition, ship Relationship) bool {
tables := strings.Split(ship.Middle.Table, " ")
from := strings.Split(qq.From, " ")
on := ""
if ship.On != "" {
on = fmt.Sprintf("and %s", on)
}
foreignKey := ship.ForeignKey
local := ship.Local
if ship.RelationType == HasMany {
foreignKey = ship.Local
local = ship.ForeignKey
}
qq.Join = append(qq.Join, []string{
"left join", ship.Middle.Table,
fmt.Sprintf("%s.%s=%s.%s %s",
tables[len(tables)-1], foreignKey, from[len(from)-1], local, on,
),
})
if ship.Middle.Middle != nil {
return parseAfterJoin(ids, qq, *ship.Middle.Middle)
} else {
ww, ok := qq.Where.(SqlBuilder)
if ok {
ww = append(ww, []string{fmt.Sprintf("%s.%s",
tables[len(tables)-1], ship.Middle.Local), "in", ""},
)
qq.Where = ww
}
if qq.Fields == "" || qq.Fields == "*" {
qq.Fields = str.Join(from[len(from)-1], ".", "*")
}
qq.In = ids
return ship.Middle.RelationType == HasMany
}
} }
func Relation(isPlural bool, db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) { func Relation(isPlural bool, db dbQuery, ctx context.Context, r any, q *QueryCondition) ([]func(), []func() error) {
var beforeFn []func() var beforeFn []func()
var afterFn []func() error var afterFn []func() error
qx := helper.GetContextVal(ctx, "ancestorsQueryCondition", q)
for _, f := range q.RelationFn { for _, f := range q.RelationFn {
getVal, isJoin, qq, relationship := f() getVal, isJoin, qq, relationship := f()
idFn, assignmentFn, rr, rrs, ship := relationship() idFn, assignmentFn, rr, rrs, ship := relationship()
if isJoin { if isJoin {
beforeFn = append(beforeFn, func() { beforeFn = append(beforeFn, func() {
tables := strings.Split(ship.Table, " ") parseBeforeJoin(qx, ship)
from := strings.Split(q.From, " ")
on := ""
if ship.On != "" {
on = fmt.Sprintf("and %s", on)
}
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 { if !getVal {
@ -71,21 +135,27 @@ func Relation(isPlural bool, db dbQuery, ctx context.Context, r any, q *QueryCon
Fields: "*", 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{ids}
qq.Where = ww
}
if qq.From == "" { if qq.From == "" {
qq.From = ship.Table qq.From = ship.Table
} }
var w any = qq.Where
if w == nil {
qq.Where = SqlBuilder{}
}
ww, ok := qq.Where.(SqlBuilder)
in := [][]any{ids}
if ok {
if ship.Middle != nil {
isPlural = parseAfterJoin(in, qq, ship)
} else {
ww = append(ww, SqlBuilder{{
ship.ForeignKey, "in", "",
}}...)
qq.In = in
qq.Where = ww
}
}
err = ParseRelation(isPlural || ship.RelationType == HasMany, db, ctx, helper.Or(isPlural, rrs, rr), qq) err = ParseRelation(isPlural || ship.RelationType == HasMany, db, ctx, helper.Or(isPlural, rrs, rr), qq)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
@ -166,7 +236,9 @@ func SetHasMany[T, V any, K comparable](assignmentFn func(*T, *[]V), pIdFn func(
// RelationHasOne // RelationHasOne
// eg: post has a user. fId is post's userId, pId is user's id // eg: post has a user. fId is post's userId, pId is user's id
func RelationHasOne[M, P any, I constraints.Integer | uint64](fId func(*M) I, pId func(*P) I, setVal func(*M, *P), r Relationship) RelationFn { func RelationHasOne[M, P any, I constraints.Integer | constraints.Unsigned](
fId func(*M) I, pId func(*P) I, setVal func(*M, *P), r Relationship) RelationFn {
idFn := GetWithID(fId) idFn := GetWithID(fId)
setFn := SetHasOne(setVal, fId, pId) setFn := SetHasOne(setVal, fId, pId)
return func() (func(any) []any, func(any, any), any, any, Relationship) { return func() (func(any) []any, func(any, any), any, any, Relationship) {
@ -178,7 +250,9 @@ func RelationHasOne[M, P any, I constraints.Integer | uint64](fId func(*M) I, pI
// RelationHasMany // RelationHasMany
// eg: post has many comments,mId is comment's postId, pId is post's id // eg: post has many comments,mId is comment's postId, pId is post's id
func RelationHasMany[M, P any, I constraints.Integer | uint64](mId func(*M) I, pId func(*P) I, setVal func(*M, *[]P), r Relationship) RelationFn { func RelationHasMany[M, P any, I constraints.Integer | constraints.Unsigned](
mId func(*M) I, pId func(*P) I, setVal func(*M, *[]P), r Relationship) RelationFn {
idFn := GetWithID(mId) idFn := GetWithID(mId)
setFn := SetHasMany(setVal, mId, pId) setFn := SetHasMany(setVal, mId, pId)
return func() (func(any) []any, func(any, any), any, any, Relationship) { return func() (func(any) []any, func(any, any), any, any, Relationship) {

View File

@ -60,6 +60,25 @@ func PostMetas() (func(any) []any, func(any, any), any, any, Relationship) {
} }
} }
var term = RelationHasMany(func(m *post) uint64 {
return m.Id
}, func(p *models.TermTaxonomy) uint64 {
return p.TermTaxonomyId
}, func(m *post, i *[]models.TermTaxonomy) {
m.TermTaxonomy = i
}, Relationship{
RelationType: HasOne,
Table: "wp_term_taxonomy taxonomy",
ForeignKey: "term_taxonomy_id",
Local: "term_taxonomy_id",
Middle: &Relationship{
RelationType: HasMany,
Table: "wp_term_relationships",
ForeignKey: "ID",
Local: "object_id",
},
})
func Meta2() RelationFn { func Meta2() RelationFn {
return RelationHasMany(postId, metasPostId, func(m *post, i *[]models.PostMeta) { return RelationHasMany(postId, metasPostId, func(m *post, i *[]models.PostMeta) {
m.PostMeta = i m.PostMeta = i
@ -93,7 +112,8 @@ func TestGets2(t *testing.T) {
), PostAuthor2()), ), PostAuthor2()),
Fields("posts.*"), Fields("posts.*"),
From("wp_posts posts"), From("wp_posts posts"),
WithFn(true, true, nil, Meta2()), WithFn(true, false, nil, Meta2()),
WithFn(true, false, nil, term),
) )
got, err := Gets[post](ctx, q) got, err := Gets[post](ctx, q)
_ = got _ = got
@ -106,7 +126,7 @@ func TestGets2(t *testing.T) {
{ {
q := Conditions( q := Conditions(
Where(SqlBuilder{{"posts.id", "in", ""}}), Where(SqlBuilder{{"posts.id", "in", ""}}),
In([]any{190, 3022}), In([]any{190, 3022, 291}),
WithCtx(&ctx), WithCtx(&ctx),
WithFn(true, false, Conditions( WithFn(true, false, Conditions(
Fields("ID,user_login,user_pass"), Fields("ID,user_login,user_pass"),
@ -114,6 +134,7 @@ func TestGets2(t *testing.T) {
Fields("posts.*"), Fields("posts.*"),
From("wp_posts posts"), From("wp_posts posts"),
WithFn(true, false, nil, Meta2()), WithFn(true, false, nil, Meta2()),
WithFn(true, false, nil, term),
) )
got, err := Finds[post](ctx, q) got, err := Finds[post](ctx, q)
_ = got _ = got