# ADR 009 : Recherche Pagefind en version 1

## Statut

**Accepté** (24/10/2026), remplace [ADR 004](/decisions/recherche)

## Contexte

La vitrine publie quelques centaines de structures, quelques milliers de personnes et projets, toutes en pages statiques. Les publications ne sont pas republiées. scanR utilise Elasticsearch ; le site d'audit, Pagefind.

## Décision

- **Pagefind** indexe, après `next build`, le HTML des fiches générées (`.next-<instance>/server/app`) dans `public/pagefind/<instance>/`. La recherche s'exécute dans le navigateur (`src/features/search/PagefindSearch.tsx`), sans serveur ni service à superviser.
- Seul le contenu marqué `data-pagefind-body` (les fiches) est indexé ; les filtres viennent de `data-pagefind-filter` (type, groupe).
- Sans filtre, les 5 premières structures sont affichées en tête des résultats : sans cela, les milliers de fiches de projets et de chercheurs, plus courtes, masquent les laboratoires (requêtes de référence : 0/20 → 17/20 à Saclay).
- Sans JavaScript, les listes (structures par groupe, thématiques, index alphabétique des chercheurs) restent navigables.
- L'issue de chaque recherche (résultats ou non) remonte en métrique agrégée (`/api/telemetry`) et dans Matomo (`trackSiteSearch`).
- Bascule vers **Meilisearch** si l'un de ces besoins apparaît : mise à jour sans reconstruction, tolérance aux fautes, recherche d'expertise sémantique.

## Deux comportements de Pagefind 1.4.0, mesurés le 26/10/2026

Les deux ont été trouvés en instruisant un score de 14/20 attribué à tort à la racinisation. Ils
sont contournés dans `src/features/search/PagefindSearch.tsx` (`warmUp`, `queryVariants`,
`searchAll`), et le script d'évaluation applique le même traitement — mesurer autrement
décrirait un comportement que personne ne rencontre.

### L'index répond avant d'être complètement chargé

À froid, `pagefind.search('biologie')` renvoie **6** fiches de laboratoire. Après un
`preload('biolog')`, la même requête en renvoie **88**, dont celles qui manquaient. L'ordre des
requêtes change donc le résultat :

```
biologie=6  biolog=88  biologie=88  biologique=88  biologie=88
```

Comme une requête à plusieurs mots exige **tous** les termes, un seul terme sous-chargé vide le
résultat : « biologie des plantes » ne renvoyait rien alors que l'IPS2 répond aux deux mots.
Correctif : précharger un préfixe de chaque terme avant de chercher.

### Les accents ne sont pas repliés, et le trait d'union coupe

« écologie » et « ecologie » sont deux mots distincts de l'index — 12 fiches contre 31 — et
« humain-machine » ne trouve pas ce que trouve « humain machine ». Les deux orthographes étant
également légitimes au clavier, la requête est déclinée en quatre variantes (telle quelle, sans
trait d'union, sans accents, les deux) et les résultats sont fusionnés par identifiant de page, au
meilleur score.

### Effet mesuré

| Jeu de requêtes de référence (Saclay) | Score     |
| ------------------------------------- | --------- |
| Avant                                 | 14/20     |
| Préchauffage de l'index               | 16/20     |
| + variantes d'orthographe             | **18/20** |

Les deux échecs restants ne sont pas des défauts de l'outil : « physique des particules » et
« droit des affaires numérique » ne figurent pas dans le vocabulaire des fiches visées (IJCLab,
DANTE). Les corriger demanderait d'enrichir les fiches, pas de régler la recherche — et le jeu de
requêtes ne doit pas être ajusté pour faire monter le score.

## Conséquences

- L'index se reconstruit à chaque build : la fraîcheur de la recherche suit celle du site.
- Pas de tolérance aux fautes avancée ; la racinisation française de Pagefind couvre les pluriels et variantes simples, mais ni les accents ni le trait d'union (voir ci-dessus).
- Chaque recherche coûte jusqu'à quatre requêtes Pagefind et quelques préchargements d'index : c'est le prix des deux contournements, et il est payé dans le navigateur.
