Slow Elasticsearch queries are usually fixable. In our experience as Elasticsearch consultants, most performance problems come down to a few root causes: bad mappings, inefficient queries, or misconfigured clusters.
This guide walks through a systematic optimization process. We've used this approach to achieve 50x speedups on production clusters.
Step 1: Identify Slow Queries
Before optimizing, you need to know what's slow. Enable slow query logging:
PUT /my-index/_settings
{
"index.search.slowlog.threshold.query.warn": "1s",
"index.search.slowlog.threshold.query.info": "500ms",
"index.search.slowlog.threshold.query.debug": "200ms",
"index.search.slowlog.level": "info"
}
Now queries over 500ms appear in your logs. Look for patterns:
- Which queries are slow?
- What do they have in common?
- Are they always slow or only under load?
Step 2: Profile Your Queries
Use the Profile API to see exactly where time is spent:
GET /my-index/_search
{
"profile": true,
"query": {
"match": { "content": "elasticsearch performance" }
}
}
The profile output shows:
- Query time: How long to find matching documents
- Collect time: How long to score and rank results
- Fetch time: How long to retrieve document data
This tells you where to focus. High query time? Check your mappings. High fetch time? You're returning too much data.
Step 3: Optimize Your Mappings
Bad mappings are the #1 cause of slow queries. Here's what to check:
Use the Right Field Types
| Use Case | Field Type | Why |
|---|---|---|
| Full-text search | text |
Analyzed for search |
| Exact match / filtering | keyword |
Not analyzed, faster for exact match |
| Numeric ranges | integer, float |
Optimized for range queries |
| Dates | date |
Optimized for date math and ranges |
| Boolean flags | boolean |
Single bit, very fast |
Multi-Field Mappings
For fields you need to both search and filter, use multi-field mappings:
{
"properties": {
"category": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
Now you can:
- Search:
match: { "category": "electronics" } - Filter:
term: { "category.keyword": "Electronics" } - Aggregate:
terms: { "field": "category.keyword" }
Disable What You Don't Need
Every feature has overhead. Disable what you don't use:
{
"properties": {
"internal_id": {
"type": "keyword",
"doc_values": false, // Don't need for aggregations
"norms": false // Don't need for scoring
},
"raw_data": {
"type": "object",
"enabled": false // Store but don't index
}
}
}
Step 4: Optimize Your Queries
Use Filter Context for Non-Scoring Clauses
This is the single biggest optimization most people miss. Anything that's a yes/no question (not "how relevant") should be in a filter:
// SLOW: Everything in "must"
{
"query": {
"bool": {
"must": [
{ "match": { "title": "elasticsearch" } },
{ "term": { "status": "published" } },
{ "range": { "date": { "gte": "2025-01-01" } } }
]
}
}
}
// FAST: Filters where appropriate
{
"query": {
"bool": {
"must": [
{ "match": { "title": "elasticsearch" } }
],
"filter": [
{ "term": { "status": "published" } },
{ "range": { "date": { "gte": "2025-01-01" } } }
]
}
}
}
Filters are:
- Cached: Results are stored for reuse
- Score-free: No relevancy calculation overhead
- Often 2-5x faster than equivalent must clauses
Avoid Leading Wildcards
Leading wildcards kill performance:
// SLOW: Scans every term in the index
{ "wildcard": { "name": "*smith" } }
// FAST: Can use the index efficiently
{ "wildcard": { "name": "smith*" } }
// BETTER: Use n-gram analyzer for substring matching
{ "match": { "name.ngram": "smith" } }
Limit Result Size
{
"size": 20, // Only return 20 results
"from": 0, // Start from first result
"_source": ["title", "author", "date"], // Only these fields
"query": { ... }
}
Never return more documents than you display. Deep pagination (from > 10000) is slow — use search_after instead.
Step 5: Leverage Caching
Filter Cache
Elasticsearch automatically caches filter results. To maximize cache effectiveness:
- Put repeated clauses in filters (status, date ranges, category)
- Use the same filter values across queries
- Avoid unique values in filters (user IDs, timestamps)
Request Cache
For static or near-static data, enable request caching:
GET /my-index/_search?request_cache=true
{
"size": 0,
"aggs": {
"popular_categories": {
"terms": { "field": "category.keyword", "size": 10 }
}
}
}
Request cache stores entire query results. Perfect for dashboards and aggregations that don't change frequently.
Step 6: Index-Level Optimizations
Force Merge Read-Only Indexes
For indexes that don't receive writes (logs, historical data), force merge to reduce segment count:
POST /logs-2025-01/_forcemerge?max_num_segments=1
Fewer segments = faster queries. Only do this on read-only indexes.
Tune Refresh Interval
Default is 1 second. If you don't need real-time search:
PUT /my-index/_settings
{
"index.refresh_interval": "30s"
}
Longer intervals = fewer segments = better query performance.
Real-World Example: 50x Speedup
Here's a real optimization we did for a legal document search system:
Before:
- Average query time: 5.2 seconds
- p99 query time: 12+ seconds
- Frequent timeouts
Problems found:
- Dynamic mapping had created inefficient field types
- All clauses in
must, none infilter - Returning full 50KB documents when only title was displayed
- 100 shards for 500GB of data (way over-sharded)
- 1-second refresh interval on static legal documents
Fixes applied:
- Reindexed with explicit mappings
- Moved date/status/type filters to filter context
- Source filtered to return only displayed fields
- Consolidated to 10 shards
- Changed refresh interval to 60 seconds
After:
- Average query time: 98ms
- p99 query time: 250ms
- Zero timeouts
A 53x improvement. The cluster handled 10x more queries per second on the same hardware.
Optimization Checklist
- Enable slow query logging to find problem queries
- Profile queries to understand where time is spent
- Review mappings: right field types, disable unused features
- Move yes/no clauses to filter context
- Limit result size and use source filtering
- Right-size your shards (10-50GB each)
- Tune refresh interval for your use case
- Force merge read-only indexes
- Monitor and iterate
Need Help Optimizing?
We've optimized Elasticsearch clusters for legal tech, healthcare, and enterprise clients. Our Elasticsearch consulting engagements typically achieve:
- 5-50x query performance improvements
- 30-50% infrastructure cost reduction
- Elimination of timeout errors