Manticore Search es un motor de búsqueda de texto completo de código abierto, heredero de Sphinx, que se consulta con SQL a través del protocolo de MySQL o con JSON a través de HTTP. Eso permite conectarte con el cliente mysql de siempre o con cualquier librería MySQL para crear tablas, insertar documentos y buscar. En este tutorial instalarás Manticore Search desde su repositorio oficial en Ubuntu 24.04, crearás una tabla en tiempo real con análisis de texto en español y harás búsquedas con filtros, facetas y resaltado de términos.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
  • Un usuario no root con privilegios sudo.
  • Al menos 1 GB de RAM (2 GB o más para índices grandes) y espacio en disco acorde a tus datos.

Manticore escucha por defecto solo en 127.0.0.1, así que no hace falta abrir puertos para seguir esta guía. Usa tres puertos:

PuertoProtocoloUso
9306MySQLConsultas SQL con cualquier cliente MySQL
9308HTTPAPI JSON y endpoint /sql
9312BinarioProtocolo nativo, usado entre nodos y por clientes antiguos de Sphinx

Paso 1: Añadir el repositorio de Manticore

Manticore distribuye un pequeño paquete que configura su repositorio APT e instala la clave de firma en /usr/share/keyrings. Descárgalo e instálalo:

cd /tmp
wget https://repo.manticoresearch.com/manticore-repo.noarch.deb
sudo dpkg -i manticore-repo.noarch.deb
sudo apt update

Comprueba que APT ya ve los paquetes de Manticore:

apt-cache policy manticore

La salida debe mostrar una versión candidata procedente de repo.manticoresearch.com.

Instala el servidor junto con la librería columnar, que añade el almacenamiento columnar y los índices secundarios que aceleran los filtros:

sudo apt install manticore manticore-columnar-lib

El servicio no arranca automáticamente tras la instalación. Habilítalo e inícialo:

sudo systemctl enable --now manticore

Comprueba que está activo y escuchando en los tres puertos:

sudo systemctl status manticore
sudo ss -tlnp | grep searchd
LISTEN 0      4096       127.0.0.1:9306       0.0.0.0:*    users:(("searchd",pid=5312,fd=12))
LISTEN 0      4096       127.0.0.1:9308       0.0.0.0:*    users:(("searchd",pid=5312,fd=13))
LISTEN 0      4096       127.0.0.1:9312       0.0.0.0:*    users:(("searchd",pid=5312,fd=11))

Paso 3: Revisar la configuración

La configuración está en /etc/manticoresearch/manticore.conf. Muéstrala:

cat /etc/manticoresearch/manticore.conf

Verás un bloque searchd parecido a este:

searchd {
    listen = 127.0.0.1:9312
    listen = 127.0.0.1:9306:mysql
    listen = 127.0.0.1:9308:http
    log = /var/log/manticore/searchd.log
    query_log = /var/log/manticore/query.log
    pid_file = /run/manticore/searchd.pid
    data_dir = /var/lib/manticore
}

La directiva data_dir activa el modo RT (en tiempo real): las tablas se crean y modifican con sentencias SQL como CREATE TABLE y Manticore guarda su definición en ese directorio, sin tocar el archivo de configuración. Es el modo recomendado para aplicaciones nuevas y el que usarás en esta guía.

Si más adelante otra máquina necesita conectarse, cambia 127.0.0.1 por la IP privada del servidor en las líneas listen y permite el acceso solo desde esa máquina con UFW, por ejemplo sudo ufw allow from 10.0.0.5 to any port 9306 proto tcp. Manticore no tiene autenticación propia, así que nunca expongas estos puertos a Internet.

Paso 4: Conectarte con el cliente MySQL

Instala el cliente de MySQL:

sudo apt install mysql-client

Conéctate al puerto SQL de Manticore. No necesita usuario ni contraseña:

mysql -h 127.0.0.1 -P 9306

Comprueba que todo responde listando las tablas, que de momento no existen:

SHOW TABLES;
Empty set (0.00 sec)

Todas las sentencias SQL de los siguientes pasos se ejecutan dentro de esta sesión.

Paso 5: Crear una tabla en tiempo real

Crea una tabla de productos. Los campos text son los que se indexan para búsqueda de texto completo; el resto son atributos que sirven para filtrar, ordenar y agrupar. morphology = 'libstemmer_es' aplica el stemmer de español, de modo que "zapatilla" y "zapatillas" se consideran la misma palabra, y min_prefix_len = '3' permite buscar por prefijo con un asterisco (zapat*):

CREATE TABLE productos (
    nombre      text,
    descripcion text,
    categoria   string,
    marca       string,
    precio      float,
    stock       integer,
    activo      bool
) morphology = 'libstemmer_es' min_prefix_len = '3';

Comprueba la estructura:

DESCRIBE productos;
+-------------+--------+----------------+
| Field       | Type   | Properties     |
+-------------+--------+----------------+
| id          | bigint |                |
| nombre      | text   | indexed stored |
| descripcion | text   | indexed stored |
| categoria   | string |                |
| marca       | string |                |
| precio      | float  |                |
| stock       | uint   |                |
| activo      | bool   |                |
+-------------+--------+----------------+

El campo id se añade automáticamente. Los campos de texto son stored, lo que significa que Manticore guarda también el texto original y puede devolverlo en los resultados.

Paso 6: Insertar y modificar documentos

Inserta varios productos. Si no indicas un id, Manticore genera uno único:

INSERT INTO productos (id, nombre, descripcion, categoria, marca, precio, stock, activo) VALUES
  (1, 'Camiseta básica de algodón', 'Camiseta unisex de algodón 100% en varios colores', 'Ropa', 'BasicWear', 19.99, 150, 1),
  (2, 'Zapatillas de running', 'Zapatillas ligeras para correr con amortiguación', 'Calzado', 'SpeedRun', 89.99, 45, 1),
  (3, 'Zapatillas de montaña', 'Calzado resistente para senderismo y trail', 'Calzado', 'TrailPro', 119.00, 12, 1),
  (4, 'Sudadera deportiva', 'Sudadera de algodón con capucha para entrenar', 'Ropa', 'SpeedRun', 39.90, 0, 0);
Query OK, 4 rows affected (0.00 sec)

Los documentos se pueden buscar en cuanto termina el INSERT. Para cambiar atributos (precio, stock, estado) usa UPDATE, que es inmediato:

UPDATE productos SET precio = 17.99, stock = 200 WHERE id = 1;

Los campos de texto no se pueden modificar con UPDATE. Para cambiarlos, sustituye el documento completo con REPLACE, que tiene la misma sintaxis que INSERT. Para borrar, usa DELETE FROM productos WHERE id = 4;.

Paso 7: Buscar con texto completo y filtros

La búsqueda de texto completo se hace con MATCH() dentro del WHERE, y se puede combinar con filtros sobre atributos. WEIGHT() devuelve la puntuación de relevancia:

SELECT id, nombre, precio, WEIGHT() AS relevancia
FROM productos
WHERE MATCH('zapatilla') AND activo = 1 AND precio < 100
ORDER BY relevancia DESC;
+------+-----------------------+-----------+------------+
| id   | nombre                | precio    | relevancia |
+------+-----------------------+-----------+------------+
|    2 | Zapatillas de running | 89.989998 |       2552 |
+------+-----------------------+-----------+------------+

Aunque buscaste "zapatilla" en singular, el stemmer encuentra "Zapatillas". El producto 3 también coincide, pero el filtro de precio lo excluye.

El lenguaje de consulta de MATCH() admite operadores para búsquedas más precisas:

ConsultaSignificado
MATCH('@nombre camiseta')Busca solo en el campo nombre
MATCH('algodón -sudadera')Contiene "algodón" pero no "sudadera"
MATCH('running | trail')Contiene cualquiera de las dos palabras
MATCH('"zapatillas running"~3')Las dos palabras a menos de 3 posiciones
MATCH('zapat*')Palabras que empiezan por "zapat"

Paso 8: Facetas y resaltado de resultados

Las facetas devuelven, junto con los resultados, cuántos documentos hay por cada valor de un atributo. Es lo que alimenta los filtros laterales de una tienda. Cada FACET genera un conjunto de resultados adicional:

SELECT id, nombre, precio FROM productos WHERE MATCH('algodón | zapatillas')
FACET categoria ORDER BY COUNT(*) DESC
FACET marca;
+------+-----------------------------+-----------+
| id   | nombre                      | precio    |
+------+-----------------------------+-----------+
...
+-----------+----------+
| categoria | count(*) |
+-----------+----------+
| Calzado   |        2 |
| Ropa      |        2 |
+-----------+----------+
...

HIGHLIGHT() devuelve fragmentos del texto original con los términos buscados marcados, listos para mostrar en una página de resultados:

SELECT id, HIGHLIGHT({before_match='<b>', after_match='</b>'}, 'descripcion') AS fragmento
FROM productos
WHERE MATCH('algodón');
+------+------------------------------------------------------------+
| id   | fragmento                                                  |
+------+------------------------------------------------------------+
|    1 | Camiseta unisex de <b>algodón</b> 100% en varios colores   |
|    4 | Sudadera de <b>algodón</b> con capucha para entrenar       |
+------+------------------------------------------------------------+

Sal del cliente con exit.

Paso 9: Consultar desde la API HTTP

Si tu aplicación no usa una librería MySQL, puedes usar la API JSON del puerto 9308. Esta petición equivale a una búsqueda de "zapatillas" con un filtro de precio:

curl -s http://127.0.0.1:9308/search -d '{
  "table": "productos",
  "query": {
    "bool": {
      "must":   [{"match": {"*": "zapatillas"}}],
      "filter": [{"range": {"precio": {"lte": 150}}}]
    }
  },
  "limit": 10
}'

La respuesta sigue un formato parecido al de Elasticsearch, con los documentos en hits.hits y el total en hits.total. También puedes enviar SQL directamente por HTTP con el endpoint /sql:

curl -s "http://127.0.0.1:9308/sql?mode=raw" -d "SELECT COUNT(*) FROM productos"

Solución de problemas

El servicio no arranca. Revisa el log de Manticore y el de systemd:

sudo tail -n 50 /var/log/manticore/searchd.log
sudo journalctl -u manticore -n 50 --no-pager

Un error de sintaxis en manticore.conf aparece al principio del log con el número de línea. Si has cambiado las líneas listen, comprueba que no hay otro proceso usando esos puertos con sudo ss -tlnp.

ERROR 2003 (HY000): Can't connect to MySQL server. Comprueba que indicas el puerto 9306 con -P 9306 y la IP 127.0.0.1. Si escribes localhost, el cliente intenta usar un socket Unix en lugar de TCP.

Una búsqueda por prefijo no devuelve resultados. MATCH('zap*') solo funciona si la tabla tiene min_prefix_len o min_infix_len y el prefijo es al menos igual de largo que ese valor. Estos ajustes se aplican al indexar, así que cambiarlos en una tabla existente requiere volver a insertar los datos.

La tabla crece mucho tras muchas escrituras. Las tablas RT se compactan solas en segundo plano. Puedes consultar su estado con SHOW TABLE productos STATUS; y forzar una compactación con OPTIMIZE TABLE productos;.

Conclusión

Tienes Manticore Search funcionando en Ubuntu 24.04 con una tabla en tiempo real, análisis de texto en español y consultas con filtros, facetas y resaltado, tanto por SQL como por HTTP. Como siguientes pasos, puedes conectar tu aplicación mediante su librería MySQL habitual, importar datos existentes de MySQL o PostgreSQL con la herramienta indexer, o crear un clúster con replicación entre varios nodos para alta disponibilidad.