diff --git a/backend/migrations/060_exercises_list_scale_indexes.sql b/backend/migrations/060_exercises_list_scale_indexes.sql new file mode 100644 index 0000000..e9d54ca --- /dev/null +++ b/backend/migrations/060_exercises_list_scale_indexes.sql @@ -0,0 +1,33 @@ +-- Migration 060: Übungslisten bei großem Bestand (Ziel: Tausende Übungen, viele Filterkombinationen). +-- Ergänzt 058 (globale Sortierung / created_by): kleinere Partial-Indizes für häufige +-- Sichtbarkeits-Pfade der Bibliothek sowie Junction-Indizes für die List-Subqueries +-- (primary_focus_name / JSON-Aggregate mit is_primary). +-- +-- Bereits vorhanden und sinnvoll: UNIQUE(exercise_id, …) auf den M:N-Tabellen für EXISTS-Joins; +-- GIN auf exercises.search_vector (014); idx_exercises_exercise_kind (056). + +-- Official: OR-Zweig der Bibliothek — kompakter als Full-Table-Scan bei BitmapOr mit anderen Partial-Indizes +CREATE INDEX IF NOT EXISTS idx_exercises_list_official_updated +ON exercises (updated_at DESC) +WHERE visibility = 'official' + AND COALESCE(status, '') <> 'archived'; + +-- Club: häufig club_id + Sortierung nach updated_at (Mandanten-Bibliothek) +CREATE INDEX IF NOT EXISTS idx_exercises_list_club_updated +ON exercises (club_id, updated_at DESC) +WHERE visibility = 'club' + AND club_id IS NOT NULL + AND COALESCE(status, '') <> 'archived'; + +-- List-SELECT: Subqueries / json_agg sortieren zuerst nach is_primary (siehe exercises.py) +CREATE INDEX IF NOT EXISTS idx_exercise_focus_areas_exercise_primary +ON exercise_focus_areas (exercise_id, is_primary DESC NULLS LAST, focus_area_id); + +CREATE INDEX IF NOT EXISTS idx_exercise_style_directions_exercise_primary +ON exercise_style_directions (exercise_id, is_primary DESC NULLS LAST, style_direction_id); + +CREATE INDEX IF NOT EXISTS idx_exercise_training_types_exercise_primary +ON exercise_training_types (exercise_id, is_primary DESC NULLS LAST, training_type_id); + +CREATE INDEX IF NOT EXISTS idx_exercise_target_groups_exercise_primary +ON exercise_target_groups (exercise_id, is_primary DESC NULLS LAST, target_group_id); diff --git a/backend/version.py b/backend/version.py index 17eb7b6..4996b24 100644 --- a/backend/version.py +++ b/backend/version.py @@ -1,8 +1,8 @@ # Shinkan Jinkendo Version Information -APP_VERSION = "0.8.113" +APP_VERSION = "0.8.114" BUILD_DATE = "2026-05-12" -DB_SCHEMA_VERSION = "20260514059" +DB_SCHEMA_VERSION = "20260514060" MODULE_VERSIONS = { "legal_documents": "1.4.0", # Admin: Live-Vorschau pro Abschnitt + modale Vollvorschau (Editor + Dokumentenliste) @@ -21,7 +21,7 @@ MODULE_VERSIONS = { "groups": "0.1.0", "skills": "0.1.0", "methods": "0.1.0", - "exercises": "2.27.3", # load_combination_slots_for_exercise (gemeinsam mit GET Übung); Hydrate für Planung + "exercises": "2.27.4", # Migration 060: Listen-Skalierung (Partial + Junction is_primary) "training_units": "0.2.0", "training_programs": "0.1.0", "planning": "0.9.3", # GET training-units/:id Sektions-Items: combination_slots + Kandidaten-Titel für Druck/Run @@ -36,6 +36,13 @@ MODULE_VERSIONS = { } CHANGELOG = [ + { + "version": "0.8.114", + "date": "2026-05-14", + "changes": [ + "Migration 060: Skalierung GET /api/exercises — Partial-Indizes official/club (+ updated_at, ohne archiviert); Junction-Indizes (exercise_id, is_primary) für List-Subqueries.", + ], + }, { "version": "0.8.113", "date": "2026-05-14", diff --git a/docs/HANDOVER.md b/docs/HANDOVER.md index b5bca01..3312965 100644 --- a/docs/HANDOVER.md +++ b/docs/HANDOVER.md @@ -1,7 +1,7 @@ # Shinkan Jinkendo – Entwicklungsstand & Handover **Stand:** 2026-05-14 -**App-Version / DB-Schema:** App **0.8.113**, DB-Schema **`20260514059`** (`backend/version.py`: `APP_VERSION`, `DB_SCHEMA_VERSION`) +**App-Version / DB-Schema:** App **0.8.114**, DB-Schema **`20260514060`** (`backend/version.py`: `APP_VERSION`, `DB_SCHEMA_VERSION`) Diese Datei ist die **Einstiegs-Doku für neue Chat-Sessions**: Anforderungen im Detail stehen in `.claude/docs/` (siehe unten); hier der **implementierte Stand**, **Medien-Meilenstein** und **sinnvolle nächste Schritte**. @@ -76,7 +76,7 @@ Das Schema ist gegenüber dem Code zurück: Migration **`022_skills_schema_compl - **036 / 037:** Bibliotheks-Rahmen, Slot-Inhalt als **`training_units`** mit **`framework_slot_id`**; **`POST /api/training-units/from-framework-slot`**. - **Code:** `training_framework_programs.py`, `training_planning.py`; Frontend **`TrainingFrameworkProgramEditPage.jsx`**, **`createTrainingUnitFromFrameworkSlot`** in `api.js`. -### Trainingsmodule, Kombinationsübungen und Coach (Stand **0.8.113**) +### Trainingsmodule, Kombinationsübungen und Coach (Stand **0.8.114**) - **Fachspez & Drift-Schutz:** `.claude/docs/functional/Shinkan Trainingsmodule Kombinationsuebungen Spezifikation V2.md` (**§ 10.2.1** IDs, **§ 10.4** Coaching-Stufen, **§ 10.6** Produkt-Backlog, **Anhang A** Abgleich). - **Umsetzungsplan:** `.claude/docs/working/TRAINING_MODULES_IMPLEMENTATION_PLAN.md` (Phase **2** / **4** teilweise; Pakete **4a–g** — u. a. **4e** Archetyp-Admin, **4f** Massen-Vorbelegung, **4g** Backend-Validierung). diff --git a/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md b/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md index dbd3f30..e251af0 100644 --- a/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md +++ b/docs/architecture/UMSETZUNGSPLAN_ROADMAP.md @@ -64,7 +64,7 @@ | Summary-API finalisieren/erweitern falls in P1 nur Teilbereich | B1 | | Optional: erste Keyset-Pagination für eine Liste mit bekanntem Sort-Key | B3 | -**Teil erledigt (2026-05-14):** Migration **058** (`exercises`: `updated_at` / `created_by`+`updated_at`), **059** (`training_units`: Sortierung der Kalenderliste, nur Zeilen ohne `framework_slot_id`). Rest: `EXPLAIN` unter echtem Volumen, ggf. weitere Indizes. +**Teil erledigt (2026-05-14):** Migration **058** (`exercises`: globale `updated_at`-Sortierung / `created_by`+`updated_at`), **059** (`training_units`: Kalenderliste ohne Blueprint), **060** (`exercises`: Partial-Indizes `official`/`club` inkl. Archiv-Filter; Junction `is_primary` für List-Subqueries). Rest: `EXPLAIN` unter Produktionsvolumen, Fähigkeits-Level-Filter nur bei Bedarf (ggf. Ausdrucks-Index). **Abnahme:** p95 der optimierten Routen **verbessert** ggü. Phase 0 oder dokumentierte Obergrenze eingehalten.