# Search & Répertoire — Documentation Two public browsing surfaces, both handled by `app/src/Controllers/SearchController.php` and routed by `app/src/Dispatcher.php`: | Route | Handler | Purpose | |-------|---------|---------| | `/search`, `/search.php` | `handleSearch()` | Full-text query + classic single filters | | `/repertoire`, `/repertoire.php` | `handleRepertoire()` | Browseable directory with multi-select filters | Both only ever expose **published** theses (`is_published = 1`). --- ## HandleSearch (`/search`) `handleSearch()` reads from `$_GET` and renders `app/templates/public/search.php` with the results fragment (`app/templates/partials/search-results.php`). Searchable text fields (via `Database::searchTheses()`): - Title, subtitle, synopsis, author names, supervisor names, tags/keywords. Single-value filters (`collectSearchParams()`): - `query` — free text - `year` — exact year - `orientation` — artistic orientation - `ap_program` — AP program - `finality` — finality type - `format` — format - `keyword` — tag/keyword - `language` — language Results are paginated (`limit = 20` default); the **search bar** (`app/templates/partials/search-bar.php`) submits a GET form to `/search`. --- ## HandleRepertoire (`/repertoire`) `handleRepertoire()` reads multi-select filter arrays from `$_GET` and renders `app/templates/public/repertoire.php`, which uses the shared results partial and `app/templates/partials/repertoire-index.php`. Multi-select filters (`collectFilterParams()`, each an array, `_GET` keys): - `fy[]` — years (validated to 1900–2100) - `ap[]` — AP program names - `or[]` — orientations - `fi[]` — finalities - `kw[]` — keywords/tags Each value is trimmed, length-capped (≤ 100), de-duplicated, and passed through as sanitised strings — no direct user input reaches SQL. There is also an HTMX **student preview** popover at `/repertoire/student-preview` (`handleStudentPreview()` → `student-preview.php`). --- ## Rate limiting Search is rate-limited via `app/src/RateLimit.php`. See the nginx config (`nginx/xamxam.conf`) for the matching server-side limits. --- ## Database access - **Full-text + single filters:** `Database::searchTheses(array $params, $limit, $offset)` and `Database::countSearchResults(array $params)`. - **Keyword/tag autocomplete:** `Database::searchTags(string $query)`. - **Supervisor autocomplete:** `Database::searchSupervisors($query, $role)`. - **Language autocomplete:** `Database::searchLanguages(string $query)`. Queries operate on `v_theses_public`; keyword matching joins the `thesis_tags` / `tags` tables (keywords are stored as lowercase-normalised **tags**, not a `keywords`/`thesis_keywords` set — see [database.md](database.md)). All queries use PDO prepared statements and escape `%`/`_` for `LIKE` (`Database::escapeLikeString`) to prevent wildcard injection. ### Accent-insensitive matching Text searches on the public **search bar**, the **student-preview** popover, and the **admin list** search are all **diacritic-insensitive**: `Théophile` and `Theophile`, `sévère` and `severe` return the same results. This is implemented via a SQLite user-defined function `accfold()` (`app/src/AccentFolding.php`, registered in `Database::registerSqliteFunctions()`), which strips precomposed accented Latin characters to their ASCII base (`é` → `e`, `ç` → `c`, …). Search conditions compare the folded column against the folded term (`accfold(column) LIKE accfold(:term)`), so both sides are normalised the same way. The function is NULL-safe and registered on the production and test connections alike; the public path uses `searchTheses()`/`countSearchResults()`, the student popover `getThesesByAuthorName()`, and the admin list `getThesesList()`/`getThesesListCount()`. Note: `intl`/`Normalizer` and `iconv` are **not** available on the runtime, so folding uses a hand-maintained transliteration map (Western Latin-1 + Latin Extended-A). --- ## Performance notes - Critical text/filter columns and the junction tables are indexed (`idx_theses_pub_year`, `idx_theses_*`, `idx_thesis_tags_*`, …). - `v_theses_public` pre-computes the joins for the common read path. - The repertoire filters operate on indexed lookup columns. --- ## Future enhancements (not yet implemented) The historical `search.md` listed potential automplete/faceted-search/export ideas. Status: - Auto-complete for tags exists at the form level (`searchTags`) - Faceted counts, saved searches, result export, and advanced boolean operators are **not** implemented