The Elasticsearch term query matches documents whose field contains the exact, unanalyzed value supplied. The query input is not run through any analyzer - it is compared byte-for-byte against the inverted index. It is the right tool for keyword, numeric, date, IP, and boolean fields. Using term against an analyzed text field is the single most common Elasticsearch query bug, because the indexed tokens rarely equal the raw input.
Syntax
{
"query": {
"term": {
"<field>": {
"value": "<exact_value>",
"case_insensitive": false,
"boost": 1.0
}
}
}
}
Shorthand: { "term": { "field": "value" } }.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
value |
any | - | Exact value to match. Required. |
case_insensitive |
bool | false |
If true, lowercases both sides before comparing (7.10+). |
boost |
float | 1.0 |
Score multiplier when used in query context. |
Examples
Exact match on a keyword field:
POST my-index/_search
{
"query": {
"term": { "status": { "value": "active" } }
}
}
Filter context - cacheable, no scoring:
GET /events/_search
{
"query": {
"bool": {
"filter": [ { "term": { "tenant_id": "acme" } } ]
}
}
}
Case-insensitive match (7.10+):
GET /users/_search
{
"query": {
"term": {
"email": { "value": "alice@example.com", "case_insensitive": true }
}
}
}
Multi-value lookup - use terms instead:
GET /products/_search
{
"query": {
"terms": { "sku": ["sku-001", "sku-002", "sku-003"] }
}
}
Performance and Use Notes
term is one of the cheapest queries in Elasticsearch. In filter context it produces a cacheable bitset reused across requests. Move every non-scoring exact-match clause into bool.filter. In query context it returns the BM25 score, which is rarely meaningful for keyword fields.
The distinction from match is purely about analysis. match runs the input through the field's analyzer; term does not. On a text field analyzed with standard, the indexed token for "Hello World" is [hello, world]. A term query for "Hello World" will not match, because the token "Hello World" does not exist. A match query against the same field would match. This trips up almost every team once.
Many production Elasticsearch slow-query reports trace to term queries against analyzed fields, returning empty results or unexpectedly wide ones depending on how the field is mapped. Pulse cross-references query patterns with field mappings on your Elasticsearch cluster and flags term/match mismatches, which usually means a field needs a keyword subfield or a query should be rewritten.
Common Mistakes
- Running
termagainst atextfield expecting it to match the literal string. Either query the.keywordsubfield or switch tomatch. - Forgetting that values are case-sensitive by default - "Active" does not equal "active" without
case_insensitive: trueor a lowercase normalizer on the field. - Putting
terminmust(query context) when no score is needed; usefilterfor cacheable, faster execution. - Using
termfor many values one at a time inside abool.should; usetermsquery (one clause, internal optimization). - Querying numeric fields as strings -
{ "term": { "price": "10" } }works due to leniency, but mixed types can mask data issues.
Frequently Asked Questions
Q: What is the difference between term query and match query in Elasticsearch?
A: The term query does not analyze the input and looks for an exact value in the inverted index. The match query runs the input through the field's analyzer first. Use term for keyword, numeric, date, and boolean fields; use match for full-text on text fields.
Q: Is the Elasticsearch term query case-sensitive?
A: By default, yes. Pass case_insensitive: true (Elasticsearch 7.10+) or define a normalizer on the field that lowercases values at index time. Note that case_insensitive only works on keyword and constant_keyword field types.
Q: Why does my term query return zero results on a text field?
A: Because text fields are analyzed and the raw input typically does not equal any indexed token. Either query the .keyword subfield (e.g. field.keyword), or use the match query, which will analyze the input.
Q: When should I use term query in filter context vs query context?
A: Always use filter context unless you need BM25 scoring from the clause - which is rare for exact-value matches. Filter context skips scoring and caches the resulting bitset across requests.
Q: How do I match multiple exact values in one query?
A: Use the terms query with an array: { "terms": { "status": ["active", "pending"] } }. It is optimized for multi-value lookup and produces a single cacheable bitset.
Q: Can the term query match on nested or object fields?
A: Object subfields work with dotted paths (user.id). True nested fields require a nested query wrapper to scope the term match to a single nested document.
Related Reading
- Elasticsearch Match Query: analyzed full-text counterpart to term.
- Elasticsearch Wildcard Query: exact-value query with wildcard characters.
- Elasticsearch Prefix Query: prefix-only exact match.
- Elasticsearch Bool Query: wrap term clauses in filter context.
- Elasticsearch IDs Query: exact match on the
_idmetadata field.