schema: add composite index (is_published, year DESC) + fix stale migration 005

- Add idx_theses_pub_year composite index on theses(is_published, year DESC) to
  schema.sql; replaces the need for the query planner to pick between the two
  separate idx_theses_published / idx_theses_year indexes and sort with a temp
  B-tree. Every public query filters on is_published=1 and orders/filters by year,
  so this covering index eliminates the sort pass for those queries.

- Create storage/migrations/006_add_composite_index.sql and apply to both
  posterg.db and test.db.

- Fix storage/migrations/005_add_banner.sql: the view recreation in that file
  still referenced the pre-migration-001 table/column names (thesis_keywords,
  keywords.keyword). Updated to use thesis_tags / tags tg to match the canonical
  schema.sql. The live DB was unaffected (migration 001 ran before 005), but the
  file was misleading and would fail if ever re-run from scratch.
This commit is contained in:
Pontoporeia
2026-03-27 13:48:22 +01:00
parent 42af4644c5
commit f37069720a
6 changed files with 14 additions and 5 deletions

View File

@@ -351,7 +351,7 @@ Goal: rename the tables and column to the canonical M2M pattern (`tags`, `thesis
Eliminates full-database read-locks on every write; makes concurrent PHP-FPM workers safe. Eliminates full-database read-locks on every write; makes concurrent PHP-FPM workers safe.
Also add `PRAGMA cache_size = -8000` (≈8 MB page cache) while there. Also add `PRAGMA cache_size = -8000` (≈8 MB page cache) while there.
- [ ] **Composite index `(is_published, year DESC)`** on `theses` — every public query filters on both. - [x] **Composite index `(is_published, year DESC)`** on `theses` — every public query filters on both.
Currently `idx_theses_published` and `idx_theses_year` are separate; the query planner picks one Currently `idx_theses_published` and `idx_theses_year` are separate; the query planner picks one
and sorts the other with a temp B-tree. A single covering index eliminates the sort: and sorts the other with a temp B-tree. A single covering index eliminates the sort:
`CREATE INDEX IF NOT EXISTS idx_theses_pub_year ON theses(is_published, year DESC);` `CREATE INDEX IF NOT EXISTS idx_theses_pub_year ON theses(is_published, year DESC);`
@@ -370,7 +370,7 @@ Goal: rename the tables and column to the canonical M2M pattern (`tags`, `thesis
`Database::getPublishedAuthors(): array` that queries `thesis_authors JOIN authors` directly, `Database::getPublishedAuthors(): array` that queries `thesis_authors JOIN authors` directly,
avoiding the view entirely. avoiding the view entirely.
- [ ] **`migration 005` view is stale in the file** — `005_add_banner.sql` recreates the view still - [x] **`migration 005` view is stale in the file** — `005_add_banner.sql` recreates the view still
referencing `thesis_keywords` / `keywords.keyword` (the old pre-migration-001 names). referencing `thesis_keywords` / `keywords.keyword` (the old pre-migration-001 names).
The file is already applied to the live DB (correctly, since 001 ran first), but the migration The file is already applied to the live DB (correctly, since 001 ran first), but the migration
file itself is wrong and misleading. Fix: rewrite it to reference `thesis_tags` / `tags.name`, file itself is wrong and misleading. Fix: rewrite it to reference `thesis_tags` / `tags.name`,

View File

@@ -38,7 +38,7 @@ SELECT
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' THEN s.name END) as jury_lecteurs, GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' THEN s.name END) as jury_lecteurs,
GROUP_CONCAT(DISTINCT l.name) as languages, GROUP_CONCAT(DISTINCT l.name) as languages,
GROUP_CONCAT(DISTINCT fmt.name) as formats, GROUP_CONCAT(DISTINCT fmt.name) as formats,
GROUP_CONCAT(DISTINCT k.keyword) as keywords GROUP_CONCAT(DISTINCT tg.name) as keywords
FROM theses t FROM theses t
LEFT JOIN orientations o ON t.orientation_id = o.id LEFT JOIN orientations o ON t.orientation_id = o.id
LEFT JOIN ap_programs ap ON t.ap_program_id = ap.id LEFT JOIN ap_programs ap ON t.ap_program_id = ap.id
@@ -53,8 +53,8 @@ LEFT JOIN thesis_languages tl ON t.id = tl.thesis_id
LEFT JOIN languages l ON tl.language_id = l.id LEFT JOIN languages l ON tl.language_id = l.id
LEFT JOIN thesis_formats tf ON t.id = tf.thesis_id LEFT JOIN thesis_formats tf ON t.id = tf.thesis_id
LEFT JOIN format_types fmt ON tf.format_id = fmt.id LEFT JOIN format_types fmt ON tf.format_id = fmt.id
LEFT JOIN thesis_keywords tk ON t.id = tk.thesis_id LEFT JOIN thesis_tags tt ON t.id = tt.thesis_id
LEFT JOIN keywords k ON tk.keyword_id = k.id LEFT JOIN tags tg ON tt.tag_id = tg.id
GROUP BY t.id; GROUP BY t.id;
-- Recreate public view -- Recreate public view

View File

@@ -0,0 +1,8 @@
-- Migration 006: Add composite covering index (is_published, year DESC) on theses
--
-- Every public-facing query filters on is_published = 1 AND orders/filters by year.
-- The existing separate idx_theses_published and idx_theses_year force the query
-- planner to pick one index and sort the other via a temp B-tree.
-- This single covering index eliminates the extra sort pass.
CREATE INDEX IF NOT EXISTS idx_theses_pub_year ON theses(is_published, year DESC);

Binary file not shown.

View File

@@ -306,6 +306,7 @@ INSERT OR IGNORE INTO pages (slug, title, content) VALUES
CREATE INDEX IF NOT EXISTS idx_theses_year ON theses(year); CREATE INDEX IF NOT EXISTS idx_theses_year ON theses(year);
CREATE INDEX IF NOT EXISTS idx_theses_published ON theses(is_published); CREATE INDEX IF NOT EXISTS idx_theses_published ON theses(is_published);
CREATE INDEX IF NOT EXISTS idx_theses_pub_year ON theses(is_published, year DESC);
CREATE INDEX IF NOT EXISTS idx_theses_identifier ON theses(identifier); CREATE INDEX IF NOT EXISTS idx_theses_identifier ON theses(identifier);
CREATE INDEX IF NOT EXISTS idx_theses_orientation ON theses(orientation_id); CREATE INDEX IF NOT EXISTS idx_theses_orientation ON theses(orientation_id);
CREATE INDEX IF NOT EXISTS idx_theses_ap_program ON theses(ap_program_id); CREATE INDEX IF NOT EXISTS idx_theses_ap_program ON theses(ap_program_id);

Binary file not shown.