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" }
}
}
}
*gmail.com are extremely slow.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 Y | range | Fast |
| Field has a value | exists | Fast |
| "starts with X" | prefix | Fast |
| "contains pattern X*Y" | wildcard | Medium |
| Complex pattern | regexp | Slow |
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
keywordfields 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.