Buscador: AND obligatorio en FULLTEXT, excluir paginas-indice K2 y soportar frase exacta entre comillas
Tres bugs corregidos (issue gitea.feadulta.com#178): - OR implicito en Boolean Mode devolvia casi todo el sitio (~24.700/24.780 posts) para cualquier busqueda de varias palabras; ahora cada palabra es AND obligatorio (+). - Paginas-indice residuales K2 (categorias 37/38, listados por autor) puntuaban altisimo en relevancia por su tamano y tapaban el articulo real; excluidas de resultados de busqueda. - Palabras <3 caracteres forzadas como termino obligatorio rompian el calculo de relevancia (score 0 en filas que si coincidian); se descartan antes de construir la query. - Anadido soporte real de frase exacta: termino entre comillas dobles se envia a MySQL Boolean Mode como frase, en vez de descartarse silenciosamente.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* relevancia FULLTEXT si no se pide otro criterio de orden. Degradación elegante:
|
||||
* si no hay término o el índice FULLTEXT no existe, usa el comportamiento nativo.
|
||||
* Convive con fea-search-advanced.php (filtros pre_get_posts de autor/cat/cita/fecha).
|
||||
* Version: 1.1
|
||||
* Version: 1.3
|
||||
*/
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
@@ -38,16 +38,46 @@ function fea_ft_index_exists(): bool {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcula el término FULLTEXT en Boolean Mode (cada palabra con prefijo *).
|
||||
* Calcula el término FULLTEXT en Boolean Mode.
|
||||
*
|
||||
* Caso normal (sin comillas): cada palabra requerida con prefijo + y sufijo *. El '+'
|
||||
* fuerza AND entre palabras — sin él, Boolean Mode las une con OR implícito y cualquier
|
||||
* post que contenga UNA sola palabra común (p.ej. "ser") entra en el resultado, lo que en
|
||||
* la práctica devolvía casi todo el sitio (~24.700 de ~24.780 posts, bug #8 reportado en
|
||||
* feedback: "Tu verdadero ser" devolvía cientos de artículos irrelevantes).
|
||||
*
|
||||
* Caso frase exacta (entrecomillado, ej. `"tu verdadero ser"`): Boolean Mode soporta
|
||||
* nativamente búsqueda de frase entre comillas dobles (coincidencia de adyacencia, no
|
||||
* solo de palabras sueltas). Reenviamos el contenido saneado tal cual entre comillas, sin
|
||||
* partirlo en palabras con + ni *, para que el usuario que escribe entre comillas obtenga
|
||||
* de verdad una búsqueda de cadena exacta.
|
||||
*
|
||||
* Devuelve '' si el término sanitizado queda vacío.
|
||||
*/
|
||||
function fea_ft_boolean_term(string $raw): string {
|
||||
// Frase exacta: todo el término entre comillas dobles.
|
||||
if (preg_match('/^"(.*)"$/us', $raw, $m)) {
|
||||
$phrase = trim(substr(preg_replace('/[^\p{L}\p{N}\s\'\-]/u', '', $m[1]), 0, 200));
|
||||
if ($phrase === '') return '';
|
||||
return '"' . $phrase . '"';
|
||||
}
|
||||
|
||||
$term = trim(substr(preg_replace('/[^\p{L}\p{N}\s\'\-]/u', '', $raw), 0, 200));
|
||||
if ($term === '') return '';
|
||||
|
||||
global $wpdb;
|
||||
$words = preg_split('/\s+/', $term);
|
||||
return implode('* ', array_map(fn($w) => $wpdb->esc_like($w), $words)) . '*';
|
||||
|
||||
// Descartamos palabras por debajo de innodb_ft_min_token_size (3 en este servidor):
|
||||
// MySQL las excluye del índice, y forzarlas igualmente como término obligatorio ('+')
|
||||
// no aporta nada al filtrado (WP igual las ignora) pero SÍ rompe el cálculo de
|
||||
// relevancia: si un término '+' no existe en el índice, MATCH()...AGAINST() en modo
|
||||
// boolean puede devolver 0 aunque la fila cumpla el resto de términos, dejando el
|
||||
// ORDER BY sin poder distinguir resultados relevantes de irrelevantes.
|
||||
$words = array_values(array_filter($words, fn($w) => mb_strlen($w) >= 3));
|
||||
if (empty($words)) return '';
|
||||
|
||||
return implode(' ', array_map(fn($w) => '+' . $wpdb->esc_like($w) . '*', $words));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user