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:

TokenPosición
My1-2
yS2-3
SQ3-4
QL4-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_nameValue
ngram_token_size2

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 sizeUso recomendado
1Chino, japonés
2Chino, japonés, coreano, subcadenas
3Bú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ísticaParser por defectoParser ngram
SeparaciónPor espacios y puntuaciónPor N caracteres
Idiomas CJKNo funcionaFunciona
Búsqueda de subcadenasNo
Tamaño del índiceMenorMayor
Velocidad de búsquedaMás rápidoMás lento
StopwordsAplicaAplica a tokens
WildcardLimitado

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