Buscador: OR implicito en FULLTEXT + paginas-indice K2 tapaban resultados reales #178

Open
opened 2026-07-15 11:32:43 +00:00 by rafa · 2 comments
Owner

Reportado por feedback

2 comentarios del formulario Beta Feedback dicen que la búsqueda no funciona. Reproducido: buscar "Tu verdadero ser" (artículo de Fray Marcos) devolvía prácticamente todo el sitio en vez del artículo.

Diagnóstico (verificado en local contra la BD real, ~24.778 posts)

Tres bugs en el motor nativo FULLTEXT (wordpress/wp-content/mu-plugins/fea-search-fulltext.php + fea-search-advanced.php, del #8):

  1. El gordo — OR implícito en Boolean Mode. fea_ft_boolean_term() unía las palabras de la búsqueda sin prefijo + (tu* verdadero* ser*), y en MySQL Boolean Mode sin +/- las palabras se combinan con OR, no con AND. Como "ser" es palabra larga pero utilísima en contenido religioso, la query hacía MATCH contra 24.693 de 24.778 posts (comprobado con SQL directo). Básicamente cualquier búsqueda de varias palabras devolvía casi todo el sitio.
  2. Páginas-índice residuales de la migración K2 tapando el resultado real. Dos categorías "fantasma" que sobrevivieron a la migración — term 37 ("Lista de autores habituales") y term 38 ("Lista completa de autores por orden alfabético") — contienen ~1.169 posts que en realidad son páginas-índice por autor (un post gigante con cientos de enlaces a sus artículos, no es un artículo). Por su tamaño y densidad de palabras, el ranking de relevancia FULLTEXT las puntuaba altísimo (score ~49 vs ~2-5 del artículo real) y salían las primeras en cualquier búsqueda.
  3. Palabras cortas rompiendo el cálculo de relevancia. Palabras de menos de 3 caracteres (ej. "tu", por debajo de innodb_ft_min_token_size=3) no existen en el índice FULLTEXT. Al forzarlas igualmente como término obligatorio (+tu*), MySQL devolvía relevancia 0 para filas que sí cumplían el resto de la búsqueda, dejando el ORDER BY sin poder distinguir resultados relevantes de irrelevantes.

Fix aplicado (en local, /home/rafa/joomla-migration, pendiente desplegar a prod)

  • fea_ft_boolean_term(): cada palabra ahora lleva + obligatorio (+verdadero* +ser*), en vez de OR implícito.
  • Se descartan palabras <3 caracteres antes de construir el término (no aportan filtrado y rompían la relevancia).
  • fea-search-advanced.php: nueva constante FEA_CATS_RESIDUAL_EXCLUIR = [37, 38], excluida vía category__not_in en pre_get_posts cuando es una búsqueda (y no hay filtro de categoría explícito).

Resultado verificado (WP local, Docker): "Tu verdadero ser" pasa de ~24.700 resultados (todo el sitio) a un rango razonable, y las dos páginas-índice basura ya no aparecen en resultados de búsqueda.

Limitación de fondo que persiste (no es un bug, es el motor)

El ranking de relevancia nativo de MySQL FULLTEXT normaliza por longitud de documento: favorece documentos cortos con alta densidad de la palabra buscada sobre coincidencias de frase exacta en artículos largos. Con esta query el artículo exacto de Fray Marcos sigue sin salir en la posición #1 (sale entre los primeros 20-40, ya no perdido entre 24.700). Probé además un boost de frase exacta en Boolean Mode ("tu verdadero ser" como término opcional) y no cambia sustancialmente el orden — es una limitación estructural del algoritmo de relevancia de MySQL en Boolean Mode con truncamiento (*), no algo arreglable con más ajustes de query.

Esto es justo la razón por la que ya estaba decidida la Fase 3 (Typesense) documentada en docs/plan-buscador-typesense.md — motor de búsqueda real con ranking BM25, que sí resolvería esto de raíz. Sigue pendiente la decisión de dónde correrlo (PC+Cloudflare Tunnel / VPS / Typesense Cloud), documentada en ese plan.

Siguiente paso

Revisar el diff de fea-search-fulltext.php y fea-search-advanced.php (solo esos 2 ficheros tocados) y desplegar a prod (wp-nuevo.feadulta.com / www.feadulta.com, método ssh 'cat > ruta' porque scp no funciona en ese hosting).

## Reportado por feedback 2 comentarios del formulario Beta Feedback dicen que la búsqueda no funciona. Reproducido: buscar **"Tu verdadero ser"** (artículo de Fray Marcos) devolvía prácticamente todo el sitio en vez del artículo. ## Diagnóstico (verificado en local contra la BD real, ~24.778 posts) Tres bugs en el motor nativo FULLTEXT (`wordpress/wp-content/mu-plugins/fea-search-fulltext.php` + `fea-search-advanced.php`, del #8): 1. **El gordo — OR implícito en Boolean Mode.** `fea_ft_boolean_term()` unía las palabras de la búsqueda sin prefijo `+` (`tu* verdadero* ser*`), y en MySQL Boolean Mode sin `+`/`-` las palabras se combinan con **OR**, no con AND. Como "ser" es palabra larga pero utilísima en contenido religioso, la query hacía `MATCH` contra **24.693 de 24.778 posts** (comprobado con SQL directo). Básicamente cualquier búsqueda de varias palabras devolvía casi todo el sitio. 2. **Páginas-índice residuales de la migración K2 tapando el resultado real.** Dos categorías "fantasma" que sobrevivieron a la migración — **term 37** ("Lista de autores habituales") y **term 38** ("Lista completa de autores por orden alfabético") — contienen ~1.169 posts que en realidad son páginas-índice por autor (un post gigante con cientos de enlaces a sus artículos, no es un artículo). Por su tamaño y densidad de palabras, el ranking de relevancia FULLTEXT las puntuaba altísimo (score ~49 vs ~2-5 del artículo real) y salían las primeras en cualquier búsqueda. 3. **Palabras cortas rompiendo el cálculo de relevancia.** Palabras de menos de 3 caracteres (ej. "tu", por debajo de `innodb_ft_min_token_size=3`) no existen en el índice FULLTEXT. Al forzarlas igualmente como término obligatorio (`+tu*`), MySQL devolvía relevancia **0** para filas que sí cumplían el resto de la búsqueda, dejando el `ORDER BY` sin poder distinguir resultados relevantes de irrelevantes. ## Fix aplicado (en local, `/home/rafa/joomla-migration`, pendiente desplegar a prod) - `fea_ft_boolean_term()`: cada palabra ahora lleva `+` obligatorio (`+verdadero* +ser*`), en vez de OR implícito. - Se descartan palabras <3 caracteres antes de construir el término (no aportan filtrado y rompían la relevancia). - `fea-search-advanced.php`: nueva constante `FEA_CATS_RESIDUAL_EXCLUIR = [37, 38]`, excluida vía `category__not_in` en `pre_get_posts` cuando es una búsqueda (y no hay filtro de categoría explícito). **Resultado verificado** (WP local, Docker): "Tu verdadero ser" pasa de ~24.700 resultados (todo el sitio) a un rango razonable, y las dos páginas-índice basura ya no aparecen en resultados de búsqueda. ## Limitación de fondo que persiste (no es un bug, es el motor) El ranking de relevancia nativo de MySQL FULLTEXT normaliza por longitud de documento: favorece documentos cortos con alta densidad de la palabra buscada sobre coincidencias de frase exacta en artículos largos. Con esta query el artículo exacto de Fray Marcos sigue sin salir en la posición #1 (sale entre los primeros 20-40, ya no perdido entre 24.700). Probé además un boost de frase exacta en Boolean Mode (`"tu verdadero ser"` como término opcional) y no cambia sustancialmente el orden — es una limitación estructural del algoritmo de relevancia de MySQL en Boolean Mode con truncamiento (`*`), no algo arreglable con más ajustes de query. Esto es justo la razón por la que ya estaba decidida la **Fase 3 (Typesense)** documentada en `docs/plan-buscador-typesense.md` — motor de búsqueda real con ranking BM25, que sí resolvería esto de raíz. Sigue pendiente la decisión de dónde correrlo (PC+Cloudflare Tunnel / VPS / Typesense Cloud), documentada en ese plan. ## Siguiente paso Revisar el diff de `fea-search-fulltext.php` y `fea-search-advanced.php` (solo esos 2 ficheros tocados) y desplegar a prod (`wp-nuevo.feadulta.com` / `www.feadulta.com`, método `ssh 'cat > ruta'` porque scp no funciona en ese hosting).
Author
Owner

Añadido: soporte de frase exacta entre comillas.

fea_ft_boolean_term() ahora detecta si el término viene entre comillas dobles ("tu verdadero ser") y en ese caso lo reenvía tal cual a MySQL Boolean Mode como frase (coincidencia de adyacencia real), en vez de partirlo en palabras AND sueltas. Antes las comillas se descartaban silenciosamente en el saneo y no hacían nada — buscar entre comillas daba el mismo resultado que sin ellas.

Verificado en local:

  • Sin comillas: 2.559 resultados (AND de palabras, sin cambios de comportamiento).
  • Con comillas "Tu verdadero ser": 564 resultados (frase real, más precisa).
  • Comilla suelta (input mal formado): cae al modo palabra normal, sin error.
  • Comillas vacías "": término vacío, degrada al buscador nativo de WP, sin error (HTTP 200).

No había ningún artículo cuyo título sea exactamente "Tu verdadero ser" (comprobado con match exacto en BD) — los más parecidos lo llevan como parte de un título más largo (ej. id 46942 "TU VERDADERO SER ES INVIOLABLE").

Sigue todo en local, mismos 2 ficheros (fea-search-fulltext.php v1.3), pendiente de revisar/desplegar junto con el resto del fix.

**Añadido: soporte de frase exacta entre comillas.** `fea_ft_boolean_term()` ahora detecta si el término viene entre comillas dobles (`"tu verdadero ser"`) y en ese caso lo reenvía tal cual a MySQL Boolean Mode como frase (coincidencia de adyacencia real), en vez de partirlo en palabras AND sueltas. Antes las comillas se descartaban silenciosamente en el saneo y no hacían nada — buscar entre comillas daba el mismo resultado que sin ellas. Verificado en local: - Sin comillas: 2.559 resultados (AND de palabras, sin cambios de comportamiento). - Con comillas `"Tu verdadero ser"`: 564 resultados (frase real, más precisa). - Comilla suelta (input mal formado): cae al modo palabra normal, sin error. - Comillas vacías `""`: término vacío, degrada al buscador nativo de WP, sin error (HTTP 200). No había ningún artículo cuyo título sea exactamente "Tu verdadero ser" (comprobado con match exacto en BD) — los más parecidos lo llevan como parte de un título más largo (ej. id 46942 "TU VERDADERO SER ES INVIOLABLE"). Sigue todo en local, mismos 2 ficheros (`fea-search-fulltext.php` v1.3), pendiente de revisar/desplegar junto con el resto del fix.
Author
Owner

Desplegado a producción (2026-07-16).

  • Commit local 2504666 en rama fix/multiidioma-portada-132 (solo los 2 ficheros).
  • Backup de los originales en prod: /web/wp-content/mu-plugins/fea-search-{fulltext,advanced}.php.bak-2026-07-16.
  • Subido vía ssh 'cat > ruta' (scp sigue roto en ese hosting). Bytes verificados idénticos local↔prod (5.700 y 34.482 respectivamente).
  • Verificado en vivo pidiendo directo al origen (IP 134.0.10.170 con Host: www.feadulta.com, sin pasar por Cloudflare):
    • s=Tu verdadero ser → 2.559 resultados (antes ~24.700, básicamente todo el sitio).
    • s="Tu verdadero ser" (frase exacta) → 564 resultados.
    • Sin errores PHP en la respuesta.

Nota aparte, no bloqueante: el remoto origin del repo local sigue apuntando al Gitea local antiguo (localhost:3000), no a gitea.feadulta.com (donde vive este issue). No lo he tocado — a decidir si se corrige el remote o es intencional.

**Desplegado a producción (2026-07-16).** - Commit local `2504666` en rama `fix/multiidioma-portada-132` (solo los 2 ficheros). - Backup de los originales en prod: `/web/wp-content/mu-plugins/fea-search-{fulltext,advanced}.php.bak-2026-07-16`. - Subido vía `ssh 'cat > ruta'` (scp sigue roto en ese hosting). Bytes verificados idénticos local↔prod (5.700 y 34.482 respectivamente). - Verificado en vivo pidiendo directo al origen (IP `134.0.10.170` con `Host: www.feadulta.com`, sin pasar por Cloudflare): - `s=Tu verdadero ser` → 2.559 resultados (antes ~24.700, básicamente todo el sitio). - `s="Tu verdadero ser"` (frase exacta) → 564 resultados. - Sin errores PHP en la respuesta. **Nota aparte, no bloqueante:** el remoto `origin` del repo local sigue apuntando al Gitea local antiguo (`localhost:3000`), no a `gitea.feadulta.com` (donde vive este issue). No lo he tocado — a decidir si se corrige el remote o es intencional.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rafa/feadulta#178