This is the most asked Elasticsearch question. Get it wrong and the interviewer immediately knows we’ve never actually used ES in production.
In simple language: match runs our search text through the same analyzer that indexed the field, then looks for the resulting tokens. term skips the analyzer entirely and searches for the exact value as-is.
That single difference explains 90% of the “why isn’t my query returning results?” bugs we see.
The flow — what actually happens
When we index a document with "title": "MacBook Pro 16-inch", the standard analyzer lowercases and tokenizes it into ["macbook", "pro", "16", "inch"]. Those tokens go into the inverted index.
Now if we search:
match: "MacBook"→ analyzer turns it into["macbook"]→ matches the token → hit.term: "MacBook"→ looks for the literal stringMacBookin the index → no match (because the index only hasmacbook).term: "macbook"→ matches the token → hit.
When to use which
Think of it like this — use match for human-typed search (search bars, autocomplete) and term for structured/exact data (status flags, IDs, tags, enums).
GET /products/_search
{
"query": {
"match": { "title": "wireless bluetooth headphones" }
}
}
The above is perfect for a search bar. ES will tokenize, lowercase, and find docs containing any of those words (with relevance scoring).
GET /orders/_search
{
"query": {
"term": { "status": "shipped" }
}
}
This is perfect for filtering by a known enum value. We know status is always one of pending | shipped | delivered, so we don’t want fuzzy matching.
The keyword field trick
Here’s where most people get burned. By default, a string field gets mapped as both text (analyzed) AND keyword (not analyzed). To do an exact match on a name field:
GET /users/_search
{
"query": {
"term": { "name.keyword": "Manish Prajapati" }
}
}
Notice the .keyword suffix. Without it, term on a text field will almost never work because the original string isn’t in the index — only its tokens are.
Scoring difference
match queries are run in query context — they compute a relevance score (BM25). term queries can run in filter context (inside bool.filter) where they’re cached and don’t compute scores. That makes term filters dramatically faster for repeated queries.
GET /products/_search
{
"query": {
"bool": {
"must": [{ "match": { "title": "laptop" } }],
"filter": [{ "term": { "in_stock": true } }]
}
}
}
Quick rules of thumb
- Searching free text from a user?
match. - Filtering by status, ID, boolean, tag, enum?
term. - Got results that don’t make sense? Check if the field is
textorkeyword— that’s almost always the culprit. - Want exact-match on a string? Use
termonfield.keyword.