Parser ngram
El parser ngram es un parser alternativo para índices full-text que divide el texto en secuencias de N caracteres consecutivos en lugar de palabras separadas por espacios. Es imprescindible para idiomas como chino, japonés y coreano que no usan espacios entre palabras. También es útil para búsquedas de subcadenas en cualquier idioma.
Qué es un ngram
Un ngram es una secuencia de N caracteres consecutivos. Con un tamaño de token de 2, la palabra "MySQL" se divide en los bigramas:
| Token | Posición |
|---|---|
| My | 1-2 |
| yS | 2-3 |
| SQ | 3-4 |
| QL | 4-5 |
Esto permite buscar subcadenas dentro de palabras, algo que el parser por defecto no puede hacer.
Sintaxis
-- Crear índice con parser ngram
CREATE FULLTEXT INDEX nombre_indice ON tabla (columna) WITH PARSER ngram;
-- En CREATE TABLE
CREATE TABLE tabla (
contenido TEXT,
FULLTEXT INDEX ft_contenido (contenido) WITH PARSER ngram
);Configuración del tamaño del token
-- Ver el tamaño actual del ngram
SHOW VARIABLES LIKE 'ngram_token_size';| Variable_name | Value |
|---|---|
| ngram_token_size | 2 |
El valor por defecto es 2. Se configura al iniciar MySQL y no se puede cambiar en tiempo de ejecución:
-- En my.cnf / my.ini:
-- [mysqld]
-- ngram_token_size=2| Token size | Uso recomendado |
|---|---|
| 1 | Chino, japonés |
| 2 | Chino, japonés, coreano, subcadenas |
| 3 | Búsquedas más amplias |
Ejemplo con datos en español
CREATE TABLE articulos (
id INT AUTO_INCREMENT PRIMARY KEY,
titulo VARCHAR(200),
contenido TEXT,
FULLTEXT INDEX ft_articulos (titulo, contenido) WITH PARSER ngram
);
INSERT INTO articulos (titulo, contenido) VALUES
('Introducción a MySQL', 'MySQL es un sistema de gestión de bases de datos relacional'),
('PostgreSQL vs MySQL', 'Comparación entre PostgreSQL y MySQL para aplicaciones web'),
('MongoDB NoSQL', 'MongoDB es una base de datos NoSQL orientada a documentos'),
('Redis Cache', 'Redis es un almacén de datos en memoria utilizado como cache');-- El parser ngram permite buscar subcadenas
SELECT titulo FROM articulos
WHERE MATCH(titulo, contenido) AGAINST('SQL' IN BOOLEAN MODE);| titulo |
|---|
| Introducción a MySQL |
| PostgreSQL vs MySQL |
| MongoDB NoSQL |
Con el parser por defecto, "SQL" no encontraría "MySQL" ni "PostgreSQL" porque son palabras diferentes. Con ngram, "SQL" se divide en "SQ" y "QL", que coinciden con los bigramas de "MySQL", "PostgreSQL" y "NoSQL".
Ejemplo con CJK
CREATE TABLE contenido_cjk (
id INT AUTO_INCREMENT PRIMARY KEY,
texto VARCHAR(500),
FULLTEXT INDEX ft_cjk (texto) WITH PARSER ngram
);
INSERT INTO contenido_cjk (texto) VALUES
('数据库管理系统'),
('数据分析与机器学习'),
('人工智能技术');
-- Buscar documentos que contengan "数据"
SELECT texto FROM contenido_cjk
WHERE MATCH(texto) AGAINST('数据' IN BOOLEAN MODE);| texto |
|---|
| 数据库管理系统 |
| 数据分析与机器学习 |
Sin el parser ngram, esta búsqueda no funcionaría porque el parser por defecto no puede separar caracteres CJK en palabras.
ngram vs parser por defecto
| Característica | Parser por defecto | Parser ngram |
|---|---|---|
| Separación | Por espacios y puntuación | Por N caracteres |
| Idiomas CJK | No funciona | Funciona |
| Búsqueda de subcadenas | No | Sí |
| Tamaño del índice | Menor | Mayor |
| Velocidad de búsqueda | Más rápido | Más lento |
| Stopwords | Aplica | Aplica a tokens |
| Wildcard | Sí | Limitado |
ngram en modo booleano
-- Búsqueda booleana con ngram
SELECT titulo FROM articulos
WHERE MATCH(titulo, contenido) AGAINST('+MySQL -PostgreSQL' IN BOOLEAN MODE);| titulo |
|---|
| Introducción a MySQL |
-- Frase exacta con ngram
SELECT titulo FROM articulos
WHERE MATCH(titulo, contenido) AGAINST('"bases de datos"' IN BOOLEAN MODE);| titulo |
|---|
| Introducción a MySQL |
| MongoDB NoSQL |
Cuándo usar el parser ngram
Sí usar:
- Contenido en chino, japonés o coreano
- Necesitas buscar subcadenas dentro de palabras
- Búsqueda de códigos, SKUs o identificadores parciales
No usar:
- Contenido solo en idiomas occidentales con separación por espacios
- Tablas muy grandes donde el tamaño del índice importa
- Cuando la búsqueda por palabras completas es suficiente
Limpieza
DROP TABLE IF EXISTS articulos;
DROP TABLE IF EXISTS contenido_cjk;Con esto completamos la sección de búsqueda full-text. Hemos cubierto cómo crear índices full-text, optimizar su rendimiento, usar MATCH AGAINST en sus diferentes modos, y el parser ngram para idiomas especiales. En la siguiente sección exploraremos las vistas en MySQL.
Escrito por Eduardo Lázaro
