Elasticsearch Suggesters y autocompletado en 2026: de completion a search-as-you-type en producción
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
standardno funciona bien con cirílico en completion. Usasimpleo configura un analizador personalizado con el filtro de tokenslowercase. - 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
inputdemasiado 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í →