Match vs. Term Query: the Difference That Changes Results
AI generated
_doc
_index
Elasticsearch · OpenSearch · Query DSL
Match vs. Term Query
the difference that changes results

Match query and term query look interchangeable at first glance, but they work fundamentally differently. The match query analyzes text before comparing it, the term query compares exactly and without any filtering. Anyone who ignores this difference gets empty result lists on text fields and wrongly permissive hits on keyword fields, without understanding why.

16 min read Match Query · Term Query · Analyzer · Keyword Field Elasticsearch 8.x · OpenSearch 2.x

1. Two queries, two fundamentally different worlds

The Match Query and the Term Query are among the first two queries every Elasticsearch beginner learns, and that is exactly why they cause the most beginner mistakes. Both apparently take the same basic pattern: a field name and a search value. But what Elasticsearch does with that search value internally differs fundamentally. The match query runs the search text through the same analyzer used at index time, the term query compares the search value exactly and byte-for-byte against the stored value, with no preprocessing at all.

This difference is not an academic nuance, it is the cause of one of the most common support requests in any search integration: "The search finds nothing, even though the term is clearly in the document." In almost every case, the cause is that a term query was used on a text field that was broken into lowercase tokens at index time, while the term query searches for the unchanged original value.

This article clears up exactly this distinction: how the match query analyzes, how the term query compares exactly, why keyword fields and text fields serve different use cases, and how multi-fields make both worlds available at once without compromise.

2. Match Query: understanding analyzed search

The Match Query is the default query for full-text search in Elasticsearch. When a match query runs against a text field, the search term passes through the same analyzer that was used when that field was indexed. A standard analyzer lowercases the text, strips punctuation, and splits it into individual tokens. If a user searches for "Running Shoes Nike", it internally becomes the token list ["running", "shoes", "nike"], which is compared against the tokens produced at index time.

This analysis makes the match query robust against differences in case, word order, and, depending on the analyzer configuration, even word forms through stemming. By default, a match query with multiple tokens behaves like a should combination: at least one token has to match, with more matches leading to a higher score. Through the operator parameter, this behavior can be switched to AND, requiring every token to be present.

An important aspect of the match query is that it only works on fields that were actually analyzed and stored in tokenized form, meaning fields of type text. On a keyword field, which stores the original value unchanged as a single token, the match query technically still returns a result, but it effectively behaves like an exact search, because no tokenizing takes place.


GET /products/_search
{
  "query": {
    "match": {
      "title": {
        "query": "Running Shoes NIKE",
        "operator": "and"
      }
    }
  }
}
// The search string is lowercased and tokenized before comparison
// Matches "nike running shoes", "Running Shoes by Nike", etc.

3. Term Query: the exact, unfiltered comparison

The Term Query compares the given search value exactly against the term stored in the inverted index, with no analysis at all. That means: case must match exactly, special characters are preserved, and no tokenization occurs. A term query with the value "Nike" does not find a document with the stored value "nike", because the two strings differ byte-for-byte.

This behavior makes the term query the right tool for structured data where exactness is desired: status codes, IDs, SKUs, raw email addresses, or enum-like values such as colors or sizes, provided they were indexed as a keyword field. A term query on {"status": "active"} only returns documents whose status field contains exactly "active", not "Active", not "ACTIVE", no partial match.

A frequently overlooked aspect of the term query: it typically runs in filter context when used inside the filter clause of a Bool Query, and is then both cheap to compute and cacheable. Outside a filter context, the term query still produces a score, but for exact matches that score is usually not very meaningful, since there is no graduated similarity, only a match or no match.


GET /products/_search
{
  "query": {
    "term": {
      "status.keyword": "active"
    }
  }
}
// Exact, unanalyzed comparison
// "Active" or "ACTIVE" will NOT match this query

4. What the analyzer actually does at index time

To really understand match query and term query, you need to know what happens when a text field is indexed. An analyzer consists of three stages: a character filter that preprocesses the raw text, a tokenizer that splits the text into individual tokens, and a chain of token filters that further transform these tokens, for example by lowercasing them or removing stopwords. The Elasticsearch standard analyzer uses the standard tokenizer and the lowercase filter, plus optionally a stop filter.

The result of this analysis, not the original text, ends up in the inverted index. For a document with the title "Waterproof Running Shoes", Elasticsearch stores the tokens ["waterproof", "running", "shoes"] in the inverted index of the text field. These exact tokens are what a match query compares against after analyzing its own search term, and that is precisely why the match query works reliably while a term query with the original value "Waterproof Running Shoes" comes up empty.

The _analyze API lets you see exactly which tokens an analyzer produces for a given text. This debugging tool is the first stop for any discrepancy between expected and actual search results, because it makes the analysis black box visible.


GET /products/_analyze
{
  "analyzer": "standard",
  "text": "Waterproof Running Shoes, Size 42!"
}
// Response tokens: ["waterproof", "running", "shoes", "size", "42"]
// Punctuation removed, everything lowercased, split into tokens

5. The classic mistake: keyword vs. text field

By far the most common mistake in the interplay of match query and term query is using a term query on a text field. Because a text field is analyzed and stored as lowercase tokens, a term query with the original value practically never succeeds, unless the search value happens to already be fully lowercase and consist of exactly one token. Conversely, the second common mistake is using a match query on a keyword field expecting a substring search to work, which it does not, because a keyword field is not tokenized.

This confusion arises because both query types look syntactically similar and Elasticsearch does not throw an error when a term query is applied to a text field, it simply, quietly returns empty results. That silence is what makes the mistake so treacherous: there is no exception, no warning, just an unexpectedly empty result list, which often leads developers to look at data quality or the indexing process first, instead of the query type itself.

The rule of thumb is: for free-text input from users, where typing variations, case, and word order should be tolerated, the match query is right. For structured, controlled values such as IDs, status codes, category names from a fixed list, or SKUs, the term query on a keyword field is right. Applying this rule consistently avoids most of the confusion between match query and term query.

6. Multi-fields: using both worlds at once

Elasticsearch elegantly resolves the apparent conflict between match query and term query through multi-fields: the same data field is indexed simultaneously as a text field for the match query and as a keyword sub-field for the term query. The mapping defines a fields object inside the field definition for this, typically named keyword. Access to the sub-field then happens through dot notation, such as title.keyword.

This structure allows one and the same product field to be used both for full-text search with the match query and for exact filtering, sorting, and aggregating with the term query, without storing data twice or maintaining a second field manually. Sorting by a text field is not directly possible anyway, because sorting always has to happen against the exact, unfiltered value, which is why the keyword sub-field is essential for sorting as well.

In practice, the multi-field structure is the default case for nearly every textual product attribute in an e-commerce search: the product title is searched via match query, the same information is used via term query on title.keyword for exact duplicate checks or aggregations.


PUT /products
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }
  }
}
// title         -> full-text search with match query
// title.keyword -> exact match, sorting, aggregations with term query

7. When Match, when Term: decision criteria

The decision between match query and term query can be made using a few criteria. First: if the search value comes directly from a user's input in a search field, the match query is almost always right, because it offers tolerance for spelling variations. Second: if the value comes from a closed set, a dropdown, an internal system, or an ID, the term query is right, because exactness is desired here and tolerance would even be unwanted.

Third: if the result is meant to feed relevance ranking, that points to a match query in scoring context. If the result is only meant to narrow the hit list, that points to a term query in filter context, ideally inside the filter clause of a Bool Query for maximum performance. This combination, match query for the search text in must and term query for structured criteria in filter, is the most commonly correct structure in practice.

Criterion Match Query Term Query
Field type text, analyzed keyword, unchanged
Case sensitivity Ignored Must match exactly
Word order Flexible Entire value exact
Typical use User search text, full-text search IDs, status, categories, SKUs
Sortable Not directly Yes

8. terms Query and variants at a glance

Alongside the simple term query, there is the terms query, the plural, which checks against a list of values and matches a document as soon as at least one of the values matches exactly. It is the equivalent of a SQL IN comparison and considerably more efficient than combining several term queries in a should clause, because Elasticsearch can evaluate the list internally in an optimized way. For facet filters where a user selects multiple categories or brands at once, the terms query is the right tool.

On the match side there is the analogous multi_match query, which runs the same analyzed search term against several text fields at once, such as title and description, with individual weighting per field. Both variants, terms and multi_match, follow the same base principle as their underlying queries, but each extends it with the ability to check several values or several fields in a single call.


GET /products/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "running shoes waterproof",
            "fields": ["title^2", "description"]
          }
        }
      ],
      "filter": [
        { "terms": { "brand.keyword": ["nike", "adidas", "puma"] } }
      ]
    }
  }
}
// multi_match runs the analyzed query against title and description
// terms checks brand.keyword against a list of exact values

9. Troubleshooting: why a query returns no hits

When a term query or a match query unexpectedly returns no hits, the _analyze API is the first debugging step to check which tokens actually end up in the index. The second step is a look at the mapping with the _mapping API, to confirm whether the field used is actually of the expected type, text or keyword. A very common pattern: a field is named "category", but what is actually meant is the multi-field category.keyword, and the query was accidentally written against the wrong sub-field.

A third debugging step is the explain option in the search API, which breaks down in detail for every document how the score came about or why a document does not appear in the result list. Combined with the _analyze API and a look at the mapping, almost all discrepancies between match query and term query can be clarified within minutes, instead of spending hours looking in the wrong place for the bug.

Mironsoft

Elasticsearch and OpenSearch search solutions for demanding data volumes

Empty search results despite the data being there?

We review your search's mapping, analyzer, and query structure and fix discrepancies between match query and term query that lead to empty or incomplete result lists.

Mapping review

Structure text and keyword fields correctly, add multi-fields

Query debugging

Inspect analyzer output and find misplaced term queries

Search optimization

Set up multi_match and terms queries cleanly for your catalog

10. Summary

The Match Query analyzes search text before comparing it and is the right choice for free-form user input on text fields. The Term Query compares exactly, without analysis, and is the right choice for structured values on keyword fields. The most common mistake occurs when a term query is used on an analyzed text field and quietly returns empty results instead of throwing an error.

Multi-fields resolve this conflict structurally, by indexing the same data field simultaneously as text and as a keyword sub-field. In practice, a good search query combines both query types: match query for the search text in scoring context, term query for structured filter criteria in the filter context of a Bool Query. The _analyze API remains the most important tool for quickly understanding what the analyzer actually produced when results are unexpected.

Match Query vs. Term Query, the essentials at a glance

Match Query

Analyzes the search text before comparing. Right for free-form user input on text fields.

Term Query

Compares exactly, without analysis. Right for structured values on keyword fields.

Multi-Fields

Index one data field as text and keyword at once, reachable through field.keyword.

Debugging

The _analyze API shows which tokens an analyzer produces. The first stop for empty result lists.

11. FAQ: Match vs. Term Query

1Fundamental difference between Match and Term?
Match analyzes text before comparing, Term compares exactly with no preprocessing.
2Why does my term query return no hits?
It likely runs on a text field instead of the matching keyword field.
3What is a keyword field?
Stores the value unchanged as one token, ideal for exact comparisons and sorting.
4Match Query on a keyword field possible?
Technically yes, but without tokenization it behaves effectively like an exact search.
5What are multi-fields?
The same field indexed as text and keyword at once, reachable through field.keyword.
6How do I find analyzer tokens?
With the _analyze API, the most important debugging tool for unexpected results.
7When to use terms instead of term?
For lists of values, such as facet filters with several brands, more efficient than several term clauses.
8Is Term Query faster than Match Query?
Generally yes, because no analysis is needed and usually no scoring is computed.
9Why doesn't Elasticsearch sort by text fields?
Because text fields are stored split into tokens, there is no unambiguous sort value, keyword is needed.
10Does this work identically in OpenSearch?
Yes, query types and the analyzer concept were adopted unchanged.