# 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.