Range, Exists, Wildcard, Prefix & Regex Queries

intermediate elasticsearch query-dsl range wildcard regex

These are the term-level queries we reach for when match and term aren’t enough. They all skip the analyzer and work on raw terms — so they’re typically used on keyword, numeric, or date fields.

Range query

In simple language — “find docs where this field is between X and Y.” Works on numbers, dates, and even strings (lexicographic).

GET /orders/_search
{
  "query": {
    "range": {
      "total_amount": {
        "gte": 100,
        "lt": 500
      }
    }
  }
}

Operators: gt (greater than), gte (greater or equal), lt (less than), lte (less or equal).

Date math

ES supports a mini date-math language. Super handy for relative time queries like “orders from the last 7 days”:

GET /orders/_search
{
  "query": {
    "range": {
      "created_at": {
        "gte": "now-7d/d",
        "lt":  "now/d"
      }
    }
  }
}

now-7d/d means “7 days ago, rounded down to the start of the day”. The /d rounding makes the query cacheable (because rounded values change less often).

Exists query

In simple language — “find docs where this field has a value.” It’s the ES equivalent of SQL’s IS NOT NULL.

GET /users/_search
{
  "query": {
    "exists": { "field": "phone_number" }
  }
}

To find docs where the field is missing, wrap it in must_not:

GET /users/_search
{
  "query": {
    "bool": {
      "must_not": [
        { "exists": { "field": "phone_number" } }
      ]
    }
  }
}

A field is considered “missing” if it’s null, [], or simply not present in the source.

Wildcard query

Pattern matching with * (any characters) and ? (single character). Works on keyword fields.

GET /users/_search
{
  "query": {
    "wildcard": {
      "email.keyword": { "value": "*@gmail.com" }
    }
  }
}
⚠ Performance warning
Leading wildcards like *gmail.com are extremely slow.
ES has to scan every term in the inverted index — no shortcuts possible.
For suffix search at scale, index a reversed copy of the field and do a prefix query on it.

Prefix query

Faster cousin of wildcard — looks for terms starting with a given string. Used for “starts with” filters.

GET /products/_search
{
  "query": {
    "prefix": {
      "sku.keyword": { "value": "APPL-" }
    }
  }
}

This is way faster than wildcard: "APPL-*" because ES can use the inverted index’s sorted structure to jump to the prefix range.

For real autocomplete, prefer the completion suggester or an edge-ngram analyzer over prefix queries — they’re built for that use case.

Regex query

Full regular expressions on keyword fields. Powerful but slow — use sparingly.

GET /logs/_search
{
  "query": {
    "regexp": {
      "user_agent.keyword": "Mozilla.*Chrome/[0-9]+.*"
    }
  }
}

ES uses a flavor of regex (Lucene’s regex syntax), not Perl/PCRE. No lookahead, no backreferences. Anchors ^ and $ are implicit — the entire term must match.

When to use which

Use case Query Speed
Number/date between X and YrangeFast
Field has a valueexistsFast
"starts with X"prefixFast
"contains pattern X*Y"wildcardMedium
Complex patternregexpSlow

All of these are term-level, so put them in filter context whenever scoring doesn’t matter. That’s a free 2-10x speedup.

Quick rules

  • All these queries skip the analyzer — use them on keyword fields or numeric/date fields.
  • Range with date math + rounding (now-1d/d) is cacheable. No rounding = no cache.
  • Avoid leading wildcards. They scan the entire index.
  • Regex looks cool in interviews — but in production, prefer prefix + a smarter mapping.