tercul-backend/db/migrations.go
Damir Mukimov 4957117cb6 Initial commit: Tercul Go project with comprehensive architecture
- Core Go application with GraphQL API using gqlgen
- Comprehensive data models for literary works, authors, translations
- Repository pattern with caching layer
- Authentication and authorization system
- Linguistics analysis capabilities with multiple adapters
- Vector search integration with Weaviate
- Docker containerization support
- Python data migration and analysis scripts
- Clean architecture with proper separation of concerns
- Production-ready configuration and middleware
- Proper .gitignore excluding vendor/, database files, and build artifacts
2025-08-13 07:42:32 +02:00

332 lines
10 KiB
Go

package db
import (
"gorm.io/gorm"
"tercul/logger"
"tercul/models"
)
// RunMigrations runs all database migrations
func RunMigrations(db *gorm.DB) error {
logger.LogInfo("Running database migrations")
// First, create all tables using GORM AutoMigrate
if err := createTables(db); err != nil {
logger.LogError("Failed to create tables", logger.F("error", err))
return err
}
// Then add indexes to improve query performance
if err := addIndexes(db); err != nil {
logger.LogError("Failed to add indexes", logger.F("error", err))
return err
}
logger.LogInfo("Database migrations completed successfully")
return nil
}
// createTables creates all database tables using GORM AutoMigrate
func createTables(db *gorm.DB) error {
logger.LogInfo("Creating database tables")
// Enable recommended extensions
if err := db.Exec("CREATE EXTENSION IF NOT EXISTS pg_trgm").Error; err != nil {
logger.LogError("Failed to enable pg_trgm extension", logger.F("error", err))
return err
}
// Create all models/tables
err := db.AutoMigrate(
// User-related models
&models.User{},
&models.UserProfile{},
&models.UserSession{},
&models.PasswordReset{},
&models.EmailVerification{},
// Literary models
&models.Work{},
&models.Translation{},
&models.Author{},
&models.Book{},
&models.Publisher{},
&models.Source{},
&models.Edition{},
&models.Series{},
&models.WorkSeries{},
// Organization models
&models.Tag{},
&models.Category{},
// Interaction models
&models.Comment{},
&models.Like{},
&models.Bookmark{},
&models.Collection{},
&models.Contribution{},
&models.InteractionEvent{},
// Location models
&models.Country{},
&models.City{},
&models.Place{},
&models.Address{},
&models.Language{},
// Linguistic models
&models.ReadabilityScore{},
&models.WritingStyle{},
&models.LinguisticLayer{},
&models.TextMetadata{},
&models.PoeticAnalysis{},
&models.Word{},
&models.Concept{},
&models.LanguageEntity{},
&models.TextBlock{},
&models.WordOccurrence{},
&models.EntityOccurrence{},
// Relationship models
&models.Edge{},
&models.Embedding{},
&models.Media{},
&models.BookWork{},
&models.AuthorCountry{},
&models.WorkAuthor{},
&models.BookAuthor{},
// System models
&models.Notification{},
&models.EditorialWorkflow{},
&models.Admin{},
&models.Vote{},
&models.Contributor{},
&models.HybridEntityWork{},
&models.ModerationFlag{},
&models.AuditLog{},
// Rights models
&models.Copyright{},
&models.CopyrightClaim{},
&models.Monetization{},
&models.License{},
// Analytics models
&models.WorkStats{},
&models.TranslationStats{},
&models.UserStats{},
&models.BookStats{},
&models.CollectionStats{},
&models.MediaStats{},
// Metadata models
&models.LanguageAnalysis{},
&models.Gamification{},
&models.Stats{},
&models.SearchDocument{},
// Psychological models
&models.Emotion{},
&models.Mood{},
&models.TopicCluster{},
)
if err != nil {
return err
}
logger.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
}
logger.LogInfo("Database indexes added successfully")
return nil
}