-- Migration 014: Variant Progression System + Search Vector + Legacy Cleanup -- Autor: Claude Code -- Datum: 2026-04-24 -- Zweck: Varianten-Progression, Volltext-Suche, Legacy-Spalten entfernen DO $$ BEGIN -- ============================================================================ -- HELPER FUNCTION: update_timestamp (für Triggers) -- ============================================================================ CREATE OR REPLACE FUNCTION update_timestamp() RETURNS trigger AS $func$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $func$ LANGUAGE plpgsql; -- ============================================================================ -- VARIANT PROGRESSION -- ============================================================================ -- Erweitere exercise_variants Tabelle ALTER TABLE exercise_variants ADD COLUMN IF NOT EXISTS progression_level INT DEFAULT 1 CHECK (progression_level BETWEEN 1 AND 10), ADD COLUMN IF NOT EXISTS sequence_order INT, ADD COLUMN IF NOT EXISTS prerequisite_variant_id INT REFERENCES exercise_variants(id) ON DELETE SET NULL; -- Index für Prerequisites CREATE INDEX IF NOT EXISTS idx_exercise_variants_prerequisite ON exercise_variants(prerequisite_variant_id); -- ============================================================================ -- SEARCH VECTOR (Volltext-Suche) -- ============================================================================ -- Füge search_vector zu exercises hinzu ALTER TABLE exercises ADD COLUMN IF NOT EXISTS search_vector tsvector; -- Index für Volltext-Suche CREATE INDEX IF NOT EXISTS idx_exercises_search ON exercises USING gin(search_vector); -- Funktion für automatisches Update CREATE OR REPLACE FUNCTION update_exercises_search_vector() RETURNS trigger AS $func$ BEGIN NEW.search_vector := setweight(to_tsvector('german', COALESCE(NEW.title, '')), 'A') || setweight(to_tsvector('german', COALESCE(NEW.summary, '')), 'B') || setweight(to_tsvector('german', COALESCE(NEW.execution, '')), 'C') || setweight(to_tsvector('german', COALESCE(NEW.trainer_notes, '')), 'D'); RETURN NEW; END; $func$ LANGUAGE plpgsql; -- Trigger DROP TRIGGER IF EXISTS exercises_search_update ON exercises; CREATE TRIGGER exercises_search_update BEFORE INSERT OR UPDATE ON exercises FOR EACH ROW EXECUTE FUNCTION update_exercises_search_vector(); -- Initiales Befüllen (für existierende Zeilen) UPDATE exercises SET search_vector = ( setweight(to_tsvector('german', COALESCE(title, '')), 'A') || setweight(to_tsvector('german', COALESCE(summary, '')), 'B') || setweight(to_tsvector('german', COALESCE(execution, '')), 'C') || setweight(to_tsvector('german', COALESCE(trainer_notes, '')), 'D') ) WHERE search_vector IS NULL; -- ============================================================================ -- LEGACY COLUMN CLEANUP -- Deprecated Felder aus exercises (ersetzt durch M:N Tabellen in Migration 008+) -- ============================================================================ -- age_groups JSONB → ersetzt durch exercise_age_groups M:N (seit Migration 008) ALTER TABLE exercises DROP COLUMN IF EXISTS age_groups; -- focus_area VARCHAR → ersetzt durch exercise_focus_areas M:N (seit Migration 008) ALTER TABLE exercises DROP COLUMN IF EXISTS focus_area; -- secondary_areas JSONB → ersetzt durch exercise_focus_areas M:N ALTER TABLE exercises DROP COLUMN IF EXISTS secondary_areas; -- training_character VARCHAR → ersetzt durch exercise_training_characters M:N (seit Migration 012) ALTER TABLE exercises DROP COLUMN IF EXISTS training_character; -- ============================================================================ -- ADDITIONAL INDEXES (Performance) -- ============================================================================ -- Häufige Filter CREATE INDEX IF NOT EXISTS idx_exercises_visibility ON exercises(visibility); CREATE INDEX IF NOT EXISTS idx_exercises_status ON exercises(status); CREATE INDEX IF NOT EXISTS idx_exercises_created_at ON exercises(created_at DESC); -- M:N Relations (falls noch nicht vorhanden) CREATE INDEX IF NOT EXISTS idx_exercise_focus_areas_focus ON exercise_focus_areas(focus_area_id); CREATE INDEX IF NOT EXISTS idx_exercise_styles_style ON exercise_style_directions(style_direction_id); CREATE INDEX IF NOT EXISTS idx_exercise_target_groups_group ON exercise_target_groups(target_group_id); CREATE INDEX IF NOT EXISTS idx_exercise_skills_skill ON exercise_skills(skill_id); RAISE NOTICE 'Migration 014 completed successfully'; END $$;