tercul-backend/internal/platform/db/migrations.go
Damir Mukimov fa336cacf3
wip
2025-09-01 00:43:59 +02:00

332 lines
10 KiB
Go

package db
import (
"gorm.io/gorm"
models2 "tercul/internal/models"
"tercul/internal/platform/log"
)
// RunMigrations runs all database migrations
func RunMigrations(db *gorm.DB) error {
log.LogInfo("Running database migrations")
// First, create all tables using GORM AutoMigrate
if err := createTables(db); err != nil {
log.LogError("Failed to create tables", log.F("error", err))
return err
}
// Then add indexes to improve query performance
if err := addIndexes(db); err != nil {
log.LogError("Failed to add indexes", log.F("error", err))
return err
}
log.LogInfo("Database migrations completed successfully")
return nil
}
// createTables creates all database tables using GORM AutoMigrate
func createTables(db *gorm.DB) error {
log.LogInfo("Creating database tables")
// Enable recommended extensions
if err := db.Exec("CREATE EXTENSION IF NOT EXISTS pg_trgm").Error; err != nil {
log.LogError("Failed to enable pg_trgm extension", log.F("error", err))
return err
}
// Create all models/tables
err := db.AutoMigrate(
// User-related models
&models2.User{},
&models2.UserProfile{},
&models2.UserSession{},
&models2.PasswordReset{},
&models2.EmailVerification{},
// Literary models
&models2.Work{},
&models2.Translation{},
&models2.Author{},
&models2.Book{},
&models2.Publisher{},
&models2.Source{},
&models2.Edition{},
&models2.Series{},
&models2.WorkSeries{},
// Organization models
&models2.Tag{},
&models2.Category{},
// Interaction models
&models2.Comment{},
&models2.Like{},
&models2.Bookmark{},
&models2.Collection{},
&models2.Contribution{},
&models2.InteractionEvent{},
// Location models
&models2.Country{},
&models2.City{},
&models2.Place{},
&models2.Address{},
&models2.Language{},
// Linguistic models
&models2.ReadabilityScore{},
&models2.WritingStyle{},
&models2.LinguisticLayer{},
&models2.TextMetadata{},
&models2.PoeticAnalysis{},
&models2.Word{},
&models2.Concept{},
&models2.LanguageEntity{},
&models2.TextBlock{},
&models2.WordOccurrence{},
&models2.EntityOccurrence{},
// Relationship models
&models2.Edge{},
&models2.Embedding{},
&models2.Media{},
&models2.BookWork{},
&models2.AuthorCountry{},
&models2.WorkAuthor{},
&models2.BookAuthor{},
// System models
&models2.Notification{},
&models2.EditorialWorkflow{},
&models2.Admin{},
&models2.Vote{},
&models2.Contributor{},
&models2.HybridEntityWork{},
&models2.ModerationFlag{},
&models2.AuditLog{},
// Rights models
&models2.Copyright{},
&models2.CopyrightClaim{},
&models2.Monetization{},
&models2.License{},
// Analytics models
&models2.WorkStats{},
&models2.TranslationStats{},
&models2.UserStats{},
&models2.BookStats{},
&models2.CollectionStats{},
&models2.MediaStats{},
// Metadata models
&models2.LanguageAnalysis{},
&models2.Gamification{},
&models2.Stats{},
&models2.SearchDocument{},
// Psychological models
&models2.Emotion{},
&models2.Mood{},
&models2.TopicCluster{},
)
if err != nil {
return err
}
log.LogInfo("Database tables created successfully")
return nil
}
// addIndexes adds indexes to frequently queried columns
func addIndexes(db *gorm.DB) error {
// Work table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_language ON works(language)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_title ON works(title)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_status ON works(status)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_slug ON works(slug)").Error; err != nil {
return err
}
// Translation table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_translations_work_id ON translations(work_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_translations_language ON translations(language)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_translations_translator_id ON translations(translator_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE UNIQUE INDEX IF NOT EXISTS ux_translations_entity_lang ON translations(translatable_type, translatable_id, language)").Error; err != nil {
return err
}
// User table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_users_username ON users(username)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_users_role ON users(role)").Error; err != nil {
return err
}
// Author table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_authors_name ON authors(name)").Error; err != nil {
return err
}
// Category table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_categories_name ON categories(name)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_categories_slug ON categories(slug)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_categories_path ON categories(path)").Error; err != nil {
return err
}
// Tag table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_tags_slug ON tags(slug)").Error; err != nil {
return err
}
// Comment table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_comments_user_id ON comments(user_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_comments_work_id ON comments(work_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_comments_translation_id ON comments(translation_id)").Error; err != nil {
return err
}
// Like table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_likes_user_id ON likes(user_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_likes_work_id ON likes(work_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_likes_translation_id ON likes(translation_id)").Error; err != nil {
return err
}
// Bookmark table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_bookmarks_user_id ON bookmarks(user_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_bookmarks_work_id ON bookmarks(work_id)").Error; err != nil {
return err
}
// Collection table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_collections_user_id ON collections(user_id)").Error; err != nil {
return err
}
// Contribution table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_contributions_user_id ON contributions(user_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_contributions_work_id ON contributions(work_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_contributions_status ON contributions(status)").Error; err != nil {
return err
}
// Edge table indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_edges_source_table_id ON edges(source_table, source_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_edges_target_table_id ON edges(target_table, target_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_edges_relation ON edges(relation)").Error; err != nil {
return err
}
// WorkAuthor unique pair and order index
if err := db.Exec("CREATE UNIQUE INDEX IF NOT EXISTS ux_work_authors_pair ON work_authors(work_id, author_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_work_authors_ordinal ON work_authors(ordinal)").Error; err != nil {
return err
}
// BookAuthor unique pair and order index
if err := db.Exec("CREATE UNIQUE INDEX IF NOT EXISTS ux_book_authors_pair ON book_authors(book_id, author_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_book_authors_ordinal ON book_authors(ordinal)").Error; err != nil {
return err
}
// InteractionEvent indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_interaction_events_target ON interaction_events(target_type, target_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_interaction_events_kind ON interaction_events(kind)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_interaction_events_user ON interaction_events(user_id)").Error; err != nil {
return err
}
// SearchDocument indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_search_documents_entity ON search_documents(entity_type, entity_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_search_documents_lang ON search_documents(language_code)").Error; err != nil {
return err
}
// Linguistic analysis indexes
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_text_metadata_work_id ON text_metadata(work_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_readability_scores_work_id ON readability_scores(work_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_language_analyses_work_id ON language_analyses(work_id)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_poetic_analyses_work_id ON poetic_analyses(work_id)").Error; err != nil {
return err
}
// Timestamps indexes for frequently queried tables
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_works_created_at ON works(created_at)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_translations_created_at ON translations(created_at)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_comments_created_at ON comments(created_at)").Error; err != nil {
return err
}
if err := db.Exec("CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at)").Error; err != nil {
return err
}
log.LogInfo("Database indexes added successfully")
return nil
}