From 7d96a0832436ceee2ea2b48b3239b28739de31c9 Mon Sep 17 00:00:00 2001 From: Pontoporeia Date: Sat, 28 Mar 2026 13:35:43 +0100 Subject: [PATCH] perf: replace fat-view student index query with lean getPublishedAuthors() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The répertoire page was loading the full v_theses_public view (15 JOINs + 8 GROUP_CONCAT temp B-trees) via getAllPublishedTheses() just to build the student name → thesis-id map on the index page. Only two columns (id, authors) were ever consumed by the template. Add Database::getPublishedAuthors(): array - Queries thesis_authors JOIN authors directly on the theses base table - Filters on theses.is_published = 1 using the existing index - Returns only id + GROUP_CONCAT(authors) — no view expansion - Results verified identical to the old getAllPublishedTheses() output Update search.php to call getPublishedAuthors() instead. Mark getAllPublishedTheses() @deprecated in Database.php. All tests pass. --- TODO.md | 9 +++--- public/search.php | 4 +-- src/Database.php | 27 ++++++++++++++++++ .../ad921d60486366258809553a3db49a4a.json | 2 +- storage/test.db | Bin 237568 -> 237568 bytes 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index e5739fc..22ff7c8 100644 --- a/TODO.md +++ b/TODO.md @@ -363,12 +363,11 @@ Goal: rename the tables and column to the canonical M2M pattern (`tags`, `thesis with a plain table query in `searchTheses` and `getPublishedTheses`** — build the minimal JOIN set per query and let indexes filter `theses` first. Keep the views for reporting/admin use only. -- [ ] **`getAllPublishedTheses()` in `search.php`** — fetches every published thesis (all columns, +- [x] **`getAllPublishedTheses()` in `search.php`** — fetches every published thesis (all columns, all JOINs) just to build the student-name index on the Répertoire page. This is a full table - scan through the fat view. Replace with a dedicated lean query: - `SELECT id, authors FROM v_theses_public ORDER BY authors ASC` — or add - `Database::getPublishedAuthors(): array` that queries `thesis_authors JOIN authors` directly, - avoiding the view entirely. + scan through the fat view. Replaced with `Database::getPublishedAuthors(): array` that queries + `thesis_authors JOIN authors` directly (only `id` + `authors` columns), avoiding the view entirely. + `getAllPublishedTheses()` kept but marked `@deprecated`. - [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). diff --git a/public/search.php b/public/search.php index 3ef9134..67eecf4 100644 --- a/public/search.php +++ b/public/search.php @@ -46,8 +46,8 @@ try { $orientations = $db->getAllOrientations(); $apPrograms = $db->getAllAPPrograms(); $keywords = $db->getUsedTags(); - // Fetch all published theses for the student index (no artificial cap) - $students = $db->getAllPublishedTheses(); + // Fetch id+authors only — lean query bypassing the fat v_theses_public view + $students = $db->getPublishedAuthors(); } catch (InvalidArgumentException $e) { $validationError = $e->getMessage(); $results = []; $totalItems = 0; $totalPages = 0; diff --git a/src/Database.php b/src/Database.php index b8d8773..015686c 100644 --- a/src/Database.php +++ b/src/Database.php @@ -408,6 +408,10 @@ class Database { /** * Return ALL published theses (no cap) — for internal use (student index). * Not exposed to user-controlled input. + * + * @deprecated Use getPublishedAuthors() for the répertoire student index — + * it avoids the expensive v_theses_public view and only fetches + * the two columns actually needed (id, authors). */ public function getAllPublishedTheses(): array { $stmt = $this->pdo->query( @@ -416,6 +420,29 @@ class Database { return $stmt->fetchAll(); } + /** + * Lean query for the répertoire student-name index. + * + * Avoids the fat v_theses_public view (15 JOINs + 8 GROUP_CONCAT B-trees). + * Queries thesis_authors → authors directly, letting the index on + * theses(is_published) filter the base table before any JOIN. + * + * Returns rows of [id => int, authors => "Name1,Name2"]. + */ + public function getPublishedAuthors(): array { + $stmt = $this->pdo->query( + "SELECT t.id, + GROUP_CONCAT(a.name ORDER BY ta.author_order ASC) AS authors + FROM theses t + JOIN thesis_authors ta ON ta.thesis_id = t.id + JOIN authors a ON a.id = ta.author_id + WHERE t.is_published = 1 + GROUP BY t.id + ORDER BY MIN(a.name) ASC" + ); + return $stmt->fetchAll(); + } + public function getAvailableYears() { $sql = "SELECT DISTINCT year FROM theses WHERE is_published = 1 ORDER BY year DESC"; $stmt = $this->pdo->query($sql); diff --git a/src/cache/rate_limit/ad921d60486366258809553a3db49a4a.json b/src/cache/rate_limit/ad921d60486366258809553a3db49a4a.json index f3b3c3f..be25078 100644 --- a/src/cache/rate_limit/ad921d60486366258809553a3db49a4a.json +++ b/src/cache/rate_limit/ad921d60486366258809553a3db49a4a.json @@ -1 +1 @@ -[1774694544] \ No newline at end of file +[1774701325] \ No newline at end of file diff --git a/storage/test.db b/storage/test.db index 7e572e06a16c0f3e7d7f3d8d129577ae31fd8476..3cfd596c11556ea158d0af64ad32a2220eb4f3e9 100644 GIT binary patch delta 28 icmZoTz}IkqZ$pqiV`6i#etWP!BM>uf57uX1TL1u*pb4V@ delta 28 icmZoTz}IkqZ$pqiV?uMVetWP!BM>uf57uX1TL1u*jtQaw