Instalación de Sonic: Motor de Búsqueda Ligero
Sonic es un motor de búsqueda de texto completo minimalista y extremadamente eficiente escrito en Rust, diseñado para ser la pieza de búsqueda en aplicaciones donde los recursos son limitados. A diferencia de Elasticsearch o Solr, Sonic no almacena los documentos originales sino únicamente el índice invertido, lo que lo hace ideal como backend de búsqueda complementario a tu base de datos principal. Esta guía cubre la instalación en Linux, los canales de comunicación, la ingesta de datos y la integración con aplicaciones web.
Requisitos Previos
- Ubuntu 20.04/22.04, Debian 11+ o CentOS 8+/Rocky Linux 8+
- 128 MB de RAM mínimo (Sonic es muy eficiente en memoria)
- Rust y Cargo (para compilar desde fuente) o binario precompilado
- Puerto 1491 disponible
Instalación de Sonic
Instalación del binario precompilado
# Descargar el binario precompilado para Linux x86_64
SONIC_VERSION=1.4.9
wget "https://github.com/valeriansaliou/sonic/releases/download/v${SONIC_VERSION}/v${SONIC_VERSION}.tar.gz" -P /tmp
cd /tmp && tar xzf v${SONIC_VERSION}.tar.gz
# Mover el binario al PATH del sistema
sudo mv sonic /usr/local/bin/
sudo chmod +x /usr/local/bin/sonic
# Verificar la instalación
sonic --version
Compilación desde fuente con Cargo
# Instalar Rust si no está disponible
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Instalar dependencias de compilación
sudo apt install -y build-essential pkg-config libssl-dev # Ubuntu/Debian
sudo dnf install -y gcc openssl-devel # CentOS/Rocky
# Clonar y compilar Sonic
git clone https://github.com/valeriansaliou/sonic.git /tmp/sonic-src
cd /tmp/sonic-src
cargo build --release
# Mover el binario compilado
sudo cp target/release/sonic /usr/local/bin/
Configuración
Crea el archivo de configuración:
sudo mkdir -p /etc/sonic /var/lib/sonic
sudo useradd -r -s /bin/false sonic
sudo chown -R sonic:sonic /var/lib/sonic
# /etc/sonic/config.cfg
[server]
log_level = "error"
[channel]
# Puerto TCP donde Sonic escucha
inet = "127.0.0.1:1491"
# Contraseña de autenticación del canal
auth_password = "SecretPassword"
# Número máximo de clientes simultáneos
tcp_timeout = 300
[store]
[store.kv]
# Directorio donde se almacena el índice
path = "/var/lib/sonic/store/kv/"
# Retener el índice entre reinicios
retain_word_objects = 1000
[store.fst]
# Directorio del índice FST (para autocompletado)
path = "/var/lib/sonic/store/fst/"
# Intervalo de consolidación del índice FST (segundos)
consolidate_after = 180
# Crear el servicio systemd
sudo tee /etc/systemd/system/sonic.service > /dev/null <<EOF
[Unit]
Description=Sonic Search Backend
After=network.target
[Service]
Type=simple
User=sonic
Group=sonic
ExecStart=/usr/local/bin/sonic -c /etc/sonic/config.cfg
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable sonic --now
# Verificar que está corriendo
sudo systemctl status sonic
# Verificar conectividad al puerto
nc -zv 127.0.0.1 1491
Protocolo de Canal (Channel Protocol)
Sonic usa un protocolo TCP basado en texto plano similar a Redis. La comunicación se organiza en tres canales, cada uno con sus comandos específicos:
# Conectarse manualmente con netcat para depuración
nc 127.0.0.1 1491
# Respuesta: CONNECTED <sonic-server v1.4.9>
# Iniciar sesión en el canal de búsqueda
START search SecretPassword
# Respuesta: STARTED search protocol(1) buffer(20000)
Los tres canales son:
- search: para realizar búsquedas (solo lectura)
- ingest: para añadir, eliminar y vaciar el índice (escritura)
- control: para operaciones de administración del servidor
Canal Ingest: Indexar Datos
# Conectar al canal ingest via netcat
nc 127.0.0.1 1491
# Iniciar sesión
START ingest SecretPassword
# Respuesta: STARTED ingest protocol(1) buffer(20000)
# Formato de los comandos:
# PUSH <coleccion> <bucket> <id_objeto> "<texto_a_indexar>"
# Indexar productos
PUSH productos tienda prod-001 "Camiseta básica algodón colores varios"
# Respuesta: OK
PUSH productos tienda prod-002 "Zapatillas deportivas running ligeras amortiguacion"
# Respuesta: OK
PUSH productos tienda prod-003 "Pantalon vaquero slim ajustado corte moderno azul"
# Respuesta: OK
# Eliminar un objeto del índice
POP productos tienda prod-001
# Respuesta: RESULT 1
# Vaciar todos los datos de una colección/bucket
FLUSHB productos tienda
# Respuesta: RESULT 3
# Vaciar una colección completa
FLUSHC productos
# Respuesta: OK
# Cerrar la sesión
QUIT
La estructura de Sonic es jerárquica:
- Colección (collection): agrupación principal (ej: "productos", "artículos", "usuarios")
- Bucket: partición dentro de la colección (ej: "tienda-madrid", "tienda-barcelona", o simplemente "default")
- ID de objeto: identificador único del documento en tu base de datos
- Texto: contenido textual a indexar
Canal Search: Buscar
# Conectar al canal de búsqueda
nc 127.0.0.1 1491
START search SecretPassword
# STARTED search protocol(1) buffer(20000)
# Búsqueda básica: QUERY <coleccion> <bucket> "<terminos>"
QUERY productos tienda "zapatillas running"
# Respuesta: PENDING Bt2m9h41
# EVENT QUERY Bt2m9h41 prod-002
# (devuelve los IDs de los objetos que coinciden)
# Búsqueda con límite y offset
QUERY productos tienda "zapatillas" LIMIT(5) OFFSET(0)
# Búsqueda en todos los buckets de una colección
QUERY productos * "camiseta"
# Sugerencias (autocompletado): SUGGEST <coleccion> <bucket> "<prefijo>"
SUGGEST productos tienda "zapati"
# Respuesta: PENDING Xt9k2j88
# EVENT SUGGEST Xt9k2j88 zapatilla zapatillas zapatillas_running
QUIT
Canal Control
nc 127.0.0.1 1491
START control SecretPassword
# Consolidar el índice FST (mejora el rendimiento del autocompletado)
TRIGGER consolidate
# Respuesta: PONG
# Ver información del servidor
INFO
# Respuesta: RESULT uptime(3600) clients_connected(2) commands_total(1500) ...
# Reiniciar los canales sin reiniciar el servicio
TRIGGER reindex
QUIT
Integración con Aplicaciones
Sonic tiene clientes oficiales y de la comunidad para múltiples lenguajes:
Python
pip install sonic-client
from sonic import SearchClient, IngestClient
# Indexar datos desde tu base de datos
with IngestClient("127.0.0.1", 1491, "SecretPassword") as ingest:
# Indexar productos desde la base de datos
for producto in obtener_productos_de_db():
texto = f"{producto['nombre']} {producto['descripcion']} {' '.join(producto['etiquetas'])}"
ingest.push("productos", "tienda", str(producto['id']), texto)
# Buscar
with SearchClient("127.0.0.1", 1491, "SecretPassword") as search:
# Obtener IDs de objetos coincidentes
ids = search.query("productos", "tienda", "zapatillas running", limit=10)
print(f"IDs encontrados: {ids}")
# Obtener sugerencias para autocompletado
sugerencias = search.suggest("productos", "tienda", "zapati", limit=5)
print(f"Sugerencias: {sugerencias}")
# Luego, con los IDs devueltos por Sonic, buscar los datos completos en tu BD
Node.js
npm install sonic-channel
const { Search, Ingest } = require("sonic-channel");
// Indexar datos
const ingestChannel = new Ingest({
host: "127.0.0.1",
port: 1491,
auth: "SecretPassword",
});
ingestChannel.connect({
connected: () => {
// Indexar un producto
ingestChannel.push(
"productos",
"tienda",
"prod-001",
"Camiseta básica algodón varios colores"
);
},
});
// Buscar
const searchChannel = new Search({
host: "127.0.0.1",
port: 1491,
auth: "SecretPassword",
});
searchChannel.connect({
connected: () => {
searchChannel.query("productos", "tienda", "camiseta", {
limit: 10,
offset: 0,
}).then(ids => {
console.log("IDs encontrados:", ids);
// Buscar los documentos completos en tu base de datos usando estos IDs
});
},
});
Patrón de uso típico con base de datos
import psycopg2
from sonic import SearchClient, IngestClient
def indexar_todos_los_productos():
"""Reindexar todos los productos desde PostgreSQL en Sonic."""
conn = psycopg2.connect("postgresql://usuario:pass@localhost/midb")
cursor = conn.cursor()
cursor.execute("SELECT id, nombre, descripcion, categoria FROM productos WHERE activo = true")
with IngestClient("127.0.0.1", 1491, "SecretPassword") as ingest:
# Vaciar el índice existente
ingest.flush_collection("productos")
# Reindexar todos los productos
for fila in cursor.fetchall():
id_prod, nombre, descripcion, categoria = fila
texto = f"{nombre} {descripcion} {categoria}"
ingest.push("productos", "general", str(id_prod), texto)
cursor.close()
conn.close()
def buscar_productos(query, pagina=1, por_pagina=20):
"""Buscar productos y devolver los datos completos desde PostgreSQL."""
offset = (pagina - 1) * por_pagina
with SearchClient("127.0.0.1", 1491, "SecretPassword") as search:
ids = search.query("productos", "general", query, limit=por_pagina, offset=offset)
if not ids:
return []
# Obtener los datos completos de los productos encontrados
conn = psycopg2.connect("postgresql://usuario:pass@localhost/midb")
cursor = conn.cursor()
cursor.execute(
"SELECT id, nombre, precio, categoria FROM productos WHERE id = ANY(%s)",
(ids,)
)
resultados = cursor.fetchall()
cursor.close()
conn.close()
return resultados
Solución de Problemas
Sonic no arranca:
# Verificar logs del servicio
sudo journalctl -u sonic -n 50 --no-pager
# Verificar permisos del directorio de datos
ls -la /var/lib/sonic/
sudo chown -R sonic:sonic /var/lib/sonic/
# Probar la configuración manualmente
sudo -u sonic sonic -c /etc/sonic/config.cfg
Error "Connection refused" al conectar:
# Verificar que Sonic está escuchando
sudo ss -tlnp | grep 1491
# Verificar la dirección de escucha en la configuración
grep "inet" /etc/sonic/config.cfg
# Si es 127.0.0.1, solo acepta conexiones locales
# Cambiar a 0.0.0.0 para aceptar conexiones remotas
El autocompletado no funciona:
# El autocompletado usa el índice FST que se consolida periódicamente
# Forzar la consolidación manualmente
nc 127.0.0.1 1491
START control SecretPassword
TRIGGER consolidate
# O reducir el intervalo de consolidación en config.cfg:
# consolidate_after = 30 # 30 segundos
Búsquedas que no devuelven resultados esperados:
# Sonic solo indexa texto plano; verifica que estás indexando el texto correcto
# El texto indexado debe incluir los términos que quieres buscar
# Verificar que los datos se han indexado correctamente
nc 127.0.0.1 1491
START ingest SecretPassword
COUNT productos tienda
# Devuelve el número de objetos indexados
Conclusión
Sonic es la solución perfecta cuando necesitas añadir búsqueda de texto completo a una aplicación sin la complejidad operativa de Elasticsearch o Solr. Su enfoque minimalista, limitado al índice invertido sin almacenamiento de documentos, lo hace extraordinariamente eficiente en recursos y fácil de mantener. El patrón de uso ideal es como complemento a tu base de datos principal: Sonic gestiona el índice de búsqueda y tú recuperas los datos completos desde tu base de datos con los IDs devueltos, obteniendo lo mejor de ambos mundos.


