Instalación y Configuración de Typesense

Typesense es un motor de búsqueda de código abierto, rápido y tolerante a errores tipográficos, diseñado para ofrecer experiencias de búsqueda instantánea en aplicaciones web y móviles. Con tiempos de respuesta por debajo de los 50ms, soporte para búsqueda facetada, geolocalización y una API sencilla compatible con múltiples lenguajes, Typesense es una alternativa ligera y fácil de gestionar a Elasticsearch o Algolia. Esta guía cubre la instalación en Linux, creación de colecciones, indexación de documentos y configuración avanzada de búsqueda.

Requisitos Previos

  • Ubuntu 20.04/22.04, Debian 11+ o CentOS 8+/Rocky Linux 8+
  • 1 GB de RAM mínimo (depende del tamaño del índice)
  • 2 GB de espacio en disco
  • Puerto 8108 disponible

Instalación de Typesense

Instalación con el script oficial

# Instalar usando el script de instalación oficial
curl -O https://dl.typesense.org/releases/0.25.2/typesense-server-0.25.2-linux-amd64.tar.gz
tar -xzf typesense-server-0.25.2-linux-amd64.tar.gz

# O instalar como paquete del sistema en Ubuntu/Debian
curl -sL https://dl.typesense.org/deb/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/typesense.gpg

echo "deb [signed-by=/usr/share/keyrings/typesense.gpg] https://dl.typesense.org/deb stable main" \
  | sudo tee /etc/apt/sources.list.d/typesense.list

sudo apt update && sudo apt install -y typesense-server

Instalación en CentOS/Rocky

# Descargar el binario directamente
TYPESENSE_VERSION=0.25.2
wget https://dl.typesense.org/releases/${TYPESENSE_VERSION}/typesense-server-${TYPESENSE_VERSION}-linux-amd64.tar.gz

tar -xzf typesense-server-${TYPESENSE_VERSION}-linux-amd64.tar.gz

# Mover el binario al PATH del sistema
sudo mv typesense-server /usr/local/bin/
sudo chmod +x /usr/local/bin/typesense-server

Configuración Inicial

Crea el archivo de configuración en /etc/typesense/typesense-server.ini:

sudo mkdir -p /etc/typesense /var/lib/typesense /var/log/typesense
sudo useradd -r -s /bin/false typesense
sudo chown -R typesense:typesense /var/lib/typesense /var/log/typesense
; /etc/typesense/typesense-server.ini

[server]

; Directorio donde se almacenan los datos del índice
data-dir = /var/lib/typesense

; Clave API de administrador - cambiar por una clave aleatoria segura
api-key = TU_CLAVE_API_SECRETA_AQUI

; Dirección y puerto donde escucha el servidor
listen-address = 0.0.0.0
listen-port = 8108

; Habilitar CORS para peticiones desde el navegador
enable-cors = true

; Archivo de log
log-dir = /var/log/typesense

; Nivel de log: debug, info, warn, error
log-level = info

Crear el servicio systemd

sudo tee /etc/systemd/system/typesense.service > /dev/null <<EOF
[Unit]
Description=Typesense Search Engine
After=network.target

[Service]
Type=simple
User=typesense
Group=typesense
ExecStart=/usr/local/bin/typesense-server --config=/etc/typesense/typesense-server.ini
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable typesense --now

# Verificar que está corriendo
sudo systemctl status typesense

# Probar la API
curl http://localhost:8108/health

Crear Colecciones

Las colecciones en Typesense son equivalentes a las tablas en SQL o los índices en Elasticsearch. Cada colección tiene un schema definido:

# Variable con la clave API para no repetirla
export TYPESENSE_API_KEY="TU_CLAVE_API_SECRETA_AQUI"

# Crear una colección de productos
curl -X POST http://localhost:8108/collections \
  -H "Content-Type: application/json" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -d '{
    "name": "productos",
    "fields": [
      {"name": "id",          "type": "string"},
      {"name": "nombre",      "type": "string"},
      {"name": "descripcion", "type": "string"},
      {"name": "categoria",   "type": "string", "facet": true},
      {"name": "marca",       "type": "string", "facet": true},
      {"name": "precio",      "type": "float"},
      {"name": "stock",       "type": "int32"},
      {"name": "activo",      "type": "bool",   "index": true},
      {"name": "etiquetas",   "type": "string[]", "facet": true},
      {"name": "ubicacion",   "type": "geopoint", "optional": true}
    ],
    "default_sorting_field": "precio"
  }'
# Listar todas las colecciones
curl http://localhost:8108/collections \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}"

# Ver detalles de una colección
curl http://localhost:8108/collections/productos \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}"

# Eliminar una colección
curl -X DELETE http://localhost:8108/collections/productos \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}"

Indexar Documentos

# Indexar un documento individual
curl -X POST http://localhost:8108/collections/productos/documents \
  -H "Content-Type: application/json" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -d '{
    "id": "prod-001",
    "nombre": "Camiseta básica algodón",
    "descripcion": "Camiseta de algodón 100% en varios colores",
    "categoria": "Ropa",
    "marca": "BasicWear",
    "precio": 19.99,
    "stock": 150,
    "activo": true,
    "etiquetas": ["ropa", "camiseta", "algodón", "basico"]
  }'

# Importación masiva con JSONL (más eficiente)
cat > /tmp/productos.jsonl <<EOF
{"id": "prod-002", "nombre": "Zapatillas deportivas", "descripcion": "Zapatillas ligeras para correr", "categoria": "Calzado", "marca": "SpeedRun", "precio": 89.99, "stock": 45, "activo": true, "etiquetas": ["calzado", "deporte", "running"]}
{"id": "prod-003", "nombre": "Pantalón vaquero slim", "descripcion": "Vaquero ajustado de corte moderno", "categoria": "Ropa", "marca": "DenimPro", "precio": 49.99, "stock": 80, "activo": true, "etiquetas": ["ropa", "pantalon", "vaquero"]}
EOF

# Importar el archivo JSONL
curl -X POST "http://localhost:8108/collections/productos/documents/import?action=create" \
  -H "Content-Type: text/plain" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  --data-binary @/tmp/productos.jsonl
# Actualizar un documento (upsert)
curl -X PUT "http://localhost:8108/collections/productos/documents/prod-001" \
  -H "Content-Type: application/json" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -d '{"precio": 17.99, "stock": 200}'

# Eliminar un documento
curl -X DELETE http://localhost:8108/collections/productos/documents/prod-001 \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}"

Búsqueda y Parámetros

# Búsqueda básica
curl "http://localhost:8108/collections/productos/documents/search?q=zapatillas&query_by=nombre,descripcion" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}"

# Búsqueda con parámetros avanzados
curl "http://localhost:8108/collections/productos/documents/search" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -G \
  --data-urlencode "q=zapatillas deporte" \
  --data-urlencode "query_by=nombre,descripcion,etiquetas" \
  --data-urlencode "query_by_weights=3,1,2" \
  --data-urlencode "filter_by=activo:=true && precio:<100" \
  --data-urlencode "sort_by=precio:asc" \
  --data-urlencode "per_page=20" \
  --data-urlencode "page=1" \
  --data-urlencode "num_typos=2" \
  --data-urlencode "prefix=true"

Búsqueda Facetada

# Búsqueda con facetas para filtros de navegación
curl "http://localhost:8108/collections/productos/documents/search" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -G \
  --data-urlencode "q=*" \
  --data-urlencode "query_by=nombre" \
  --data-urlencode "facet_by=categoria,marca,etiquetas" \
  --data-urlencode "max_facet_values=10" \
  --data-urlencode "filter_by=categoria:=Ropa" \
  --data-urlencode "sort_by=precio:asc"

La respuesta incluirá el campo facet_counts con los valores posibles y sus conteos para construir filtros dinámicos en la interfaz.

Despliegue con Docker

# docker-compose.yml para Typesense
version: "3.8"
services:
  typesense:
    image: typesense/typesense:0.25.2
    container_name: typesense
    ports:
      - "8108:8108"
    volumes:
      - typesense-data:/data
    command: >
      --data-dir /data
      --api-key=TU_CLAVE_API_SECRETA_AQUI
      --enable-cors=true
      --listen-port=8108
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8108/health"]
      interval: 30s
      timeout: 10s
      retries: 3

volumes:
  typesense-data:
docker compose up -d
docker compose logs typesense

Integración con Aplicaciones

Generar claves API de solo lectura para el frontend

# Crear una clave de búsqueda con permisos limitados
curl -X POST http://localhost:8108/keys \
  -H "Content-Type: application/json" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" \
  -d '{
    "description": "Clave de búsqueda pública para el frontend",
    "actions": ["documents:search"],
    "collections": ["productos"]
  }'

Integración con JavaScript (InstantSearch)

# Instalar las librerías necesarias
npm install typesense typesense-instantsearch-adapter react-instantsearch-dom
// Configuración básica del cliente Typesense
import Typesense from 'typesense';

const client = new Typesense.Client({
  nodes: [{
    host: 'tu-servidor.com',
    port: 8108,
    protocol: 'https',
  }],
  apiKey: 'CLAVE_PUBLICA_SOLO_BUSQUEDA',
  connectionTimeoutSeconds: 2,
});

// Búsqueda simple
const resultados = await client.collections('productos').documents().search({
  q: 'zapatillas',
  query_by: 'nombre,descripcion',
  filter_by: 'activo:=true',
  sort_by: 'precio:asc',
});

Solución de Problemas

Typesense no arranca:

# Revisar logs del servicio
sudo journalctl -u typesense -n 50 --no-pager

# Verificar permisos del directorio de datos
ls -la /var/lib/typesense/
sudo chown -R typesense:typesense /var/lib/typesense/

Error 401 en las peticiones API:

# Verificar que la clave API en la cabecera es correcta
curl -v http://localhost:8108/health -H "X-TYPESENSE-API-KEY: TU_CLAVE"

# La clave debe coincidir exactamente con la del archivo de configuración
grep "api-key" /etc/typesense/typesense-server.ini

La búsqueda no devuelve resultados esperados:

# Ver los documentos indexados
curl "http://localhost:8108/collections/productos/documents/search?q=*&query_by=nombre&per_page=10" \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}"

# Verificar el número de documentos en la colección
curl http://localhost:8108/collections/productos \
  -H "X-TYPESENSE-API-KEY: ${TYPESENSE_API_KEY}" | python3 -m json.tool | grep num_documents

Rendimiento lento:

# Typesense mantiene todo el índice en memoria
# Verificar RAM disponible
free -m

# El índice debe caber en RAM; si no, considerar:
# 1. Aumentar la RAM del servidor
# 2. Reducir el número de campos indexados
# 3. Usar clustering de Typesense para distribuir la carga

Conclusión

Typesense es una excelente opción para implementar búsqueda de alta calidad en aplicaciones que requieren velocidad, tolerancia a errores tipográficos y facilidad de gestión. Su instalación es notablemente más sencilla que Elasticsearch y su consumo de recursos es considerablemente menor, lo que lo hace ideal para proyectos con presupuesto ajustado o equipos pequeños. Las claves de API con permisos limitados permiten exponer la búsqueda directamente al navegador del usuario de forma segura, eliminando la necesidad de un servidor proxy intermedio.