# Prompt pour ajouter la recherche plein texte PostgreSQL

> Prompt IA copier-coller pour ajouter la recherche plein texte native PostgreSQL avec tsvector, index GIN et résultats classés à une application Next.js existante.

**Type:** Prompt  
**Tools:** Cursor, Claude Code, Codex, Windsurf  
**Stack:** Next.js, PostgreSQL, TypeScript  
**Difficulty:** medium  
**Updated:** 2026-06-08

---

Utilisez ce prompt pour ajouter une véritable recherche plein texte PostgreSQL à une application existante — en utilisant `tsvector`, un index GIN et `ts_rank` — sans que l'agent ait recours à Elasticsearch, Algolia ou une API de recherche tierce.

## Prompt principal

```txt title="Main Prompt"
You are working in a Next.js App Router project with TypeScript and PostgreSQL (using the
`postgres` npm package, not Prisma or Drizzle).

Task: add full-text search over the `posts` table (columns: id, title, body, created_at).

Database changes:
1. Write a migration file `migrations/0010_add_fts.sql` that:
   - Adds a generated column: `search_vector tsvector GENERATED ALWAYS AS
     (to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,''))) STORED;`
   - Creates a GIN index: `CREATE INDEX posts_search_idx ON posts USING GIN (search_vector);`
2. Do NOT use `pg_trgm` or LIKE queries — use `@@` with `to_tsquery` or `websearch_to_tsquery`.

Application changes:
- Create `src/lib/search.ts` with a `searchPosts(query: string, limit = 20)` function that:
  - Uses `websearch_to_tsquery('english', $1)` to parse the query safely.
  - Returns rows ordered by `ts_rank(search_vector, query_vector) DESC`.
  - Returns `{ id, title, body_excerpt, rank }` — body_excerpt via `ts_headline`.
  - Uses parameterized queries only — no string interpolation.
- Create a Server Action `src/lib/actions/search.ts` that calls `searchPosts` and returns results.
- Create `src/components/SearchBox.tsx` (Client Component) with a debounced input (300 ms) that
  calls the Server Action via `useTransition` and renders results.

Do not install pg_search, Meilisearch, or any search service. Stop and list files before coding.
```

## Notes d'implémentation

- `websearch_to_tsquery` est plus sûr que `to_tsquery` pour les entrées utilisateur car il tolère les requêtes malformées (opérateurs manquants, caractères spéciaux) sans générer d'erreur PostgreSQL.
- La colonne `tsvector` générée est automatiquement mise à jour lors des INSERT/UPDATE — aucun déclencheur nécessaire.
- `ts_headline` nécessite le texte de la colonne d'origine et le vecteur de requête ; transmettez les deux depuis le SELECT.
- L'index GIN rend la recherche rapide sur les grandes tables ; sans lui, les requêtes effectuent un balayage séquentiel.

## Modifications de fichiers attendues

```txt
migrations/0010_add_fts.sql              (new)
src/lib/search.ts                        (new)
src/lib/actions/search.ts               (new — Server Action)
src/components/SearchBox.tsx            (new — Client Component)
```

## Critères d'acceptation

- `psql -f migrations/0010_add_fts.sql` s'exécute sans erreur sur le schéma existant.
- La recherche d'un mot qui n'apparaît que dans `title` renvoie le bon article.
- La recherche d'une phrase avec une faute de frappe (ex. "postgress") ne renvoie aucun résultat sans planter.
- Les résultats sont classés avec les articles les plus pertinents en premier.

## Commandes de test

```bash
psql "$DATABASE_URL" -f migrations/0010_add_fts.sql
bun run typecheck
bun run dev
# type a search term in SearchBox and verify ranked results appear
psql "$DATABASE_URL" -c "EXPLAIN ANALYZE SELECT * FROM posts WHERE search_vector @@ websearch_to_tsquery('english','test');"
# confirm "Index Scan using posts_search_idx" appears in output
```

## Erreurs courantes de l'IA

- Utiliser `LIKE '%query%'` au lieu de l'opérateur plein texte `@@`.
- Utiliser `to_tsquery('english', $1)` avec des entrées utilisateur brutes — se casse sur des requêtes comme `"foo bar"`.
- Oublier l'index GIN, laissant la recherche en balayage séquentiel.
- Appeler `searchPosts` dans un composant client au lieu de l'envelopper dans une action serveur.

## Correctif du prompt

```txt title="Fix Prompt"
The search is using LIKE or crashing on special characters. Fix in order:
1. Replace any LIKE clause with `search_vector @@ websearch_to_tsquery('english', $1)`.
2. If `to_tsquery` is used directly with user input, replace it with `websearch_to_tsquery`.
3. Confirm the GIN index exists: `CREATE INDEX IF NOT EXISTS posts_search_idx ON posts USING GIN (search_vector);`
Show only the corrected diff. Do not modify unrelated files.
```