Desarrollo backend

Elasticsearch Suggesters y autocompletado en 2026: de completion a search-as-you-type en producción

Ruslan Ismailov Publicado 14 min de lectura
E

El autocompletado es uno de esos elementos de interfaz que el usuario solo nota cuando funciona mal. Sugerencias lentas, resultados irrelevantes o la ausencia total de ellos tras un error tipográfico deterioran instantáneamente la experiencia. En este artículo repasamos todas las herramientas que ofrece Elasticsearch para construir un autocompletado rápido, preciso y escalable: desde los fundamentos teóricos hasta una arquitectura lista para producción en 2026.

1. El autocompletado en el contexto de la búsqueda: por qué es importante

El autocompletado cumple varias funciones a la vez: reduce el tiempo de escritura de la consulta, guía al usuario hacia entidades existentes en la base de datos y disminuye la cantidad de resultados «vacíos». Según estudios del sector, las interfaces con autocompletado de calidad muestran tasas de conversión entre un 20 y un 30 % superiores a las que no lo tienen.

Desde el punto de vista técnico, el autocompletado es una búsqueda en tiempo real: la consulta se envía tras cada pulsación de tecla, la respuesta debe llegar en menos de 50–100 ms y los resultados deben ser relevantes incluso con una entrada incompleta o errónea. Precisamente estos requisitos determinan la elección de herramientas dentro de Elasticsearch.

2. Visión general de los tipos de Suggesters en Elasticsearch

Elasticsearch ofrece cuatro tipos de Suggesters, cada uno orientado a una tarea concreta:

  • Term Suggester — propone correcciones a nivel de palabra individual basándose en la distancia de Levenshtein. Se usa para el clásico «¿Quisiste decir?».
  • Phrase Suggester — trabaja con frases completas, tiene en cuenta la frecuencia de co-ocurrencia de palabras. Ideal para corregir consultas tras su envío.
  • Completion Suggester — estructura de datos especializada (FST — Finite State Transducer) en memoria para sugerencias instantáneas por prefijo. Es la herramienta principal para el autocompletado.
  • Context Suggester — extensión del Completion Suggester con filtrado por categoría o geolocalización.

El tipo de campo search_as_you_type es un concepto independiente, implementado mediante un mapeo de campo especial y no a través de la API Suggest. Analizaremos ambos enfoques y explicaremos cuándo usar cada uno.

3. Completion Suggester: mapeo, indexación y consulta

Mapeo del campo de tipo completion

Para usar el Completion Suggester es necesario declarar un campo con el tipo completion en el mapeo del índice:

PUT /products
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "suggest": {
        "type": "completion",
        "analyzer": "simple",
        "preserve_separators": true,
        "preserve_position_increments": true,
        "max_input_length": 50
      }
    }
  }
}

El parámetro analyzer: "simple" divide la entrada por espacios y la convierte a minúsculas — una elección estándar para texto latino y cirílico sin morfología. max_input_length limita la longitud de las variantes almacenadas para ahorrar memoria.

Indexación de documentos con sugerencias

El campo suggest admite tanto una cadena de texto como un objeto con pesos y múltiples variantes de entrada:

POST /products/_doc/1
{
  "name": "Apple MacBook Pro 16",
  "suggest": {
    "input": ["Apple MacBook Pro", "MacBook Pro 16", "portátil Apple"],
    "weight": 100
  }
}

POST /products/_doc/2
{
  "name": "Apple MacBook Air M3",
  "suggest": {
    "input": ["Apple MacBook Air", "MacBook Air M3", "portátil MacBook Air"],
    "weight": 85
  }
}

El campo weight controla el orden de las sugerencias: los documentos con mayor peso aparecen primero. Esto resulta útil para destacar productos populares o en promoción.

Ejecución de la consulta de autocompletado

POST /products/_search
{
  "suggest": {
    "product_suggest": {
      "prefix": "macbook",
      "completion": {
        "field": "suggest",
        "size": 5,
        "skip_duplicates": true
      }
    }
  }
}

La respuesta contiene un array options con los campos text, _score y el documento original. El parámetro skip_duplicates: true excluye sugerencias repetidas provenientes de distintos documentos — una opción crítica para entornos de producción.

4. Search-as-you-type: cuándo usarlo en lugar del Completion Suggester

El tipo de campo search_as_you_type, introducido en Elasticsearch 7.2, crea automáticamente varios subcampos con analizadores de edge n-gram y shingle. Esto permite buscar no solo por prefijo, sino también por coincidencias en el interior de la cadena.

PUT /articles
{
  "mappings": {
    "properties": {
      "title": {
        "type": "search_as_you_type"
      }
    }
  }
}

Con este mapeo, Elasticsearch crea automáticamente los subcampos: title, title._2gram, title._3gram y title._index_prefix. La consulta tiene el siguiente aspecto:

GET /articles/_search
{
  "query": {
    "multi_match": {
      "query": "elasticsearch auto",
      "type": "bool_prefix",
      "fields": [
        "title",
        "title._2gram",
        "title._3gram"
      ]
    }
  }
}

Diferencia clave: el Completion Suggester solo funciona por prefijo y almacena el FST en memoria (heap), lo que proporciona latencias de 1–5 ms. search_as_you_type opera a través del índice invertido estándar, admite búsqueda por el interior de la cadena y puntuación completa, pero es más lento (10–30 ms). Elige el Completion Suggester para autocompletado estricto y search_as_you_type para búsqueda de texto completo en tiempo real.

5. Context Suggester: filtrado por categoría y geolocalización

El Context Suggester permite restringir el espacio de sugerencias por categorías arbitrarias o coordenadas geográficas. Es indispensable cuando un único índice sirve a varios inquilinos (tenants) o cuando las sugerencias deben depender de la sección del sitio.

Mapeo con contexto de categoría

PUT /marketplace
{
  "mappings": {
    "properties": {
      "suggest": {
        "type": "completion",
        "contexts": [
          {
            "name": "category",
            "type": "category"
          }
        ]
      }
    }
  }
}

Indexación con contexto

POST /marketplace/_doc/1
{
  "suggest": {
    "input": "iPhone 16 Pro",
    "weight": 90,
    "contexts": {
      "category": ["electronics", "smartphones"]
    }
  }
}

Consulta con filtrado por contexto

POST /marketplace/_search
{
  "suggest": {
    "product_suggest": {
      "prefix": "iphone",
      "completion": {
        "field": "suggest",
        "size": 5,
        "contexts": {
          "category": [
            { "context": "electronics", "boost": 2 },
            { "context": "smartphones", "boost": 3 }
          ]
        }
      }
    }
  }
}

El parámetro boost dentro del contexto permite no solo filtrar, sino también elevar las categorías relevantes en los resultados. Para sugerencias geolocalizadas se utiliza el tipo geo con los parámetros precision y radio de búsqueda.

6. Sugerencias fuzzy: tolerancia a errores tipográficos

El Completion Suggester admite búsqueda aproximada mediante el parámetro fuzzy. Esto salva situaciones en las que el usuario escribe «machbook» o «iPhon» con un error tipográfico:

POST /products/_search
{
  "suggest": {
    "product_suggest": {
      "prefix": "macbok",
      "completion": {
        "field": "suggest",
        "fuzzy": {
          "fuzziness": "AUTO",
          "min_length": 3,
          "prefix_length": 1,
          "unicode_aware": true
        }
      }
    }
  }
}

Configuración de fuzzy:

  • fuzziness: "AUTO" — selecciona automáticamente la distancia de edición permitida según la longitud de la palabra.
  • min_length: 3 — el modo fuzzy se activa solo para cadenas de más de 3 caracteres.
  • prefix_length: 1 — el primer carácter debe coincidir exactamente (mejora el rendimiento).
  • unicode_aware: true — funcionamiento correcto con cirílico y otros caracteres Unicode.

Importante: el modo fuzzy en el Completion Suggester es significativamente más lento que la búsqueda exacta. En sistemas de alta carga se recomienda usarlo solo cuando no hay coincidencias exactas — es decir, realizar dos consultas: primero la exacta y luego la fuzzy como fallback.

7. Optimización del rendimiento

Tamaño del índice y FST en memoria

El FST del Completion Suggester se almacena en el heap de la JVM. Su tamaño depende del número de cadenas de entrada únicas y de su longitud. En índices con millones de sugerencias, el FST puede ocupar varios gigabytes. Controla esto mediante el parámetro max_input_length y limita el número de variantes en input por documento (lo óptimo es entre 3 y 5).

Sharding

El Completion Suggester ejecuta la consulta en cada shard y combina los resultados en el nodo coordinador. Para los índices de sugerencias lo óptimo es usar 1–2 primary shards — esto minimiza la sobrecarga de merge y serialización. No crees el índice de sugerencias con la misma configuración de sharding que el índice de búsqueda principal.

Índice separado para sugerencias

La mejor práctica es almacenar las sugerencias en un índice independiente que contenga únicamente los campos necesarios para el autocompletado. Esto permite gestionar su ciclo de vida de forma autónoma, actualizarlo sin afectar al índice principal y controlar el tamaño del FST.

Caché a nivel de Elasticsearch

El Completion Suggester no utiliza la caché de solicitudes de Elasticsearch. Sin embargo, con un conjunto de datos estable, las consultas a un mismo prefijo llegarán a la caché de páginas del sistema operativo. Asegúrate de que los nodos tengan suficiente memoria libre fuera del heap para una caché de SO eficiente.

8. Integración arquitectónica: frontend, debounce y Redis

Debounce en el lado del cliente

Enviar una petición en cada pulsación de tecla sin retardo es un error. La práctica estándar es un debounce de 150–200 ms. Esto reduce la carga sobre el backend entre 3 y 5 veces para una velocidad de escritura típica.

// Ejemplo en React con debounce
import { useMemo } from 'react';
import debounce from 'lodash/debounce';

const fetchSuggestions = async (query) => {
  const res = await fetch(`/api/suggest?q=${encodeURIComponent(query)}`);
  return res.json();
};

const debouncedFetch = useMemo(
  () => debounce(fetchSuggestions, 180),
  []
);

API proxy en el backend

El frontend nunca debe conectarse directamente a Elasticsearch. Las peticiones pasan por una API de backend (Go, Node.js, Java) que:

  • Valida y sanea la entrada del usuario.
  • Aplica autorización y multitenancy (por ejemplo, añade el contexto de categoría).
  • Almacena los resultados en caché con Redis.

Caché de sugerencias en Redis

Las sugerencias para prefijos populares pueden almacenarse en Redis con un TTL de 60–300 segundos. La clave de caché se construye como suggest:{tenant}:{normalized_prefix}. Esto es especialmente eficiente: según las estadísticas, el 80 % de las consultas de autocompletado corresponde al 20 % de los prefijos más populares.

# Pseudocódigo en Go
func GetSuggestions(ctx context.Context, tenant, prefix string) ([]string, error) {
    cacheKey := fmt.Sprintf("suggest:%s:%s", tenant, strings.ToLower(prefix))
    
    // Intentar obtener desde Redis
    cached, err := redisClient.Get(ctx, cacheKey).Result()
    if err == nil {
        var suggestions []string
        json.Unmarshal([]byte(cached), &suggestions)
        return suggestions, nil
    }
    
    // Consulta a Elasticsearch
    suggestions, err := esClient.Suggest(ctx, tenant, prefix)
    if err != nil {
        return nil, err
    }
    
    // Guardar en Redis por 120 segundos
    data, _ := json.Marshal(suggestions)
    redisClient.Set(ctx, cacheKey, data, 120*time.Second)
    
    return suggestions, nil
}

La combinación Elasticsearch + Redis proporciona latencias inferiores a 5 ms para consultas en caché, frente a los 20–50 ms de las consultas directas a Elasticsearch.

9. Analítica de sugerencias: qué eligen los usuarios

El seguimiento de los clics en sugerencias es una valiosa fuente de datos para mejorar la búsqueda. El enfoque recomendado:

  • Al mostrar una sugerencia, asigna a cada una un suggestion_id único (hash del texto y la posición).
  • Al hacer clic, envía un evento: { query, selected_suggestion, position, timestamp, session_id }.
  • Agrega los eventos en un índice separado de Elasticsearch o en una base de datos analítica.
  • Usa el CTR (click-through rate) por posición para evaluar la calidad del ranking de sugerencias.

A partir de la analítica se puede actualizar dinámicamente el campo weight de las sugerencias populares mediante actualizaciones con script o reconstruyendo el índice una vez al día. Esto crea un ciclo de retroalimentación: las consultas populares ascienden en el ranking, mejorando la experiencia de los siguientes usuarios.

Además, conviene analizar las sugerencias con CTR nulo — o bien son irrelevantes, o bien su posición es demasiado baja. Una auditoría regular de estas sugerencias ayuda a limpiar el índice y mantener su calidad.

10. Errores habituales al implementar el autocompletado

  • Indexar todo el texto del documento en el campo completion. El campo completion está diseñado para variantes definidas explícitamente, no para texto completo. Indexa solo las cadenas que deseas mostrar como sugerencias.
  • Un único índice para sugerencias y búsqueda principal. Esto mezcla requisitos de rendimiento y complica la optimización. Usa un índice separado.
  • Falta de normalización de la entrada. El usuario puede escribir «MacBook» o «macbook» — normaliza a minúsculas antes de enviar a Elasticsearch y en el momento de la indexación.
  • Ignorar caracteres especiales o no latinos. El analizador standard no funciona bien con cirílico en completion. Usa simple o configura un analizador personalizado con el filtro de tokens lowercase.
  • Sin debounce en el frontend. A una velocidad de 200 ms/carácter y con 50 ms de latencia de red, cada carácter sin debounce genera peticiones en paralelo que pueden llegar desordenadas (race condition).
  • Array input demasiado grande. Añadir decenas de variantes por documento infla el FST e incrementa el heap. Limítate a entre 3 y 7 variantes por documento.
  • Sin monitorización del heap tras añadir el índice de completion. El crecimiento del FST puede provocar pausas de GC y degradación del rendimiento de todo el clúster.

Conclusión

Implementar un autocompletado de calidad con Elasticsearch no consiste en usar una sola herramienta, sino en construir toda una arquitectura: elegir correctamente entre Completion Suggester y search_as_you_type, diseñar el mapeo adecuado, aislar el índice de sugerencias, añadir caché con Redis, aplicar debounce en el frontend y establecer un ciclo de retroalimentación mediante analítica de clics. En 2026, estas prácticas se han convertido en el estándar para sistemas en producción con requisitos de latencia inferiores a 100 ms.

Comienza con un Completion Suggester sencillo en un índice separado, añade caché Redis para los prefijos más populares y configura la recopilación de analítica de clics — así obtendrás una base sólida que podrás ampliar a medida que crezcan tus requisitos.

Tecnologías

Etiquetas

Ruslan Ismailov

Desarrollador Senior Web / Backend. Desarrollador senior web/backend con 9 años de experiencia. Stack: PHP, Laravel, PostgreSQL, Redis, Docker, Kubernetes, REST, microservicios, CI/CD. Más sobre mí →