Búsqueda en lenguaje natural
El modo de lenguaje natural es el modo por defecto de MATCH() AGAINST(). Trata la cadena de búsqueda como una frase en lenguaje humano: MySQL divide la frase en palabras individuales, busca cada una en el índice full-text y calcula una puntuación de relevancia basada en la frecuencia de las palabras y su importancia estadística.
Sintaxis
-- Ambas son equivalentes
SELECT * FROM tabla WHERE MATCH(columna) AGAINST('texto de búsqueda');
SELECT * FROM tabla WHERE MATCH(columna) AGAINST('texto de búsqueda' IN NATURAL LANGUAGE MODE);Preparación
CREATE FULLTEXT INDEX ft_productos ON productos (nombre, descripcion);
CREATE FULLTEXT INDEX ft_resenas ON resenas (comentario);Cómo funciona
- MySQL divide la cadena de búsqueda en palabras
- Elimina las stopwords y las palabras más cortas que el mínimo
- Busca cada palabra en el índice full-text
- Calcula la relevancia usando el algoritmo TF-IDF
- Devuelve las filas ordenadas por relevancia descendente
TF-IDF
La relevancia se calcula con TF-IDF:
| Componente | Significado | Efecto |
|---|---|---|
| TF | Frecuencia del término | Más apariciones = más relevante |
| IDF | Frecuencia inversa del documento | Palabras raras = más relevantes |
Una palabra que aparece en pocas filas tiene más peso que una que aparece en muchas. Si una palabra aparece en más del 50% de las filas, MySQL la trata como una stopword y la ignora.
Ejemplo básico
SELECT nombre, precio,
MATCH(nombre, descripcion) AGAINST('portátil ligero') AS relevancia
FROM productos
WHERE MATCH(nombre, descripcion) AGAINST('portátil ligero');| nombre | relevancia |
|---|---|
| MacBook Air M3 | 0.8234 |
| Lenovo ThinkPad X1 | 0.4521 |
| ASUS ROG Zephyrus | 0.3876 |
"portátil" y "ligero" se buscan por separado. El MacBook Air M3 tiene la mayor relevancia porque su descripción menciona "ultraligero" y "portátil".
Buscar en reseñas
SELECT
p.nombre AS producto,
r.puntuacion,
r.comentario,
MATCH(r.comentario) AGAINST('calidad precio') AS relevancia
FROM resenas r
JOIN productos p ON r.producto_id = p.id
WHERE MATCH(r.comentario) AGAINST('calidad precio')
ORDER BY relevancia DESC;| producto | puntuacion | comentario | relevancia |
|---|---|---|---|
| Xiaomi 14 | 4 | Buena relación calidad-precio | 0.7832 |
| Camiseta algodón básica | 3 | Calidad aceptable por el precio | 0.6543 |
Regla del 50%
En modo de lenguaje natural, si una palabra aparece en más del 50% de las filas, MySQL la considera demasiado común y la ignora. Con solo 30 productos, esta regla puede ser notable:
-- Si buscamos una palabra muy común en las descripciones
SELECT COUNT(*) AS total,
SUM(CASE WHEN descripcion LIKE '%de%' THEN 1 ELSE 0 END) AS con_de
FROM productos;| total | con_de |
|---|---|
| 30 | 25 |
La palabra "de" aparece en el 83% de las descripciones, así que aunque no fuera una stopword, se ignoraría por la regla del 50%.
Múltiples palabras
Todas las palabras de la búsqueda se tratan como OR. MySQL devuelve filas que contienen al menos una de las palabras:
SELECT nombre
FROM productos
WHERE MATCH(nombre, descripcion) AGAINST('gaming pantalla procesador');| nombre |
|---|
| ASUS ROG Zephyrus |
| Samsung Galaxy S24 |
| Google Pixel 8 |
Las filas que contienen más palabras de la búsqueda reciben mayor relevancia.
Frases no son exactas
En modo de lenguaje natural, MySQL no busca frases exactas. La búsqueda "cámara de 48MP" busca las palabras "cámara", "de" y "48MP" por separado:
SELECT nombre
FROM productos
WHERE MATCH(nombre, descripcion) AGAINST('cámara de 48MP');Para buscar frases exactas, usa el modo booleano con comillas dobles.
Limitaciones del modo natural
- No soporta operadores como +, -, *
- No permite búsqueda de frases exactas
- Ignora palabras que aparecen en más del 50% de las filas
- No funciona bien con tablas muy pequeñas por la regla del 50%
- No se puede forzar que una palabra sea obligatoria
Limpieza
DROP INDEX ft_productos ON productos;
DROP INDEX ft_resenas ON resenas;En el siguiente artículo veremos la búsqueda booleana, que ofrece operadores para controlar exactamente qué palabras deben o no aparecer.
Escrito por Eduardo Lázaro
