Exact values: term, terms and range

Elasticsearch · Essentials · 9 min

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 with gt, gte, lt, lte,
  • exists: the field has an indexed value; prefix, wildcard and ids also 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 hit

Remember

  • 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.

Check yourself

  1. A status field is mapped as text. Why might { "term": { "status": "Shipped" } } return nothing?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.elastic.co/guide/en/elasticsearch/reference/current/term-level-queries.html