Curso de MySQL

FULLTEXT en MySQL: búsqueda de texto completo

Por Víctor Peña · Publicado el

FULLTEXT en MySQL

Hola, ¿cómo están? Continuando con el curso de MySQL, hoy veremos FULLTEXT: la forma correcta de implementar un buscador dentro de tu sistema.

¡Empecemos!

El problema con LIKE

Para buscar texto, lo primero que se le ocurre a todo el mundo es esto:

SELECT * FROM citas WHERE diagnostico LIKE '%dolor%';

Funciona, pero tiene tres limitaciones serias:

No usa índices. Como vimos, el comodín al inicio obliga a recorrer toda la tabla. Con un millón de registros, cada búsqueda es un escaneo completo.

Busca coincidencias literales. «dolor» no encuentra «dolores», y buscar «dolor cabeza» no encuentra «dolor de cabeza», porque la frase exacta no aparece.

No ordena por relevancia. Un texto que menciona la palabra ocho veces y otro que la menciona una vez pesan igual.

FULLTEXT resuelve los tres.

Qué es un índice FULLTEXT

Es un tipo especial de índice que analiza el texto y guarda las palabras que contiene, junto con dónde aparecen y con qué frecuencia.

En vez de guardar la frase completa, guarda algo parecido al índice de términos de un libro. Por eso puede responder «dame los registros que contengan esta palabra» sin leer los textos uno por uno.

Crear el índice

ALTER TABLE citas ADD FULLTEXT INDEX ft_diagnostico (diagnostico);

O al crear la tabla:

CREATE TABLE articulos (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    titulo VARCHAR(255) NOT NULL,
    contenido TEXT NOT NULL,
    FULLTEXT INDEX ft_articulo (titulo, contenido)
) ENGINE=InnoDB;

Fíjate en que puede cubrir varias columnas a la vez: buscará tanto en el título como en el contenido.

Requisitos: las columnas deben ser CHAR, VARCHAR o TEXT, e InnoDB lo soporta desde MySQL 5.6.

Buscar con MATCH AGAINST

SELECT id, diagnostico
FROM citas
WHERE MATCH(diagnostico) AGAINST('dolor');

Dos reglas al escribirlo:

  • Las columnas del MATCH deben coincidir exactamente con las del índice. Si el índice cubre (titulo, contenido), hay que escribir MATCH(titulo, contenido).
  • Si no coinciden, MySQL da error.

Modo natural

Es el modo por defecto. Busca las palabras y ordena por relevancia:

SELECT
    id,
    diagnostico,
    MATCH(diagnostico) AGAINST('dolor cabeza') AS relevancia
FROM citas
WHERE MATCH(diagnostico) AGAINST('dolor cabeza')
ORDER BY relevancia DESC;

La relevancia es un número: cuanto más alto, mejor coincidencia. Un texto que contiene ambas palabras varias veces puntúa más que uno que menciona una sola vez.

Poder pedir la relevancia como columna es lo que permite construir un buscador de verdad, con los mejores resultados arriba.

Modo booleano

Da control preciso sobre la búsqueda:

SELECT id, diagnostico
FROM citas
WHERE MATCH(diagnostico) AGAINST('+dolor -espalda' IN BOOLEAN MODE);

Los operadores disponibles:

Operador Significado
+palabra Debe aparecer
-palabra No debe aparecer
palabra* Empieza con esa raíz
"frase exacta" Esa secuencia literal
>palabra Aumenta su relevancia
<palabra La reduce
(a b) Agrupa términos

Algunos ejemplos:

-- Debe contener "dolor", y "cabeza" suma relevancia
AGAINST('+dolor cabeza' IN BOOLEAN MODE)

-- Palabras que empiecen por "cardio"
AGAINST('cardio*' IN BOOLEAN MODE)

-- La frase exacta
AGAINST('"dolor de cabeza"' IN BOOLEAN MODE)

-- Con "control" pero sin "rutinario"
AGAINST('+control -rutinario' IN BOOLEAN MODE)

El modo booleano es el que querrás para un buscador con filtros. Además, funciona aunque no exista índice FULLTEXT, solo que entonces será lento.

La longitud mínima de palabra

Este detalle desconcierta a mucha gente: por defecto, InnoDB ignora las palabras de menos de 3 caracteres.

SHOW VARIABLES LIKE 'innodb_ft_min_token_size';

Si necesitas buscar términos cortos —códigos, siglas—, hay que cambiar esa variable en la configuración del servidor y reconstruir el índice. No es un cambio que puedas hacer sobre la marcha.

Las palabras vacías

MySQL mantiene una lista de palabras demasiado comunes para ser útiles, que ignora al indexar. En inglés incluye artículos y preposiciones.

El problema para nosotros: esa lista está pensada para el inglés, así que palabras españolas frecuentes como «de», «la» o «para» sí se indexan y ensucian los resultados.

Se puede definir una lista propia:

CREATE TABLE mis_stopwords (value VARCHAR(30)) ENGINE=InnoDB;
INSERT INTO mis_stopwords VALUES ('de'), ('la'), ('el'), ('para'), ('con');

Y apuntar la configuración del servidor a esa tabla. Requiere reiniciar y reconstruir los índices, así que conviene decidirlo al principio del proyecto.

Combinar con otros filtros

FULLTEXT se lleva bien con el resto de la consulta:

SELECT
    c.fecha,
    c.diagnostico,
    CONCAT(p.nombre, ' ', p.apellido) AS paciente,
    MATCH(c.diagnostico) AGAINST('dolor') AS relevancia
FROM citas c
JOIN pacientes p ON c.paciente_id = p.id
WHERE MATCH(c.diagnostico) AGAINST('dolor')
  AND c.estado = 'atendida'
  AND c.fecha >= '2026-01-01'
ORDER BY relevancia DESC
LIMIT 20;

Búsqueda de texto, filtros normales y ordenación por relevancia en una sola consulta.

FULLTEXT o LIKE

LIKE FULLTEXT
Usa índice Solo sin % inicial
Ordena por relevancia No
Busca varias palabras Torpe Natural
Coincidencias parciales Sí, %tex% Solo por prefijo
Palabras cortas Mínimo 3 caracteres

Usa LIKE para búsquedas por prefijo en campos cortos: autocompletar un apellido, filtrar por código.

Usa FULLTEXT para buscar en textos largos: descripciones, artículos, diagnósticos, comentarios.

Cuándo se te queda corto

Seamos honestos sobre los límites. FULLTEXT es excelente para un buscador interno, pero no maneja bien:

  • Errores de escritura. Buscar «cardilogía» no encuentra «cardiología».
  • Sinónimos. «doctor» no encuentra «médico».
  • Raíces de palabra. «corriendo» no encuentra «correr».

Si necesitas eso, la herramienta adecuada es un motor de búsqueda especializado como Elasticsearch o Meilisearch, que se integran junto a la base de datos.

Pero para la mayoría de los sistemas, FULLTEXT sobra: es gratis, ya está instalado y no añade una pieza más que mantener.

Un buscador completo

Juntando todo, así se vería el buscador de un sistema:

SELECT
    c.id,
    c.fecha,
    LEFT(c.diagnostico, 150) AS resumen,
    CONCAT(p.nombre, ' ', p.apellido) AS paciente,
    ROUND(MATCH(c.diagnostico) AGAINST('+dolor +cabeza' IN BOOLEAN MODE), 2) AS relevancia
FROM citas c
JOIN pacientes p ON c.paciente_id = p.id
WHERE MATCH(c.diagnostico) AGAINST('+dolor +cabeza' IN BOOLEAN MODE)
ORDER BY relevancia DESC
LIMIT 20;

Ese LEFT(diagnostico, 150) devuelve solo un extracto, que es lo que muestras en la lista de resultados.

Desde PHP, recuerda pasar el término de búsqueda como parámetro, nunca concatenado.

Errores comunes

  • Columnas del MATCH distintas de las del índice.
  • Buscar palabras de menos de 3 letras sin cambiar la configuración.
  • Esperar coincidencias parciales en medio de la palabra: FULLTEXT solo hace prefijos.
  • Seguir usando LIKE '%texto%' en tablas grandes.
  • No ordenar por relevancia, desaprovechando la principal ventaja.

Para cerrar

FULLTEXT es la respuesta correcta cuando alguien pide «un buscador» en el sistema. Resuelve el problema de rendimiento del LIKE y añade algo que este no puede dar: ordenar por qué tan bien coincide cada resultado.

Con esta lección cerramos el recorrido principal del curso de MySQL. Lo que viene son cinco temas complementarios que no cubrimos y que aparecen en cualquier proyecto real.

Saludos y éxitos.

Norvic Software

Desarrollamos el software que tu empresa necesita

Somos una fábrica de software en Bolivia. Construimos sistemas a medida y aplicaciones móviles, y llevamos Inteligencia Artificial a las empresas que ya tienen un sistema funcionando.

Solicitar cotizaciónVer todos los servicios

Cotización sin costo · Respuesta directa por WhatsApp