January 17, 2026 · 15 min read

Elasticsearch Query Optimization: From 5 Seconds to 100ms

A systematic approach to diagnosing and fixing slow Elasticsearch queries. With code examples and real optimization wins.

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 in filter
  • 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:

  1. Reindexed with explicit mappings
  2. Moved date/status/type filters to filter context
  3. Source filtered to return only displayed fields
  4. Consolidated to 10 shards
  5. 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

  1. Enable slow query logging to find problem queries
  2. Profile queries to understand where time is spent
  3. Review mappings: right field types, disable unused features
  4. Move yes/no clauses to filter context
  5. Limit result size and use source filtering
  6. Right-size your shards (10-50GB each)
  7. Tune refresh interval for your use case
  8. Force merge read-only indexes
  9. 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

Want us to audit your Elasticsearch cluster?

Book a free 30-minute architecture review. We'll identify your biggest performance bottlenecks and give you a prioritized optimization plan.

Schedule Free Review
← Back to Blog