From 9a8f2698e726db25c816ce03528c4c997d439a0d Mon Sep 17 00:00:00 2001 From: Matrix-X Date: Tue, 19 Jul 2022 11:49:25 +0800 Subject: [PATCH 1/2] feature(database): pivot with query builder --- database/pivot.go | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/database/pivot.go b/database/pivot.go index fafe97a..4ec4446 100644 --- a/database/pivot.go +++ b/database/pivot.go @@ -175,24 +175,36 @@ func SyncMorphAssociates(db *gorm.DB, pivot ModelInterface, return err } -func SelectMorphPivot(db *gorm.DB, pivot ModelInterface, +// select many pivots with foreign key +func SelectMorphPivots(db *gorm.DB, pivot ModelInterface, foreignKey string, foreignValue string, - joinKey string, joinValue string, ownerKey string, ownerValue string, ) (result *gorm.DB) { result = &gorm.DB{} // join foreign type if exists - db = SelectMorphPivots(db, pivot, foreignKey, foreignValue, ownerKey, ownerValue) - - result = db.Where(joinKey, joinValue) + strWhereOwner := "" + strWhere := " WHERE " + foreignKey + "=?" + if ownerKey != "" && ownerValue != "" { + strWhereOwner = " AND " + ownerKey + "=" + ownerValue + strWhere += " ?" + result = db. + Debug(). + Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue, strWhereOwner) + } else { + result = db. + Debug(). + Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue) + } return result } -func SelectMorphPivots(db *gorm.DB, pivot ModelInterface, +// select one pivot with foreign key and join key +func SelectMorphPivot(db *gorm.DB, pivot ModelInterface, foreignKey string, foreignValue string, + joinKey string, joinValue string, ownerKey string, ownerValue string, ) (result *gorm.DB) { @@ -200,22 +212,23 @@ func SelectMorphPivots(db *gorm.DB, pivot ModelInterface, // join foreign type if exists strWhereOwner := "" - strWhere := " WHERE " + foreignKey + "=?" + strWhere := " WHERE " + foreignKey + "=?" + " AND " + joinKey + "=?" if ownerKey != "" && ownerValue != "" { strWhereOwner = " AND " + ownerKey + "=" + ownerValue strWhere += " ?" result = db. Debug(). - Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue, strWhereOwner) + Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue, joinValue, strWhereOwner) } else { result = db. Debug(). - Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue) + Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue, joinValue) } return result } +// save one pivot with foreign key and join key func SaveMorphPivot(db *gorm.DB, pivot ModelInterface, foreignKey string, foreignValue string, joinKey string, joinValue string, @@ -238,6 +251,7 @@ func SaveMorphPivot(db *gorm.DB, pivot ModelInterface, return db.Error } +// update one pivot with foreign key and join key func UpdateMorphPivot(db *gorm.DB, pivot ModelInterface, foreignKey string, foreignValue string, joinKey string, joinValue string, @@ -265,6 +279,7 @@ func UpdateMorphPivot(db *gorm.DB, pivot ModelInterface, return db.Error } +// clear all pivots with foreign key and value func ClearPivots(db *gorm.DB, pivot ModelInterface, foreignKey string, foreignValue string) (err error) { result := db. Debug(). From 1767da7fe8ab9d852145b96bb2c67b782026b943 Mon Sep 17 00:00:00 2001 From: Matrix-X Date: Tue, 19 Jul 2022 18:07:05 +0800 Subject: [PATCH 2/2] refact(database): pivot with PivotInterface --- database/model.go | 37 ++++- database/pivot.go | 285 ++++++++++++++++++--------------------- database/rTagToObject.go | 55 +++++++- 3 files changed, 212 insertions(+), 165 deletions(-) diff --git a/database/model.go b/database/model.go index 04f73a4..62bb9b8 100644 --- a/database/model.go +++ b/database/model.go @@ -2,7 +2,6 @@ package database import ( "database/sql" - "fmt" "github.com/ArtisanCloud/PowerLibs/v2/object" "github.com/google/uuid" "gorm.io/gorm" @@ -19,7 +18,8 @@ type ModelInterface interface { GetID() int32 GetUUID() string GetPrimaryKey() string - GetForeignKey() string + GetForeignRefer() string + GetForeignReferValue() string } type PowerModel struct { @@ -89,8 +89,12 @@ func (mdl *PowerModel) GetUUID() string { func (mdl *PowerModel) GetPrimaryKey() string { return "uuid" } -func (mdl *PowerModel) GetForeignKey() string { - return "model_uuid" + +func (mdl *PowerModel) GetForeignRefer() string { + return "uuid" +} +func (mdl *PowerModel) GetForeignReferValue() string { + return mdl.UUID } /** @@ -215,7 +219,7 @@ func GetModelFields(model interface{}) (fields []string) { return (*ArrayModelFields)[modelName].([]string) } - fmt.Printf("parse object ~%s~ model fields \n", modelName) + //fmt.Printf("parse object ~%s~ model fields \n", modelName) gormSchema, err := schema.Parse(model, &sync.Map{}, schema.NamingStrategy{}) if err != nil { println(err) @@ -229,11 +233,30 @@ func GetModelFields(model interface{}) (fields []string) { } } (*ArrayModelFields)[modelName] = fields - fmt.Printf("parsed object ~%s~ model fields and fields count is %d \n\n", modelName, len(fields)) + //fmt.Printf("parsed object ~%s~ model fields and fields count is %d \n\n", modelName, len(fields)) return fields } +func GetModelFieldValues(model interface{}) (mapFields *object.HashMap, err error) { + + //fmt.Printf("parse object ~%s~ model fields \n", modelName) + gormSchema, err := schema.Parse(model, &sync.Map{}, schema.NamingStrategy{}) + if err != nil { + println(err) + return mapFields, err + } + + mapFields = &object.HashMap{} + for _, field := range gormSchema.Fields { + if field.DBName != "" && !field.PrimaryKey && !field.Unique && field.Updatable { + (*mapFields)[field.DBName] = field.ValueOf + } + } + + return mapFields, err +} + func IsPowerModelLoaded(mdl ModelInterface) bool { if object.IsObjectNil(mdl) { return false @@ -251,7 +274,7 @@ func IsPowerModelLoaded(mdl ModelInterface) bool { return true } -func IsPowerRelationshipLoaded(mdl ModelInterface) bool { +func IsPowerPivotLoaded(mdl ModelInterface) bool { if object.IsObjectNil(mdl) { return false diff --git a/database/pivot.go b/database/pivot.go index 4ec4446..fe24261 100644 --- a/database/pivot.go +++ b/database/pivot.go @@ -2,47 +2,88 @@ package database import ( "errors" + fmt2 "fmt" + "github.com/ArtisanCloud/PowerLibs/v2/fmt" "github.com/ArtisanCloud/PowerLibs/v2/object" "gorm.io/gorm" "gorm.io/gorm/clause" "time" ) -type PowerRelationship struct { +type PivotInterface interface { + ModelInterface + GetForeignKey() string + GetForeignValue() string + GetJoinKey() string + GetJoinValue() string + GetOwnerKey() string + GetOwnerValue() string + GetPivotComposedUniqueID() string +} + +type PowerPivot struct { ID int32 `gorm:"AUTO_INCREMENT;PRIMARY_KEY;not null" json:"id"` CreatedAt time.Time `gorm:"column:created_at; ->;<-:create " json:"createdAt"` UpdatedAt time.Time `gorm:"column:updated_at" json:"updatedAt"` } -func NewPowerRelationship() *PowerRelationship { +func NewPowerPivot() *PowerPivot { now := time.Now() - return &PowerRelationship{ + return &PowerPivot{ CreatedAt: now, UpdatedAt: now, } } // -------------------------------------------------------------------- -func (mdl *PowerRelationship) GetTableName(needFull bool) string { +func (mdl *PowerPivot) GetTableName(needFull bool) string { return "" } -func (mdl *PowerRelationship) GetPowerModel() ModelInterface { +func (mdl *PowerPivot) GetPowerModel() ModelInterface { return mdl } -func (mdl *PowerRelationship) GetID() int32 { +func (mdl *PowerPivot) GetID() int32 { return mdl.ID } -func (mdl *PowerRelationship) GetUUID() string { +func (mdl *PowerPivot) GetUUID() string { return "" } -func (mdl *PowerRelationship) GetPrimaryKey() string { +func (mdl *PowerPivot) GetPrimaryKey() string { return "id" } -func (mdl *PowerRelationship) GetForeignKey() string { - return "model_id" +func (mdl *PowerPivot) GetForeignRefer() string { + return "id" +} +func (mdl *PowerPivot) GetForeignReferValue() string { + return fmt2.Sprintf("%d", mdl.ID) +} + +func (mdl *PowerPivot) GetForeignKey() string { + return "foreign_key" +} +func (mdl *PowerPivot) GetForeignValue() string { + return "foreign_id" +} + +func (mdl *PowerPivot) GetJoinKey() string { + return "join_key" +} +func (mdl *PowerPivot) GetJoinValue() string { + return "join_id" +} + +func (mdl *PowerPivot) GetOwnerKey() string { + return "owner_key" +} +func (mdl *PowerPivot) GetOwnerValue() string { + return "owner_value" +} + +func (mdl *PowerPivot) GetPivotComposedUniqueID() string { + return mdl.GetOwnerValue() + "-" + mdl.GetForeignValue() + "-" + mdl.GetJoinValue() } func GetPivotComposedUniqueID(foreignValue string, joinValue string) object.NullString { @@ -55,11 +96,13 @@ func GetPivotComposedUniqueID(foreignValue string, joinValue string) object.Null } /** - * Association Relationship + * Association Pivot */ func AssociationRelationship(db *gorm.DB, conditions *map[string]interface{}, mdl interface{}, relationship string, withClauseAssociations bool) *gorm.Association { - tx := db.Model(mdl) + tx := db. + Debug(). + Model(mdl) if withClauseAssociations { tx.Preload(clause.Associations) @@ -72,7 +115,7 @@ func AssociationRelationship(db *gorm.DB, conditions *map[string]interface{}, md return tx.Association(relationship) } -func ClearAssociations(db *gorm.DB, object ModelInterface, foreignKey string, pivot ModelInterface) error { +func ClearAssociations(db *gorm.DB, object ModelInterface, foreignKey string, pivot PivotInterface) error { result := db.Exec("DELETE FROM "+pivot.GetTableName(true)+" WHERE "+foreignKey+"=?", object.GetID()) if result.Error != nil { return result.Error @@ -81,69 +124,28 @@ func ClearAssociations(db *gorm.DB, object ModelInterface, foreignKey string, pi } // -------------------------------------------------------------------- -func AppendAssociates(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValues []string) (err error) { - - return AppendMorphAssociates(db, pivot, foreignKey, foreignValue, joinKey, joinValues, "", "") -} -func SyncAssociates(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValues []string) (err error) { - return SyncMorphAssociates(db, pivot, foreignKey, foreignValue, joinKey, joinValues, "", "") -} - -func SelectPivots(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValue string) (result *gorm.DB) { - - return SelectMorphPivots(db, pivot, foreignKey, foreignValue, "", "") -} - -func SelectPivot(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValue string) (result *gorm.DB) { - - return SelectMorphPivot(db, pivot, foreignKey, foreignValue, joinKey, joinValue, "", "") -} - -func SavePivot(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValue string) (err error) { - return SaveMorphPivot(db, pivot, foreignKey, foreignValue, joinKey, joinValue, "", "") -} - -func UpdatePivot(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValue string) (err error) { - return UpdateMorphPivot(db, pivot, foreignKey, foreignValue, joinKey, joinValue, "", "") -} - -// -------------------------------------------------------------------- - -func AppendMorphAssociates(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValues []string, - ownerKey string, ownerValue string, -) (err error) { +func AppendMorphPivots(db *gorm.DB, pivots []PivotInterface) (err error) { var result = &gorm.DB{} err = db.Transaction(func(tx *gorm.DB) error { - for i := 0; i < len(joinValues); i++ { + for i := 0; i < len(pivots); i++ { - result = SelectMorphPivot(db, pivot, foreignKey, foreignValue, joinKey, joinValues[i], ownerKey, ownerValue) + result = SelectMorphPivot(db, pivots[i]) if result.Error != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return err } + + //err = UpsertPivots(db, pivots[i].GetPivotComposedUniqueID(), []PivotInterface{pivots[i]}, nil) + if result.RowsAffected == 0 || result.Error == gorm.ErrRecordNotFound { - err = SaveMorphPivot(db, pivot, foreignKey, foreignValue, joinKey, joinValues[i], ownerKey, ownerValue) + err = SavePivot(db, pivots[i]) if err != nil { return err } } else { - err = UpdateMorphPivot(db, pivot, foreignKey, foreignValue, joinKey, joinValues[i], ownerKey, ownerValue) + err = UpdatePivot(db, pivots[i]) if err != nil { return err } @@ -155,19 +157,19 @@ func AppendMorphAssociates(db *gorm.DB, pivot ModelInterface, return err } -func SyncMorphAssociates(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValues []string, - ownerKey string, ownerValue string, -) (err error) { - +func SyncMorphPivots(db *gorm.DB, pivots []PivotInterface) (err error) { + if len(pivots) <= 0 { + fmt.Dump("pivots is empty") + return nil + } err = db.Transaction(func(tx *gorm.DB) error { - err = ClearPivots(db, pivot, foreignKey, foreignValue) + err = ClearPivots(db, pivots[0]) if err != nil { return err } - err = AppendMorphAssociates(db, pivot, foreignKey, foreignValue, joinKey, joinValues, ownerKey, ownerValue) + + err = AppendMorphPivots(db, pivots) return err }) @@ -175,115 +177,96 @@ func SyncMorphAssociates(db *gorm.DB, pivot ModelInterface, return err } +// -------------------------------------------------------------------- +func AppendPivots(db *gorm.DB, pivots []PivotInterface) (err error) { + + return AppendMorphPivots(db, pivots) +} +func SyncPivots(db *gorm.DB, pivots []PivotInterface) (err error) { + + return SyncMorphPivots(db, pivots) +} + +func SelectPivots(db *gorm.DB, pivot PivotInterface) (result *gorm.DB) { + + return SelectMorphPivots(db, pivot) +} + +func SelectPivot(db *gorm.DB, pivot PivotInterface) (result *gorm.DB) { + + return SelectMorphPivot(db, pivot) +} + +// -------------------------------------------------------------------- + // select many pivots with foreign key -func SelectMorphPivots(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - ownerKey string, ownerValue string, -) (result *gorm.DB) { +func SelectMorphPivots(db *gorm.DB, pivot PivotInterface) (result *gorm.DB) { - result = &gorm.DB{} + result = db.Model(pivot). + Debug(). + Where(pivot.GetForeignKey(), pivot.GetForeignValue()) // join foreign type if exists - strWhereOwner := "" - strWhere := " WHERE " + foreignKey + "=?" - if ownerKey != "" && ownerValue != "" { - strWhereOwner = " AND " + ownerKey + "=" + ownerValue - strWhere += " ?" - result = db. - Debug(). - Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue, strWhereOwner) - } else { - result = db. - Debug(). - Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue) + if pivot.GetOwnerValue() != "" { + db = db.Where(pivot.GetOwnerKey(), pivot.GetOwnerValue()) } return result } // select one pivot with foreign key and join key -func SelectMorphPivot(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValue string, - ownerKey string, ownerValue string, -) (result *gorm.DB) { +func SelectMorphPivot(db *gorm.DB, pivot PivotInterface) (result *gorm.DB) { - result = &gorm.DB{} - - // join foreign type if exists - strWhereOwner := "" - strWhere := " WHERE " + foreignKey + "=?" + " AND " + joinKey + "=?" - if ownerKey != "" && ownerValue != "" { - strWhereOwner = " AND " + ownerKey + "=" + ownerValue - strWhere += " ?" - result = db. - Debug(). - Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue, joinValue, strWhereOwner) - } else { - result = db. - Debug(). - Exec("select * from "+pivot.GetTableName(true)+strWhere, foreignValue, joinValue) - } + result = SelectMorphPivots(db, pivot) + result = result.Where(pivot.GetJoinKey(), pivot.GetJoinValue()).Find(&pivot) return result } -// save one pivot with foreign key and join key -func SaveMorphPivot(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValue string, - ownerKey string, ownerValue string, -) (err error) { - now := time.Now() +func UpsertPivots(db *gorm.DB, uniqueName string, pivots []PivotInterface, fieldsToUpdate []string) error { - if ownerKey != "" && ownerValue != "" { - strValue := " (" + foreignKey + ", " + joinKey + ", " + ownerKey + ", created_at, updated_at ) VALUES (?, ?, ?, ?, ?)" - db = db. - Debug(). - Exec("INSERT INTO "+pivot.GetTableName(true)+strValue, foreignValue, joinValue, ownerValue, now, now) - } else { - strValue := " (" + foreignKey + ", " + joinKey + ", created_at, updated_at ) VALUES (?, ?, ?, ?)" - db = db. - Debug(). - Exec("INSERT INTO "+pivot.GetTableName(true)+strValue, foreignValue, joinValue, now, now) + if len(pivots) <= 0 { + fmt.Dump("pivots is empty") + return nil } - return db.Error + if len(fieldsToUpdate) <= 0 { + fieldsToUpdate = GetModelFields(&pivots[0]) + } + + result := db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: uniqueName}}, + DoUpdates: clause.AssignmentColumns(fieldsToUpdate), + }). + Debug(). + Create(&pivots) + + return result.Error } -// update one pivot with foreign key and join key -func UpdateMorphPivot(db *gorm.DB, pivot ModelInterface, - foreignKey string, foreignValue string, - joinKey string, joinValue string, - ownerKey string, ownerValue string, -) (err error) { - now := time.Now() +func SavePivot(db *gorm.DB, pivot PivotInterface) error { + result := db. + Debug(). + Create(pivot) - strSet := " SET updated_at = ?" + return result.Error +} - // join foreign type if exists - strWhere := " WHERE " + foreignKey + " = ? AND " + joinKey + "=?" - strWhereOwner := "" - if ownerKey != "" && ownerValue != "" { - strWhereOwner = " AND " + ownerKey + "=" + ownerValue - strWhere += " ?" - db = db. - Debug(). - Exec("UPDATE "+pivot.GetTableName(true)+strSet+strWhere, now, foreignValue, joinValue, strWhereOwner) - } else { - db = db. - Debug(). - Exec("UPDATE "+pivot.GetTableName(true)+strSet+strWhere, now, foreignValue, joinValue) - } +func UpdatePivot(db *gorm.DB, pivot PivotInterface) error { + result := db. + Debug(). + Save(pivot) - return db.Error + return result.Error } // clear all pivots with foreign key and value -func ClearPivots(db *gorm.DB, pivot ModelInterface, foreignKey string, foreignValue string) (err error) { +func ClearPivots(db *gorm.DB, pivot PivotInterface) (err error) { result := db. Debug(). - Exec("DELETE FROM "+pivot.GetTableName(true)+" WHERE "+foreignKey+"=?", foreignValue) + Where(pivot.GetForeignKey(), pivot.GetForeignValue()). + Delete(pivot) + if result.Error != nil { return result.Error } diff --git a/database/rTagToObject.go b/database/rTagToObject.go index a03b206..c44463b 100644 --- a/database/rTagToObject.go +++ b/database/rTagToObject.go @@ -1,6 +1,7 @@ package database import ( + "fmt" "github.com/ArtisanCloud/PowerLibs/v2/object" "gorm.io/gorm" ) @@ -12,7 +13,7 @@ func (mdl *RTagToObject) TableName() string { // r_tag_to_object 数据表结构 type RTagToObject struct { - *PowerRelationship + *PowerPivot //common fields UniqueID object.NullString `gorm:"index:index_taggable_object_id;index:index_taggable_id;index;column:index_tag_to_object_id;unique"` @@ -37,14 +38,54 @@ func (mdl *RTagToObject) GetTableName(needFull bool) string { return tableName } -func (mdl *RTagToObject) GetPivots(db *gorm.DB, foreignValue string, joinValue string, ownerValue string) ([]*RTagToObject, error) { +func (mdl *RTagToObject) GetForeignKey() string { + return R_TAG_TO_OJECT_FOREIGN_KEY +} + +func (mdl *RTagToObject) GetForeignValue() string { + return mdl.TaggableObjectID.String +} + +func (mdl *RTagToObject) GetJoinKey() string { + return R_TAG_TO_OJECT_JOIN_KEY +} + +func (mdl *RTagToObject) GetJoinValue() string { + return mdl.TaggableID.String +} + +func (mdl *RTagToObject) GetOwnerKey() string { + return R_TAG_TO_OJECT_OWNER_KEY +} + +func (mdl *RTagToObject) GetOwnerValue() string { + return mdl.TaggableOwnerType.String +} + +func (mdl *RTagToObject) GetPivotComposedUniqueID() string { + return mdl.GetOwnerValue() + "-" + mdl.GetForeignValue() + "-" + mdl.GetJoinValue() +} + +// --------------------------------------------------------------------------------------------------------------------- + +func (mdl *RTagToObject) MakePivotsFromObjectAndTags(obj ModelInterface, tags []*Tag) ([]PivotInterface, error) { + pivots := []PivotInterface{} + for _, tag := range tags { + pivot := &RTagToObject{ + TaggableOwnerType: object.NewNullString(obj.GetTableName(true), true), + TaggableObjectID: object.NewNullString(obj.GetForeignRefer(), true), + TaggableID: object.NewNullString(fmt.Sprintf("%d", tag.ID), true), + } + pivot.UniqueID = object.NewNullString(pivot.GetPivotComposedUniqueID(), true) + pivots = append(pivots, pivot) + } + return pivots, nil +} + +func (mdl *RTagToObject) GetPivots(db *gorm.DB) ([]*RTagToObject, error) { pivots := []*RTagToObject{} - db = SelectMorphPivot(db, mdl, - R_TAG_TO_OJECT_FOREIGN_KEY, foreignValue, - R_TAG_TO_OJECT_JOIN_KEY, joinValue, - R_TAG_TO_OJECT_OWNER_KEY, ownerValue, - ) + db = SelectMorphPivot(db, mdl) result := db.Find(&pivots)