Cheatsheet Elasticsearch
Motor de busca e análise distribuído para logs, métricas e pesquisa full-text
Elasticsearch
Cluster e Índices
Estado del Cluster
GET /_cluster/health GET /_cluster/health?level=indices GET /_cluster/stats // Status: green (ok), yellow (sin réplicas), red (faltan shards)
_cluster/health muestra el estado general. green = todo OK, yellow = réplicas no asignadas, red = faltan shards primarios. Monitorizar constantemente.
Reindex
POST /_reindex
{
"source": { "index": "products-v1" },
"dest": { "index": "products-v2" }
}
// Con filtro:
POST /_reindex
{
"source": {
"index": "logs",
"query": { "range": { "date": { "gte": "2024-01-01" } } }
},
"dest": { "index": "logs-2024" }
}_reindex copia documentos entre índices. Útil para migrar mappings o filtrar datos. Acepta query en el source para copiar solo un subconjunto. Operación asíncrona.
Component Templates
PUT /_component_template/base-settings
{
"template": {
"settings": { "number_of_shards": 1 }
}
}
PUT /_index_template/logs
{
"index_patterns": ["logs-*"],
"composed_of": ["base-settings", "logs-mappings"]
}Los component templates son bloques reutilizables de settings/mappings. composed_of combina varios en un index template. Facilita el mantenimiento y la consistencia entre índices.
Listar Nodos e Índices
GET /_cat/nodes?v GET /_cat/indices?v&s=index GET /_cat/shards?v GET /_cat/allocation?v // Parámetro v = muestra cabeceras // s = ordenar por campo
La API _cat es legible para humanos. ?v muestra cabeceras, &s=campo ordena. _cat/indices lista índices con tamaño, docs y salud.
Index Templates
PUT /_index_template/logs-template
{
"index_patterns": ["logs-*"],
"priority": 100,
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"timestamp": { "type": "date" },
"message": { "type": "text" }
}
}
}
}Los index_templates aplican settings/mappings automáticamente a los índices nuevos que coinciden con el index_patterns. Esencial para series temporales (logs, métricas).
Crear Índice
PUT /products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}PUT /nombre crea un índice. number_of_shards define particiones primarias (fijo tras la creación). number_of_replicas son copias para redundancia (ajustable).
Shards y Réplicas
// Ver distribución de shards
GET /_cat/shards/products?v
// Mover shard manualmente
POST /_cluster/reroute
{
"commands": [{
"move": {
"index": "products", "shard": 0,
"from_node": "node1", "to_node": "node2"
}
}]
}Los shards son particiones horizontales del índice. Las réplicas son copias para alta disponibilidad. Los shards primarios son fijos en la creación; las réplicas son ajustables dinámicamente.
Gestionar Índices
GET /products/_settings
GET /products/_mapping
PUT /products/_settings
{ "number_of_replicas": 2 }
DELETE /products
POST /products/_close
POST /products/_open_settings y _mapping muestran la configuración. DELETE borra el índice. _close/_open desactiva/reactiva sin borrar (ahorra recursos).
Rollover
POST /logs-000001/_rollover
{
"conditions": {
"max_age": "7d",
"max_docs": 10000000,
"max_primary_shard_size": "50gb"
}
}_rollover crea un índice nuevo cuando se cumplen condiciones (edad, docs, tamaño). Se usa con aliases e ILM para gestión automática de índices temporales.
Documentos
Indexar (Auto-ID)
POST /products/_doc
{
"name": "Laptop Pro",
"price": 1299.99,
"tags": ["tech", "laptop"],
"stock": 15
}
// Respuesta: { "_id": "abc123", "result": "created" }POST /índice/_doc indexa con ID automático. El documento se analiza e indexa para búsqueda. result: "created" confirma la creación. Casi tiempo real (refresh 1s).
Actualizar (Script)
POST /products/_update/1
{
"script": {
"source": "ctx._source.stock -= params.qty",
"params": { "qty": 1 }
}
}
// Upsert con script:
POST /counters/_update/views
{
"script": { "source": "ctx._source.total += 1" },
"upsert": { "total": 1 }
}script permite lógica en Painless. ctx._source accede a los campos. params evita hardcoding. upsert crea el doc si no existe.
Update by Query
POST /products/_update_by_query
{
"query": {
"term": { "category": "informática" }
},
"script": {
"source": "ctx._source.discount = 0.1"
}
}_update_by_query aplica un script a todos los documentos que coinciden. Útil para migraciones y actualizaciones masivas. Asíncrono — verifica el progreso con _tasks.
Indexar (ID Manual)
PUT /products/_doc/1
{
"name": "Ratón Inalámbrico",
"price": 29.99
}
// Si ya existe, sobrescribe (result: "updated")
// Para garantizar solo creación:
PUT /products/_create/1
{ "name": "Ratón" }PUT /índice/_doc/ID define el ID manualmente. Si existe, sobrescribe. _create falla si el documento ya existe (evita overwrites accidentales).
Eliminar Documento
DELETE /products/_doc/1
// Eliminar por query
POST /products/_delete_by_query
{
"query": {
"term": { "stock": 0 }
}
}DELETE /índice/_doc/ID elimina un documento. _delete_by_query elimina todos los que coinciden con la query. Operación asíncrona — usa wait_for_completion=false para no bloquear.
Obtener Documento
GET /products/_doc/1
// Solo el source (sin metadata)
GET /products/_source/1
// Campos específicos
GET /products/_doc/1?_source=name,price
// Múltiples documentos
POST /products/_mget
{ "ids": ["1", "2", "3"] }GET /índice/_doc/ID retorna documento + metadata (_version, _seq_no). _source filtra campos. _mget obtiene varios en una llamada.
Bulk API
POST /_bulk
{"index": {"_index": "products", "_id": "1"}}
{"name": "Teclado", "price": 49}
{"index": {"_index": "products", "_id": "2"}}
{"name": "Monitor", "price": 299}
{"update": {"_index": "products", "_id": "1"}}
{"doc": {"price": 45}}
{"delete": {"_index": "products", "_id": "2"}}_bulk ejecuta múltiples operaciones en una llamada. Formato: línea de acción + línea de datos. Acciones: index, create, update, delete. Máx. recomendado: 5-15 MB por request.
Actualizar (Parcial)
POST /products/_update/1
{
"doc": {
"price": 999.99,
"on_sale": true
}
}_update con "doc" hace un merge parcial — solo cambia los campos indicados. Internamente hace get + reindex. Más eficiente que reenviar el documento completo.
Concurrencia Optimista
// Obtener versión actual
GET /products/_doc/1
// → "_seq_no": 5, "_primary_term": 1
// Actualizar con versión
PUT /products/_doc/1?if_seq_no=5&if_primary_term=1
{
"name": "Ratón v2",
"price": 34.99
}
// Falla con 409 si la versión cambióif_seq_no e if_primary_term implementan control de concurrencia optimista. Si otro proceso alteró el documento, retorna 409 Conflict. Evita overwrites perdidos.
Busca e Filtros
match (Full-Text)
GET /products/_search
{
"query": {
"match": {
"name": "laptop pro"
}
}
}match analiza el texto y búsqueda por tokens. "laptop pro" → búsqueda "laptop" OR "pro". Usa el analyzer del campo. Ideal para campos text con búsqueda natural.
match_phrase y fuzzy
// Frase exacta (el orden importa)
{ "match_phrase": { "name": "laptop pro" } }
// Con slop (tolerancia de distancia)
{ "match_phrase": { "name": { "query": "laptop rápido", "slop": 2 } } }
// Fuzzy (tolerar typos)
{ "match": { "name": { "query": "laptp", "fuzziness": "AUTO" } } }match_phrase exige tokens en el orden exacto. slop permite distancia entre palabras. fuzziness: "AUTO" tolera errores de escritura (1-2 caracteres). Genial para UX de búsqueda.
Paginación (from/size)
GET /products/_search
{
"query": { "match_all": {} },
"from": 20,
"size": 10
}
// Límite por defecto: from + size <= 10000
// Para más, usar search_afterfrom es el offset, size el número de resultados. Límite: from + size ≤ 10000 (configurable vía max_result_window). Para datasets grandes, usa search_after.
term (Coincidencia Exacta)
GET /products/_search
{
"query": {
"term": {
"status": "active"
}
}
}
// Múltiples valores:
{ "terms": { "tags": ["tech", "gaming"] } }term búsqueda el valor exacto sin análisis. Para campos keyword, boolean, number, date. terms acepta array (OR). No usar en campos text.
multi_match
GET /products/_search
{
"query": {
"multi_match": {
"query": "laptop",
"fields": ["name^3", "description", "tags^2"],
"type": "best_fields"
}
}
}multi_match búsqueda en múltiples campos. ^3 da peso triple al campo. Tipos: best_fields (por defecto), most_fields, cross_fields, phrase.
query_string y simple_query_string
{
"query_string": {
"query": "(laptop OR tablet) AND -usado",
"default_field": "name"
}
}
// Versión segura (sin errores de sintaxis):
{
"simple_query_string": {
"query": "laptop +pro -usado",
"fields": ["name", "description"]
}
}query_string soporta sintaxis Lucene (AND, OR, NOT, *, ~). simple_query_string es más seguro — nunca lanza error de parse. Ideal para input directo del usuario.
bool (Combinar Condiciones)
GET /products/_search
{
"query": {
"bool": {
"must": [{ "match": { "name": "pro" } }],
"must_not": [{ "term": { "status": "sold_out" } }],
"should": [{ "term": { "tags": "sale" } }],
"filter": [{ "range": { "price": { "lte": 1500 } } }]
}
}
}bool combina queries: must (AND, con score), filter (AND, sin score, con caché), must_not (NOT), should (OR, opcional). El más usado.
wildcard, prefix y exists
// Patrón con * y ?
{ "wildcard": { "code": "PRD-*" } }
// Prefijo
{ "prefix": { "name": "lap" } }
// El campo existe
{ "exists": { "field": "tags" } }
// Regex (usar con cuidado)
{ "regexp": { "code": "PRD-[0-9]+" } }wildcard usa * (cualquier secuencia) y ? (un carácter). prefix búsqueda por el inicio. exists verifica la presencia del campo. regexp es potente pero lento.
range (Intervalos)
{ "range": { "price": { "gte": 100, "lte": 500 } } }
{ "range": { "date": { "gte": "2024-01-01", "lt": "2025-01-01" } } }
{ "range": { "age": { "gt": 18 } } }
// Operadores: gt, gte, lt, lterange filtra por intervalo numérico o temporal. Operadores: gt (mayor), gte (mayor o igual), lt (menor), lte (menor o igual). Funciona con fechas ISO.
Ordenación (sort)
GET /products/_search
{
"query": { "match_all": {} },
"sort": [
{ "price": "asc" },
{ "name.raw": "desc" },
{ "_score": "desc" }
]
}sort ordena los resultados. asc o desc. Los campos text necesitan un subcampo keyword para ordenar. Al usar sort, el _score se desactiva por defecto.
Mappings
Definir Mapping
PUT /products
{
"mappings": {
"properties": {
"name": { "type": "text" },
"price": { "type": "float" },
"tags": { "type": "keyword" },
"created": { "type": "date" },
"active": { "type": "boolean" }
}
}
}mappings define el esquema del índice — tipos y comportamiento de cada campo. Debe definirse antes de indexar. Tipos incorrectos causan problemas de búsqueda irreversibles.
Ver y Actualizar Mapping
GET /products/_mapping
// Añadir campo nuevo (permitido):
PUT /products/_mapping
{
"properties": {
"category": { "type": "keyword" }
}
}
// ❌ No se puede cambiar el tipo de un campo existente
// Solución: reindex a un índice nuevoPuedes añadir campos nuevos a un mapping existente. Pero no puedes alterar el tipo de un campo ya creado. Para cambiar tipos, crea un índice nuevo y usa _reindex.
Index y doc_values
// Desactivar indexación (solo almacenar):
"internal_note": {
"type": "text",
"index": false
}
// Desactivar doc_values (sin ordenación/agregación):
"long_description": {
"type": "keyword",
"doc_values": false
}index: false excluye el campo de la búsqueda (ahorra espacio). doc_values: false desactiva ordenación/agregación. Optimiza el almacenamiento para campos que solo necesitan retornarse.
Tipos de Campo
text // full-text (analizado, para búsqueda) keyword // exacto (no analizado, para filtros/ordenación) integer, long, float, double, short, byte boolean date // "yyyy-MM-dd" o epoch ip // direcciones IPv4/IPv6 geo_point // coordenadas geográficas nested // objetos independientes object // objeto aplanado (por defecto)
text se analiza (tokeniza) para búsqueda full-text. keyword es exacto — para filtros, agregaciones y ordenación. nested mantiene los objetos como documentos separados.
Formatos de Fecha
"created": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
}
// "strict_date_optional_time" (por defecto)
// Acepta: "2024-03-15" o "2024-03-15T10:30:00Z"
// epoch_millis: 1710500000000Los campos date aceptan formatos configurables. || separa múltiples formatos. epoch_millis acepta timestamp Unix en ms. El estándar es strict_date_optional_time.
Multi-fields
"name": {
"type": "text",
"fields": {
"raw": { "type": "keyword" },
"lower": {
"type": "keyword",
"normalizer": "lowercase"
}
}
}
// name → búsqueda full-text
// name.raw → ordenación/filtro exacto
// name.lower → filtro case-insensitivefields indexa el mismo valor de formas diferentes. name para búsqueda, name.raw para ordenación/agregación. Patrón esencial para campos que necesitan ambos usos.
Nested vs Object
// object (por defecto) - aplanado, pierde la relación:
"comments": { "type": "object" }
// nested - cada elemento es independiente:
"comments": {
"type": "nested",
"properties": {
"author": { "type": "keyword" },
"text": { "type": "text" }
}
}
// Query nested:
{ "nested": { "path": "comments", "query": { ... } } }object aplana arrays — pierde la relación entre campos del mismo elemento. nested mantiene cada objeto independiente. Usa nested cuando necesitas filtrar por combinación de campos.
Dynamic Mapping
PUT /products
{
"mappings": {
"dynamic": "strict",
"properties": {
"name": { "type": "text" }
}
}
}
// "true" (por defecto): infiere tipos automáticamente
// "false": ignora campos no mapeados
// "strict": rechaza campos no mapeados (error)dynamic controla campos no previstos. true (por defecto) infiere tipos — arriesgado en producción. strict rechaza campos desconocidos. Recomendado: mapear explícitamente.
Normalizers
PUT /products
{
"settings": {
"analysis": {
"normalizer": {
"lowercase": {
"type": "custom",
"filter": ["lowercase", "asciifolding"]
}
}
}
},
"mappings": {
"properties": {
"code": { "type": "keyword", "normalizer": "lowercase" }
}
}
}normalizer aplica transformaciones a campos keyword en la indexación (lowercase, asciifolding). A diferencia del analyzer, opera sobre el texto entero. Útil para filtros case-insensitive.
Agregações
terms (Agrupar por Valor)
GET /products/_search
{
"size": 0,
"aggs": {
"by_tag": {
"terms": { "field": "tags", "size": 20 }
}
}
}
// → buckets: [{key:"tech", doc_count:45}, ...]terms agrupa por valores distintos de un campo keyword. size: 0 en el search evita retornar documentos. Retorna buckets con key y conteo. El más usado.
stats y cardinality
"aggs": {
"statistics": {
"stats": { "field": "price" }
},
"unique_categories": {
"cardinality": { "field": "category" }
}
}
// stats → count, min, max, avg, sum
// cardinality → conteo distinto (aproximado)stats retorna count, min, max, avg y sum de una vez. cardinality cuenta valores distintos (como COUNT DISTINCT). Es aproximada (HyperLogLog) — error ~1%.
composite (Paginación de aggs)
"aggs": {
"all_groups": {
"composite": {
"size": 100,
"sources": [
{ "cat": { "terms": { "field": "category" } } },
{ "brand": { "terms": { "field": "brand" } } }
],
"after": { "cat": "tech", "brand": "ASUS" }
}
}
}composite pagina agregaciones con after. Supera el límite de 10000 buckets de terms. Combina múltiples fuentes. Ideal para exportar todos los grupos de un dataset grande.
Métricas (avg, sum, min, max)
"aggs": {
"avg_price": { "avg": { "field": "price" } },
"total_stock": { "sum": { "field": "stock" } },
"cheapest": { "min": { "field": "price" } },
"most_expensive": { "max": { "field": "price" } },
"total_value": {
"script": { "source": "doc['price'].value * doc['stock'].value" }
}
}Agregaciones de métrica: avg, sum, min, max. script permite cálculos custom. Funciona en campos numéricos. Retorna valor único por agregación.
range y histogram
"aggs": {
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50 },
{ "from": 50, "to": 200 },
{ "from": 200 }
]
}
},
"histogram": {
"histogram": { "field": "price", "interval": 50 }
}
}range agrupa en intervalos personalizados. histogram usa intervalos fijos. Ambos retornan buckets con conteos. Útil para distribuciones e informes por franja.
date_histogram
"aggs": {
"sales_month": {
"date_histogram": {
"field": "created",
"calendar_interval": "month",
"format": "yyyy-MM",
"min_doc_count": 0
}
}
}date_histogram agrupa por período temporal. calendar_interval: minute, hour, day, week, month, quarter, year. min_doc_count: 0 incluye períodos vacíos. Esencial para series temporales.
filter y filters
"aggs": {
"active": {
"filter": { "term": { "status": "active" } },
"aggs": { "average": { "avg": { "field": "price" } } }
},
"by_status": {
"filters": {
"filters": {
"available": { "term": { "stock_gt": 0 } },
"sold_out": { "term": { "stock": 0 } }
}
}
}
}filter restringe una agregación a documentos que cumplen la condición. filters crea múltiples buckets nombrados con condiciones diferentes. Más eficiente que terms para pocos valores.
Agregaciones Anidadas
"aggs": {
"by_category": {
"terms": { "field": "category" },
"aggs": {
"avg_price": { "avg": { "field": "price" } },
"top_product": {
"top_hits": { "size": 1, "sort": [{ "sales": "desc" }] }
}
}
}
}Las agregaciones pueden contener sub-agregaciones (aggs dentro de aggs). Las métricas se calculan por bucket. top_hits retorna documentos representativos de cada grupo.
percentiles
"aggs": {
"latency_percentiles": {
"percentiles": {
"field": "response_time",
"percents": [50, 90, 95, 99]
}
},
"percentile_ranks": {
"percentile_ranks": {
"field": "response_time",
"values": [200, 500]
}
}
}percentiles muestra la distribución (p50, p90, p99). Esencial para latencias. percentile_ranks dice qué porcentaje está por debajo de un valor. Aproximado (algoritmo t-digest).
Análise de Texto
Analyze API
POST /_analyze
{
"analyzer": "standard",
"text": "¡El Portátil Pro es muy rápido!"
}
// → ["el", "portátil", "pro", "es", "muy", "rápido"]
// Probar con el analyzer del índice:
POST /products/_analyze
{ "field": "name", "text": "Portátil Gaming" }_analyze muestra cómo se tokeniza el texto. Esencial para depurar búsquedas. Prueba con el analyzer del campo real usando POST /índice/_analyze con field.
Token Filters
lowercase // minúsculas asciifolding // elimina acentos (ã → a) stop // elimina stop words stemmer // reduce a la raíz (spanish) synonym // sinónimos length // filtra por longitud truncate // trunca tokens anchos unique // elimina tokens duplicados
Token filters transforman tokens tras la tokenización. asciifolding normaliza acentos. stemmer reduce palabras a la raíz. synonym expande con sinónimos. Se encadenan en orden.
Search Analyzer vs Index Analyzer
"name": {
"type": "text",
"analyzer": "autocomplete",
"search_analyzer": "standard"
}
// Indexación: edge_ngram (genera prefijos)
// Búsqueda: standard (búsqueda el token entero)
// "gat" → match en "gato", "gatito"analyzer se usa en la indexación, search_analyzer en la búsqueda. Separarlos permite indexar con ngrams pero buscar con el token entero. Esencial para autocompletado eficiente.
Analyzers Integrados
standard // unicode, lowercase (por defecto) simple // solo letras, minúsculas whitespace // separa por espacios (sin lowercase) stop // standard + elimina stop words keyword // sin análisis (texto entero = 1 token) english // stemming + stop words en inglés
standard es el estándar — tokeniza por Unicode y pasa a minúsculas. keyword no analiza (texto entero). stop elimina palabras comunes. Elige según el caso de uso.
Stemming (español)
"filter": {
"es_stem": {
"type": "stemmer",
"language": "light_spanish"
}
}
// "portátiles" → "portátil"
// "computadoras" → "computador"
// "programación" → "program"
// Opciones: "spanish" (agresivo) o "light_spanish"stemmer reduce palabras a la raíz. light_spanish es menos agresivo (recomendado). Mejora el recall (encuentra plurales/conjugaciones). Puede reducir precisión — probar siempre.
Analyzer Personalizado
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "standard",
"filter": ["lowercase", "asciifolding", "es_stem"]
}
},
"filter": {
"es_stem": { "type": "stemmer", "language": "light_spanish" }
}
}
}Un analyzer custom combina tokenizer + filters. asciifolding elimina acentos. light_spanish hace stemming (portátiles → portátil). El orden de los filters importa.
Synonyms
"filter": {
"my_synonyms": {
"type": "synonym",
"synonyms": [
"portátil, notebook, laptop",
"móvil, celular, smartphone",
"pantalla, display, monitor"
]
}
}synonym expande tokens con equivalentes. "portátil" → búsqueda también "notebook" y "laptop". Formato: valores separados por comas. Puede usarse un archivo externo con synonyms_path.
Tokenizers
standard // unicode word boundaries
whitespace // separa por espacios
keyword // sin tokenización (1 token)
pattern // regex custom: "[^\p{L}\d]+"
ngram // subsecuencias: "cat" → "ca", "at"
edge_ngram // prefijos: "gato" → "g", "ga", "gat"
path_hierarchy // "a/b/c" → "a", "a/b", "a/b/c"tokenizer divide el texto en tokens. standard es el más usado. edge_ngram es ideal para autocompletado (prefijos). pattern usa regex custom para la división.
Autocompletado (edge_ngram)
"settings": {
"analysis": {
"analyzer": {
"autocomplete": {
"tokenizer": "autocomplete_tokenizer"
}
},
"tokenizer": {
"autocomplete_tokenizer": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 15,
"token_chars": ["letter", "digit"]
}
}
}
}edge_ngram genera prefijos progresivos: "gato" → "ga", "gat", "gato". Ideal para autocompletado. Usa un analyzer diferente en la indexación (edge_ngram) y en la búsqueda (standard).
Performance
Filter vs Query (caché)
"bool": {
"filter": [
{ "term": { "status": "active" } },
{ "range": { "price": { "lte": 100 } } }
],
"must": [
{ "match": { "name": "laptop" } }
]
}filter no calcula score y se cachea — mucho más rápido. Usa filter para condiciones exactas (status, range, term). must solo para relevancia full-text. Regla: filter por defecto.
Refresh y Flush
// Forzar refresh (los documentos quedan buscables) POST /products/_refresh // Flush (grabar segmentos en disco) POST /products/_flush // Ver segmentos: GET /_cat/segments/products?v // Refresh interval por defecto: 1s (near real-time)
refresh hace que los documentos sean buscables (crea segmento en memoria). Por defecto: 1s. flush persiste en disco. Aumentar refresh_interval mejora el throughput de escritura.
Profile API
GET /products/_search
{
"profile": true,
"query": {
"match": { "name": "portátil" }
}
}
// → muestra el tiempo de cada fase:
// build_scorer, next_doc, advance, score, etc."profile": true detalla el tiempo de ejecución de cada componente de la query. Muestra shards, collectors y tiempo por operación. Herramienta esencial para diagnosticar queries lentas.
Source Filtering
GET /products/_search
{
"_source": ["name", "price"],
"query": { "match_all": {} }
}
// Excluir campos grandes:
{ "_source": { "excludes": ["long_description"] } }_source limita los campos retornados. Reduce ancho de banda y memoria. excludes elimina campos pesados. No afecta a la búsqueda — solo lo que se devuelve al cliente.
Force Merge
// Consolidar segmentos (¡solo para índices read-only!) POST /logs-2024/_forcemerge?max_num_segments=1 // Ver segmentos antes/después: GET /_cat/segments/logs-2024?v // ⚠️ NUNCA en índices activos (mucho I/O)
_forcemerge consolida segmentos en uno solo. Mejora el rendimiento de búsqueda. Solo usar en índices que ya no reciben writes (tras rollover). Operación pesada — ejecutar en off-peak.
search_after (Paginación Profunda)
// Página 1:
GET /products/_search
{
"size": 100,
"sort": [{ "created": "desc" }, { "_id": "asc" }],
"query": { "match_all": {} }
}
// Página 2 (usar el último sort value):
{ "search_after": ["2024-06-15T10:00:00Z", "doc_99"] }search_after pagina más allá del límite de 10000. Usa los valores de sort del último documento como cursor. Más eficiente que scroll. Requiere sort con campo único (tiebreaker).
Routing
// Indexar con routing custom:
PUT /products/_doc/1?routing=tenant_42
{ "name": "Producto", "tenant": "tenant_42" }
// Buscar solo en el shard del tenant:
GET /products/_search?routing=tenant_42
{
"query": { "match": { "name": "producto" } }
}routing dirige documentos a un shard específico. Las búsquedas con routing consultan solo 1 shard en vez de todos. Esencial para multi-tenancy. Reduce la latencia drásticamente.
Bulk Indexing Optimizado
// Antes de la ingestión masiva:
PUT /logs/_settings
{
"number_of_replicas": 0,
"refresh_interval": "30s"
}
// Tras la ingestión, restaurar:
PUT /logs/_settings
{
"number_of_replicas": 1,
"refresh_interval": "1s"
}Para bulk indexing: desactiva replicas y aumenta refresh_interval. Reduce el I/O drásticamente. Restaura al terminar. Usa batches de 5-15 MB con _bulk.
Request Cache
// Activar caché de resultados (por defecto: solo size=0)
GET /products/_search?request_cache=true
{
"size": 0,
"aggs": { "average": { "avg": { "field": "price" } } }
}
// Limpiar caché:
POST /products/_cache/clear
// Ver uso:
GET /_cat/nodes?v&h=name,request_cache_memory_sizerequest_cache guarda resultados de agregaciones en el nodo. Por defecto, solo cachea queries con size: 0. Se invalida automáticamente en el refresh. Ideal para dashboards con datos poco mutables.
Avançado
Aliases
POST /_aliases
{
"actions": [
{ "remove": { "index": "products-v1", "alias": "products" } },
{ "add": { "index": "products-v2", "alias": "products" } }
]
}
// Las apps usan siempre el alias "products"
// El cambio de índice es transparente (cero downtime)aliases son punteros a índices. Permite cambiar de índice sin alterar código. Operaciones atómicas (remove + add en una llamada). Esencial para deploy sin downtime.
Reindex con Transformación
POST /_reindex
{
"source": { "index": "old" },
"dest": { "index": "new" },
"script": {
"source": """
ctx._source.full_name = ctx._source.name;
ctx._source.remove('name');
ctx._source.migrated = true;
"""
}
}_reindex con script transforma documentos durante la migración. Renombra campos, añade flags, elimina datos. Útil para evolucionar mappings sin downtime.
Cross-Cluster Search
// Configurar cluster remoto:
PUT /_cluster/settings
{
"persistent": {
"cluster.remote.dc2.seeds": ["dc2-node1:9300"]
}
}
// Buscar en ambos:
GET /products,dc2:products/_search
{
"query": { "match": { "name": "portátil" } }
}Cross-cluster search consulta múltiples clusters de Elasticsearch en una query. Se configura con cluster.remote. Se referencia como cluster:índice. Útil para multi-datacenter.
ILM (Index Lifecycle Management)
PUT /_ilm/policy/logs-policy
{
"policy": {
"phases": {
"hot": { "actions": {
"rollover": { "max_size": "50gb", "max_age": "7d" }
}},
"warm": { "min_age": "7d", "actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 }
}},
"delete": { "min_age": "30d", "actions": { "delete": {} } }
}
}
}ILM automatiza el ciclo de vida: hot → warm → cold → delete. rollover crea un índice nuevo al alcanzar los límites. shrink y forcemerge optimizan en warm. Esencial para logs.
Percolator (alertas)
// Registrar queries:
PUT /alerts/_doc/1
{ "query": { "match": { "message": "error crítico" } } }
// Verificar documento contra las queries registradas:
GET /alerts/_search
{
"query": {
"percolate": {
"field": "query",
"document": { "message": "Ocurrió un error crítico en el servidor" }
}
}
}percolator invierte la búsqueda: registra queries y verifica documentos contra ellas. Ideal para alertas y notificaciones. "¿Qué queries corresponden a este documento?"
Suggesters (sugerencias)
GET /products/_search
{
"suggest": {
"correction": {
"text": "portatil gaming",
"term": { "field": "name", "size": 3 }
},
"phrase_suggest": {
"text": "portatil gamming",
"phrase": { "field": "name" }
}
}
}term suggester sugiere correcciones por token. phrase suggester sugiere frases completas (más inteligente). completion suggester usa FST para autocompletado ultrarrápido.
Scripted Fields
GET /products/_search
{
"query": { "match_all": {} },
"script_fields": {
"price_with_vat": {
"script": { "source": "doc['price'].value * 1.23" }
},
"discount_pct": {
"script": { "source": "(doc['old_price'].value - doc['price'].value) / doc['old_price'].value * 100" }
}
}
}script_fields calcula campos al momento con Painless. No se indexan — se calculan en cada query. Útil para valores derivados. Más lento que campos precalculados.
Completion Suggester
// Mapping:
"suggestion": {
"type": "completion",
"analyzer": "simple"
}
// Query:
GET /products/_search
{
"suggest": {
"auto": {
"prefix": "port",
"completion": { "field": "suggestion", "size": 5, "fuzzy": { "fuzziness": "AUTO" } }
}
}
}completion suggester está optimizado para autocompletado. Usa estructura FST en memoria — extremadamente rápido. fuzzy tolera typos. Indexa sugerencias en un campo dedicado tipo completion.
Highlighting
GET /products/_search
{
"query": { "match": { "description": "portátil rápido" } },
"highlight": {
"pre_tags": ["<mark>"],
"post_tags": ["</mark>"],
"fields": {
"description": { "fragment_size": 150, "number_of_fragments": 3 }
}
}
}highlight retorna fragmentos con los términos encontrados destacados. pre_tags/post_tags definen el markup. fragment_size controla el tamaño de los extractos. Esencial para UX de búsqueda.
Pipelines e Ingestão
Crear Pipeline
PUT /_ingest/pipeline/clean-products
{
"description": "Normalizar datos de productos",
"processors": [
{ "lowercase": { "field": "name" } },
{ "trim": { "field": "name" } },
{ "remove_null": { "field": "description" } },
{ "set": {
"field": "created_at",
"value": "{{_ingest.timestamp}}"
}}
]
}Ingest pipelines transforman documentos antes de indexar. Los procesadores se encadenan en orden. Se crean con PUT /_ingest/pipeline/nombre. Se ejecutan en el nodo de ingestión.
Grok (parse de logs)
{
"grok": {
"field": "message",
"patterns": [
"%{IP:ip} - %{WORD:user} [%{HTTPDATE:date}] \"%{WORD:method} %{URIPATHPARAM:url}\" %{INT:status} %{INT:bytes}"
]
}
}
// Input: "192.168.1.1 - admin [10/Oct/2024:13:55:36] \"GET /api\" 200 1234"
// → { ip, user, date, method, url, status, bytes }grok extrae campos estructurados de texto no estructurado con patrones. %{TIPO:campo} captura valores. Esencial para parsear logs. Puede ser lento — probar el rendimiento.
Listar y Eliminar Pipelines
// Listar todos:
GET /_ingest/pipeline
// Ver uno específico:
GET /_ingest/pipeline/clean-products
// Eliminar:
DELETE /_ingest/pipeline/clean-products
// Probar processor individual:
POST /_ingest/pipeline/_simulate
{
"pipeline": { "processors": [{ "uppercase": { "field": "name" } }] },
"docs": [{ "_source": { "name": "prueba" } }]
}GET /_ingest/pipeline lista todos. DELETE elimina. _simulate inline prueba processors sin crear pipeline. Útil para experimentación rápida.
Usar Pipeline en la Indexación
// Documento único:
PUT /products/_doc/1?pipeline=clean-products
{ "name": " PORTÁTIL PRO " }
// Bulk:
POST /_bulk?pipeline=clean-products
{"index": {"_index": "products"}}
{"name": "TECLADO"}
// Pipeline por defecto del índice:
PUT /products/_settings
{ "index.default_pipeline": "clean-products" }Se aplica con ?pipeline=nombre en la indexación. O define default_pipeline en las settings del índice para aplicarlo automáticamente. _simulate prueba sin indexar.
Dissect (parse simple)
{
"dissect": {
"field": "message",
"pattern": "%{ip} - %{user} [%{date}] \"%{method} %{url}\" %{status} %{bytes}"
}
}
// Más rápido que grok (sin regex)
// Usa delimitadores literalesdissect es una alternativa más rápida a grok para logs con formato fijo. Usa delimitadores literales en vez de regex. Menos flexible pero mucho más eficiente.
Simular Pipeline
POST /_ingest/pipeline/clean-products/_simulate
{
"docs": [
{ "_source": { "name": " PORTÁTIL ", "price": -5 } },
{ "_source": { "name": "Ratón", "description": null } }
]
}
// Muestra el resultado de cada processor_simulate prueba el pipeline con documentos de ejemplo sin indexar. Muestra el output de cada processor. Esencial para depuración. Revela errores antes de producción.
Enrich Pipeline
// 1. Crear política de enrich:
PUT /_enrich/policy/geo-ip
{
"geoip": {
"source": { "index": "ips" },
"match_field": "ip",
"enrich_fields": ["country", "city"]
}
}
// 2. Ejecutar:
POST /_enrich/policy/geo-ip/_execute
// 3. Usar en el pipeline:
{ "enrich": { "policy_name": "geo-ip", "field": "ip", "target_field": "geo" } }enrich añade datos de un índice de referencia durante la ingestión. Ejemplo: añadir geolocalización a partir de la IP. Crea la política, ejecútala y úsala como processor.
Processors Comunes
set // definir campo remove // eliminar campo rename // renombrar campo lowercase // minúsculas uppercase // mayúsculas trim // eliminar espacios convert // cambiar tipo (string → integer) split // string → array join // array → string gsub // regex replace
Processors básicos: set, remove, rename para reestructuración. lowercase, trim, gsub para limpieza. convert cambia tipos. split/join para arrays.
Manejo de Errores (on_failure)
{
"processors": [
{
"grok": {
"field": "message",
"patterns": ["%{IP:ip}"],
"on_failure": [
{ "set": { "field": "parse_error", "value": "{{_ingest.on_failure_message}}" } },
{ "set": { "field": "ip", "value": "unknown" } }
]
}
}
]
}on_failure define qué hacer si un processor falla. Evita el rechazo del documento entero. Registra el error en un campo dedicado. Puede haber on_failure por processor o a nivel del pipeline.
Segurança e Monitorização
API Keys
POST /_security/api_key
{
"name": "app-readonly",
"expiration": "30d",
"role_descriptors": {
"readonly": {
"indices": [{
"names": ["products"],
"privileges": ["read"]
}]
}
}
}
// → { "id": "...", "api_key": "...", "encoded": "base64..." }API keys autentican sin user/password. Define permisos granulares por índice. expiration limita la validez. Usa el header Authorization: ApiKey encoded. Ideal para apps y servicios.
Hot Threads y Tasks
// Threads más activos: GET /_nodes/hot_threads // Tasks en ejecución: GET /_tasks?actions=*search*&detailed=true // Cancelar task lenta: POST /_tasks/task_id:12345/_cancel // Stats por nodo: GET /_nodes/stats/jvm,os,process
hot_threads muestra los threads con más CPU. _tasks lista operaciones en curso. _cancel termina queries lentas. _nodes/stats da JVM, disco y proceso por nodo.
Diagnóstico de Problemas
// Cluster rojo:
GET /_cluster/health?level=shards
GET /_cluster/allocation/explain
// Rechazos de escritura:
GET /_nodes/stats/thread_pool
// → "write": { "rejected": 150 }
// Circuit breaker (memoria):
GET /_nodes/stats/breaker
// Disk watermark:
GET /_cluster/settings?include_defaults=true&filter_path=**.diskCluster red → allocation/explain. Rechazos → verificar thread_pool. OOM → stats de breaker. Disco lleno → disk watermark (85%/90%/95%). Enfoque sistemático de troubleshooting.
Roles y Privilegios
POST /_security/role/app_writer
{
"indices": [{
"names": ["products", "products-*"],
"privileges": ["read", "write", "create_index"],
"field_security": { "grant": ["name", "price", "tags"] },
"query": { "term": { "tenant": "app1" } }
}],
"cluster": ["monitor"]
}roles definen permisos por índice y cluster. field_security limita los campos visibles. query restringe documentos (row-level security). Combina con API keys para multi-tenancy.
Snapshots (backup)
// Registrar repositorio:
PUT /_snapshot/my_backup
{
"type": "fs",
"settings": { "location": "/mnt/backups" }
}
// Crear snapshot:
PUT /_snapshot/my_backup/snap-2024-01?wait_for_completion=true
{ "indices": "products,logs-*" }
// Restaurar:
POST /_snapshot/my_backup/snap-2024-01/_restore
{ "indices": "products" }snapshots son backups incrementales del cluster. Registra un repositorio (fs, S3, GCS). PUT crea el snapshot, _restore lo restaura. Incremental — solo guarda cambios. Esencial en producción.
Cluster Health Detallado
GET /_cluster/health?level=shards
GET /_cluster/allocation/explain
GET /_cluster/settings
GET /_cluster/pending_tasks
// Asignar shard no asignado:
POST /_cluster/reroute
{
"commands": [{ "allocate_replica": {
"index": "products", "shard": 1, "node": "node2"
}}]
}_cluster/health?level=shards muestra el estado por shard. allocation/explain explica por qué un shard no está asignado. reroute fuerza la asignación manual. Esencial para troubleshooting.
Slow Logs
PUT /products/_settings
{
"index.search.slowlog.threshold.query.warn": "10s",
"index.search.slowlog.threshold.query.info": "5s",
"index.search.slowlog.threshold.fetch.warn": "1s",
"index.indexing.slowlog.threshold.index.warn": "10s"
}Slow logs registran queries e indexación lentas. Niveles: warn, info, debug, trace. Thresholds de tiempo. Logs en logs/nombre_índice_search_slowlog.log. Esencial para diagnóstico.
Monitorización de Índices
GET /_cat/indices?v&s=store.size:desc GET /_cat/count/products?v GET /_stats GET /products/_stats // Top índices por tamaño: GET /_cat/indices?v&h=index,docs.count,store.size&s=store.size:desc&bytes=mb
_cat/indices con s=store.size:desc ordena por tamaño. _stats da métricas detalladas (indexing, search, merge). Monitorizar el crecimiento para planificar capacidad.
Seguridad (TLS y auth)
// elasticsearch.yml: xpack.security.enabled: true xpack.security.transport.ssl.enabled: true xpack.security.http.ssl.enabled: true xpack.security.http.ssl.keystore.path: http.p12 // Definir passwords: bin/elasticsearch-setup-passwords interactive // Autenticación básica: curl -u elastic:password https://localhost:9200
xpack.security activa autenticación y TLS. transport.ssl encripta la comunicación entre nodos. http.ssl encripta cliente-servidor. Activar siempre en producción. Nunca exponer sin auth.