Elasticsearch Patterns
Mappings and Analyzers
PUT /articles
{
"settings": {
"analysis": {
"analyzer": {
"custom_english": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "stop", "snowball"]
}
}
}
},
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "custom_english", "fields": { "keyword": { "type": "keyword" } } },
"body": { "type": "text", "analyzer": "custom_english" },
"tags": { "type": "keyword" },
"price": { "type": "double" },
"created": { "type": "date" },
"author": { "type": "object", "properties": { "id": { "type": "keyword" }, "name": { "type": "text" } } }
}
}
}
Query DSL
// Bool query — the workhorse
GET /articles/_search
{
"query": {
"bool": {
"must": [{ "match": { "title": "elasticsearch patterns" } }],
"filter": [{ "term": { "tags": "backend" } }, { "range": { "created": { "gte": "2024-01-01" } } }],
"should": [{ "match": { "body": "performance" } }],
"must_not": [{ "term": { "status": "draft" } }]
}
},
"highlight": { "fields": { "title": {}, "body": {} } },
"_source": ["title", "tags", "author"],
"from": 0, "size": 20
}
// Nested query (for nested objects)
GET /orders/_search
{
"query": {
"nested": {
"path": "items",
"query": { "bool": {
"must": [
{ "match": { "items.name": "laptop" } },
{ "range": { "items.price": { "gte": 500 } } }
]
}}
}
}
}
Aggregations
GET /orders/_search
{
"size": 0,
"aggs": {
"by_status": {
"terms": { "field": "status", "size": 10 },
"aggs": {
"total_revenue": { "sum": { "field": "amount" } },
"avg_amount": { "avg": { "field": "amount" } }
}
},
"revenue_over_time": {
"date_histogram": { "field": "created", "calendar_interval": "month" },
"aggs": { "revenue": { "sum": { "field": "amount" } } }
},
"price_percentiles": {
"percentiles": { "field": "amount", "percents": [50, 75, 90, 99] }
}
}
}
Index Lifecycle Management
PUT /_ilm/policy/logs-policy
{
"policy": {
"phases": {
"hot": { "actions": { "rollover": { "max_size": "50gb", "max_age": "1d" } } },
"warm": { "min_age": "7d", "actions": { "shrink": { "number_of_shards": 1 }, "forcemerge": { "max_num_segments": 1 } } },
"cold": { "min_age": "30d", "actions": { "freeze": {} } },
"delete": { "min_age": "90d", "actions": { "delete": {} } }
}
}
}
Python Client
from elasticsearch import Elasticsearch, helpers
es = Elasticsearch("https://localhost:9200", api_key="key")
# Bulk indexing
def bulk_index(docs):
actions = [
{"_index": "articles", "_id": d["id"], "_source": d}
for d in docs
]
helpers.bulk(es, actions, chunk_size=500, request_timeout=60)
# Search with pagination (search_after for deep pagination)
resp = es.search(index="articles", body={
"query": { "match_all": {} },
"sort": [{ "created": "desc" }, { "_id": "asc" }],
"size": 100
})
last_sort = resp["hits"]["hits"][-1]["sort"]
# Next page:
resp = es.search(index="articles", body={
"query": { "match_all": {} },
"sort": [{ "created": "desc" }, { "_id": "asc" }],
"search_after": last_sort,
"size": 100
})
Performance Tuning
- Shards: 1 shard per 20-40GB; avoid over-sharding
refresh_interval: 30s during bulk indexing (set back to 1s after)
number_of_replicas: 0 during initial load, restore after
- Use
filter (cached) over query (scored) when relevance not needed
- Avoid
wildcard queries on text fields — use n-gram analyzer instead
- Monitor:
_cat/indices, _nodes/stats, _cluster/health