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 }