
Add Support for Custom Migration to keep supporting two database( sqlite and postgres) Migration from Label to LabelV2
123 lines
3.4 KiB
Go
123 lines
3.4 KiB
Go
package migrations
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"donetick.com/core/logging"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type MigrateLabels20241123 struct{}
|
|
|
|
func (m MigrateLabels20241123) ID() string {
|
|
return "20241123_migrate_label_to_labels_v2"
|
|
}
|
|
|
|
func (m MigrateLabels20241123) Description() string {
|
|
return `Migrate label to labels v2 table, Allow more advanced features with labels like assign color`
|
|
}
|
|
|
|
func (m MigrateLabels20241123) Down(ctx context.Context, db *gorm.DB) error {
|
|
return nil
|
|
}
|
|
|
|
func (m MigrateLabels20241123) Up(ctx context.Context, db *gorm.DB) error {
|
|
log := logging.FromContext(ctx)
|
|
|
|
type Label struct {
|
|
ID int `gorm:"column:id;primary_key"`
|
|
Name string `gorm:"column:name"`
|
|
CreatedBy int `gorm:"column:created_by"`
|
|
CircleID *int `gorm:"column:circle_id"`
|
|
ChoresId []int `gorm:"-"`
|
|
}
|
|
|
|
type Chore struct {
|
|
Labels *string `gorm:"column:labels"`
|
|
ID int `gorm:"column:id;primary_key"`
|
|
CircleID int `gorm:"column:circle_id"`
|
|
CreatedBy int `gorm:"column:created_by"`
|
|
}
|
|
|
|
type ChoreLabel struct {
|
|
ChoreID int `gorm:"column:chore_id"`
|
|
LabelID int `gorm:"column:label_id"`
|
|
UserID int `gorm:"column:user_id"`
|
|
}
|
|
|
|
// Start a transaction
|
|
return db.Transaction(func(tx *gorm.DB) error {
|
|
// Get all chores with labels
|
|
var choreRecords []Chore
|
|
if err := tx.Table("chores").Select("id, labels, circle_id, created_by").Find(&choreRecords).Error; err != nil {
|
|
log.Errorf("Failed to fetch chores with label: %v", err)
|
|
return err
|
|
}
|
|
|
|
// Map to store new labels
|
|
newLabelsMap := make(map[string]Label)
|
|
for _, choreRecord := range choreRecords {
|
|
if choreRecord.Labels != nil {
|
|
labels := strings.Split(*choreRecord.Labels, ",")
|
|
for _, label := range labels {
|
|
label = strings.TrimSpace(label)
|
|
if _, ok := newLabelsMap[label]; !ok {
|
|
newLabelsMap[label] = Label{
|
|
Name: label,
|
|
CreatedBy: choreRecord.CreatedBy,
|
|
CircleID: &choreRecord.CircleID,
|
|
ChoresId: []int{choreRecord.ID},
|
|
}
|
|
} else {
|
|
labelToUpdate := newLabelsMap[label]
|
|
labelToUpdate.ChoresId = append(labelToUpdate.ChoresId, choreRecord.ID)
|
|
newLabelsMap[label] = labelToUpdate
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Insert new labels and update chore_labels
|
|
for labelName, label := range newLabelsMap {
|
|
// Check if the label already exists
|
|
var existingLabel Label
|
|
if err := tx.Table("labels").Where("name = ? AND created_by = ? AND circle_id = ?", labelName, label.CreatedBy, label.CircleID).First(&existingLabel).Error; err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
// Insert new label
|
|
if err := tx.Table("labels").Create(&label).Error; err != nil {
|
|
log.Errorf("Failed to insert new label: %v", err)
|
|
return err
|
|
}
|
|
existingLabel = label
|
|
} else {
|
|
log.Errorf("Failed to check existing label: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Prepare chore_labels for batch insertion
|
|
var choreLabels []ChoreLabel
|
|
for _, choreId := range label.ChoresId {
|
|
choreLabels = append(choreLabels, ChoreLabel{
|
|
ChoreID: choreId,
|
|
LabelID: existingLabel.ID,
|
|
UserID: label.CreatedBy,
|
|
})
|
|
}
|
|
|
|
// Batch insert chore_labels
|
|
if err := tx.Table("chore_labels").Create(&choreLabels).Error; err != nil {
|
|
log.Errorf("Failed to insert chore labels: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// Register this migration
|
|
func init() {
|
|
Register(MigrateLabels20241123{})
|
|
}
|