# Plan de implementación — Buscador con Typesense (feadulta #8, fase 2)

> **Estado:** plan para ejecutar (pensado para que lo ejecute un agente Sonnet).
> **Fase 1 (MVP nativo) ya está en producción** — ver `feadulta-buscador-8` / issue #8:
> barra de búsqueda visible en móvil (`mu-plugins/fea-search.php`) + resultados en
> rejilla (`scripts/set_search_template.php`). Esta fase 2 sustituye el **motor** por
> Typesense manteniendo/мejorando esa UI, y añade **búsqueda por autor** (faceta).
>
> **DECISIÓN PENDIENTE (Rafa) — dónde corre Typesense en producción.** Todo lo demás
> se puede construir y validar en **local** sin esa decisión (Typesense en Docker local).
> Opciones de despliegue público (decidir luego): (a) PC + Cloudflare Tunnel
> `search.feadulta.com` (coste 0, atado al PC encendido, fallback al buscador nativo);
> (b) VPS pequeño dedicado ~4-6€/mes (fiable); (c) Typesense Cloud (gestionado).
> El hosting actual (cPanel compartido, sin Docker/systemd/root) **no sirve** para el motor.

## Decisiones ya tomadas
- **Integración:** Custom InstantSearch (indexador propio + UI InstantSearch.js), NO plugin.
- **Motor:** Typesense self-host en Docker.
- **Fallback:** si Typesense no responde, mantener el buscador nativo (`/?s=`) ya desplegado.

## Arquitectura
```
Navegador del usuario
   └── InstantSearch.js (typesense-instantsearch-adapter)  ── HTTPS ──►  Typesense
          (página /buscar en WP, search-only API key)                    (Docker)
                                                                            ▲
WP (wp-nuevo)  ── indexador (wp-cli/PHP) admin API key ──────────────────────┘
   └── hook publish_post / cron: reindexa incremental
```

## Pasos (ejecutables por Sonnet)

### A. Typesense local de desarrollo (no depende de la decisión de infra)
1. Añadir servicio `typesense` a `docker-compose.yml` (imagen `typesense/typesense:27.x`),
   volumen de datos, `--api-key` admin de desarrollo, puerto 8108 **solo en localhost**.
2. Levantar y verificar salud: `GET http://localhost:8108/health` → `{"ok":true}`.

### B. Schema de la colección `feadulta_posts`
Campos: `id`(post_id), `title`, `content` (texto plano, sin HTML), `excerpt`,
`author_id`(int32, facet), `author_name`(string, facet), `date`(int64, sort),
`url`, `lang`(string, facet: es/en/fr/it/pt), `categories`(string[], facet),
`thumbnail`(opcional). `default_sorting_field`: `date`.

### C. Indexador (`scripts/typesense_index.php`, wp-cli `wp eval-file`)
1. Recorre posts `publish` (`post`), excluye los template/feedback CPT.
2. Por cada post construye el documento (content = `wp_strip_all_tags`, author_name =
   `display_name` del `post_author`, lang vía Polylang `pll_get_post_language`,
   categories por slugs). Sube en **lotes** (import JSONL, action=upsert).
3. Idempotente, reanudable, con `--since` para incremental. ~24.778 posts.
4. Reindexado en caliente: hook `publish_post`/`save_post` → upsert de ese documento;
   `before_delete_post` → delete. (Encolar para no penalizar el guardado.)

### D. UI InstantSearch (sustituye/mejora la página de resultados)
1. Página `/buscar` (o el propio template `search`) con InstantSearch.js +
   `typesense-instantsearch-adapter`, apuntando a la URL pública de Typesense y la
   **search-only** key (nunca la admin) — ambas configurables (constantes en un
   mu-plugin `fea-search-typesense.php`, vacías hasta decidir la infra → fallback nativo).
2. Widgets: searchbox, hits (tarjetas con la estética actual `fea-archive-card`),
   **refinement por autor** (faceta `author_name`), por idioma y por categoría,
   paginación. Resaltado de coincidencias.
3. Multiidioma: filtrar `lang` = idioma actual por defecto; textos de UI por idioma.
4. Mantener la barra móvil de fase 1 como entrada; en desktop, integrar con el buscador
   del menú.

### E. Seguridad de claves
- **Admin API key**: solo en el servidor (indexador), nunca en el front.
- **Search-only key**: restringida a la colección, embebida en el front (es segura por diseño).
- CORS de Typesense limitado al dominio del sitio.

### F. Exposición pública (BLOQUEADO por la decisión de infra)
- Opción PC+Tunnel: `cloudflared` → `search.feadulta.com` → `localhost:8108`.
- Opción VPS: Typesense en el VPS + Caddy/HTTPS en `search.feadulta.com`.
- Opción Cloud: usar el endpoint y keys que da Typesense Cloud.
- En los tres casos, la UI solo necesita la **URL pública + search-only key**.

## Verificación
- Local: `/health` ok; `typesense_index.php` indexa N docs; la página `/buscar` devuelve
  resultados relevantes y la faceta de autor filtra (probar «González Amaro», «Pagola»).
- Comparar relevancia vs buscador nativo en consultas reales del feedback
  («reflexiones», búsqueda por autor).
- Prod: tras exponer el motor, repetir desde un navegador (Cloudflare bloquea headless).

## Riesgos / notas
- **Disponibilidad** si corre en el PC: caídas → debe degradar al buscador nativo, no a error.
- **RAM**: Typesense mantiene el índice en memoria; 24.778 posts de texto entran de sobra
  en <1 GB, pero dimensionar el host en consecuencia.
- **Reindexado masivo inicial** puede tardar; hacerlo por lotes y fuera de hora punta.
- No exponer nunca la admin key; CORS restringido.
