Elasticsearch Cheat Sheet
Elasticsearch REST API essentials for indexing, searching, filtering, and aggregating documents with mappings and analyzers.
REST API Basics
Creating an index and indexing documents.
# Create an index with an explicit mappingPUT /products{ "mappings": { "properties": { "name": { "type": "text" }, "price": { "type": "float" } } }}# Index a documentPOST /products/_doc/1{ "name": "Laptop", "price": 999.99 }# Get and deleteGET /products/_doc/1DELETE /products
Search Queries
Bool queries, filters, and sorting.
GET /products/_search{ "query": { "bool": { "must": [{ "match": { "name": "laptop" } }], "filter": [{ "range": { "price": { "lte": 1500 } } }] } }, "sort": [{ "price": "asc" }], "size": 10}# Simple query string searchGET /products/_search?q=name:laptop
Core Concepts
Building blocks of the search engine.
- Index- a collection of documents, roughly analogous to a database table
- Shard- a horizontal partition of an index, enabling scale-out
- Replica- a copy of a shard for high availability and extra read throughput
- Mapping- defines each field's type and how it is indexed
- Analyzer- tokenizes and normalizes text so it can be searched
- Inverted index- the core structure mapping terms to the documents containing them
Aggregations
Computing metrics and buckets over search results.
GET /products/_search{ "size": 0, "aggs": { "avg_price": { "avg": { "field": "price" } }, "by_category": { "terms": { "field": "category.keyword" } } }}
Index Aliases & Zero-Downtime Reindexing
Use write aliases so a mapping change never requires taking search offline.
# Point an alias at the live indexPOST /_aliases{ "actions": [ { "add": { "index": "products_v1", "alias": "products", "is_write_index": true } } ]}# Create a new index with the fixed mapping, then reindexPUT /products_v2{ "mappings": { "properties": { "name": { "type": "text" }, "sku": { "type": "keyword" } } } }POST /_reindex{ "source": { "index": "products_v1" }, "dest": { "index": "products_v2" }}# Atomically flip the alias to the new index (single request = no downtime)POST /_aliases{ "actions": [ { "remove": { "index": "products_v1", "alias": "products" } }, { "add": { "index": "products_v2", "alias": "products", "is_write_index": true } } ]}
Index Lifecycle Management (ILM)
Automate rollover and retention for time-series/log indices.
PUT _ilm/policy/logs_policy{ "policy": { "phases": { "hot": { "actions": { "rollover": { "max_primary_shard_size": "50gb", "max_age": "1d" } } }, "warm": { "min_age": "3d", "actions": { "shrink": { "number_of_shards": 1 }, "forcemerge": { "max_num_segments": 1 } } }, "cold": { "min_age": "30d", "actions": { "searchable_snapshot": { "snapshot_repository": "repo1" } } }, "delete":{ "min_age": "90d", "actions": { "delete": {} } } } }}# Bind an index template to the policy + a rollover aliasPUT _index_template/logs_template{ "index_patterns": ["logs-*"], "template": { "settings": { "index.lifecycle.name": "logs_policy", "index.lifecycle.rollover_alias": "logs" } }}
Deep Pagination with search_after + PIT
Avoid the from/size 10000-result window limit and deep-pagination cost.
# Open a point-in-time context (keeps a consistent view of the index)POST /products/_pit?keep_alive=1m# -> returns { "id": "46ToAw..." }GET /_search{ "size": 100, "query": { "match_all": {} }, "pit": { "id": "46ToAw...", "keep_alive": "1m" }, "sort": [ { "price": "asc" }, { "_shard_doc": "asc" } ], "search_after": [ 999.99, "products_v2:4:12" ]}# Close the PIT when done pagingDELETE /_pit{ "id": "46ToAw..." }
Custom Analyzers & N-grams
Build an autocomplete-friendly analyzer from char filters, tokenizer, and token filters.
PUT /autocomplete_idx{ "settings": { "analysis": { "filter": { "edge_ngram_filter": { "type": "edge_ngram", "min_gram": 2, "max_gram": 15 } }, "analyzer": { "autocomplete": { "type": "custom", "tokenizer": "standard", "filter": [ "lowercase", "edge_ngram_filter" ] } } } }, "mappings": { "properties": { "title": { "type": "text", "analyzer": "autocomplete", "search_analyzer": "standard" } } }}
Advanced Internals & Gotchas
Concepts that separate production tuning from default settings.
- Routing- controls which shard a document lands on; custom routing speeds up single-tenant queries but can create hot shards
- Refresh interval- new docs aren't searchable until a refresh (default 1s); bulk-loading workloads often set index.refresh_interval to -1 and refresh manually
- Doc values- columnar on-disk structure that backs sorting/aggregations; disabled automatically for analyzed text fields
- Circuit breakers- guard rails (fielddata, request, parent) that reject operations before they OOM the node
- Shard over-allocation- too many small shards wastes heap on cluster state; aim for shards in the tens-of-GB range, not thousands of tiny ones
- _source filtering- use "_source": ["field1"] to cut network payload when you only need a few fields
- Version conflicts- optimistic concurrency via if_seq_no/if_primary_term prevents lost updates under concurrent writes
Use the .keyword sub-field (e.g. category.keyword) for exact-match filtering, sorting, and aggregations — plain "text" fields are analyzed/tokenized and won't behave as you expect for those operations.