Filters like status, price ranges and date windows are part of almost every real search request.
Term-level queries look up exact values without analyzing the input:
term: one exact value,{ "term": { "status": "paid" } },terms: any of several values,{ "terms": { "status": ["paid", "shipped"] } },range: numbers, dates or strings withgt,gte,lt,lte,exists: the field has an indexed value;prefix,wildcardandidsalso exist.
Use them on keyword, numeric, date and boolean fields. Using term on a text field is the classic mistake: the stored terms are analyzed (lower-cased, split), so { "term": { "name": "Blue pen" } } matches nothing, while match would. If you need the exact original string, query the .keyword subfield.
Date ranges accept date math: "gte": "now-7d/d" means seven days ago rounded down to the start of the day, and "lt": "now/d" is the start of today. Rounding makes requests cacheable. Add "format" or "time_zone" when inputs aren't ISO UTC.
Exact filters don't need scoring. Put them in the filter clause of a bool query (next lesson) so Elasticsearch skips scoring and can cache them, which is both faster and more correct than letting a filter affect relevance.
Example
GET /orders/_search
{
"query": {
"bool": {
"filter": [
{ "terms": { "status": ["paid", "shipped"] } },
{ "range": { "total": { "gte": 20, "lt": 100 } } },
{ "range": { "placed_at": { "gte": "now-7d/d", "lt": "now/d" } } },
{ "exists": { "field": "notes" } }
]
}
}
}
# Gotcha: term on an analyzed text field
GET /orders/_search
{ "query": { "term": { "customer": "Ana Petrosyan" } } } # 0 hits
GET /orders/_search
{ "query": { "term": { "customer.raw": "Ana Petrosyan" } } } # 1 hitRemember
- term/terms/range/exists match exact values without analysis
- Use them on keyword, numeric, date and boolean fields
- term on a text field usually finds nothing; use match or .keyword
- Date math like now-7d/d with rounding is cache friendly
Watch out for
- Querying a text field with term and getting zero results.