The Elasticsearch prefix query matches documents that contain at least one indexed term starting with a given string. It runs against the term dictionary, so behavior depends on the field's analyzer: on keyword fields it matches the full value, on analyzed text fields it matches per-token prefixes. The query is classified as expensive by default but becomes cheap when the field has the index_prefixes mapping option enabled - in that case it bypasses the search.allow_expensive_queries restriction.
Syntax
GET /index/_search
{
"query": {
"prefix": {
"field_name": {
"value": "lap",
"case_insensitive": false,
"rewrite": "constant_score"
}
}
}
}
Parameters
| Parameter | Description | Required | Default |
|---|---|---|---|
value |
Prefix to search for. | Yes | - |
case_insensitive |
ASCII case-insensitive matching. Available since Elasticsearch 7.10. | No | false |
rewrite |
Multi-term rewrite method (constant_score, top_terms_N, etc.). |
No | constant_score |
A short-form syntax also exists: {"prefix": {"field_name": "lap"}} is equivalent to the longer form with default options.
Examples
Basic prefix match on a keyword field:
GET /products/_search
{
"query": {
"prefix": {
"product_name.keyword": {
"value": "lap"
}
}
}
}
Case-insensitive prefix:
GET /users/_search
{
"query": {
"prefix": {
"email": {
"value": "John.",
"case_insensitive": true
}
}
}
}
Use the field's index_prefixes for cheap autocomplete (define the mapping option at index creation):
PUT /products
{
"mappings": {
"properties": {
"title": {
"type": "text",
"index_prefixes": { "min_chars": 2, "max_chars": 5 }
}
}
}
}
GET /products/_search
{
"query": {
"prefix": {
"title": { "value": "wir" }
}
}
}
Combine with a filter to prune the candidate set first:
GET /products/_search
{
"query": {
"bool": {
"filter": [ { "term": { "category": "phones" } } ],
"must": [ { "prefix": { "model.keyword": "iph" } } ]
}
}
}
Performance and Use Notes
Prefix queries walk the slice of the term dictionary that starts with the given string. On a sorted dictionary that slice is found by binary search, then iterated. Cost grows with the number of matching terms, not with index size. For autocomplete on large indices, the index_prefixes mapping option pre-indexes prefixes between min_chars (default 2) and max_chars (default 5) at index time; queries within that length range execute as single term lookups.
Prefix queries are considered expensive by default and are rejected when search.allow_expensive_queries: false - unless the field uses index_prefixes, in which case they are exempt. On analyzed text fields, the prefix is matched against tokens after analysis; on keyword fields, against the unanalyzed value. Pick the right sub-field to match user expectations.
Slow autocomplete is a frequent driver of search latency complaints. The manual triage - identifying prefix queries running on fields without index_prefixes, then chasing each one back to the originating service - is the loop Pulse runs continuously.
Common Mistakes
- Running prefix queries on analyzed
textfields and getting per-token matches instead of starts-with-string semantics. Use the.keywordsub-field. - Implementing autocomplete with plain prefix queries on a multi-million-document index instead of
index_prefixes,search_as_you_type, or a completion suggester. - Forgetting that
prefixignorescase_insensitiveon field types without ASCII semantics (full Unicode lowercasing requires a normalizer or analyzer). - Allowing one-character prefixes from user input on large indices - the candidate set can include hundreds of thousands of terms.
- Expecting
search.allow_expensive_queries: falseto block prefix queries onindex_prefixesfields. It does not - those are exempt.
Optimize Slow Prefix Queries with Pulse
Pulse is an AI DBA for Elasticsearch and OpenSearch that continuously profiles production query traffic. For prefix queries specifically, Pulse:
- Identifies prefix queries running against fields without the
index_prefixesmapping option, where the term-dictionary slice scan dominates latency - Flags prefix queries rejected by
search.allow_expensive_queries: falsebecause the field is not exempt, plus the services still attempting them - Spots one-character prefixes from user input that are enumerating hundreds of thousands of terms on large indices
- Traces each slow prefix query back to the calling service via slow-log and APM correlation
- Recommends the right mapping change for the use case: add
index_prefixeswith appropriatemin_chars/max_chars, migrate tosearch_as_you_type, switch to a completion suggester for autocomplete, or use match_phrase_prefix for multi-word input - Tracks latency improvement after the mapping change ships
This converts the manual slow-log plus mapping-review loop into a continuous optimization workflow.
Frequently Asked Questions
Q: How is a prefix query different from a wildcard query?
A: A prefix query matches terms that start with a fixed string and is optimized by the sorted term dictionary. A wildcard query accepts * and ? anywhere, and a leading wildcard forces a full term scan. Use prefix whenever the pattern is "starts with X".
Q: What does the index_prefixes mapping option do?
A: It pre-indexes term prefixes between min_chars and max_chars (defaults 2 and 5) into a sidecar field. Prefix queries that fall in that length range turn into a single term lookup instead of a term-dictionary slice scan, and they are exempt from the search.allow_expensive_queries cap.
Q: How do I make a prefix query case-insensitive?
A: Set case_insensitive: true on the query (Elasticsearch 7.10+). For older clusters or non-ASCII data, index the field with a lowercase normalizer (for keyword) or a lowercasing analyzer (for text) and lowercase the input value.
Q: How is a prefix query different from match_phrase_prefix?
A: A prefix query matches a single term's prefix. `match_phrase_prefix` takes a multi-word phrase, requires the leading terms to match exactly, and uses prefix matching only on the last term. It is the standard query for "search as you type" multi-word input.
Q: Can I use a prefix query on numeric fields?
A: It works syntactically because numeric fields are converted to their string form, but the result is rarely useful. Use a range query for numeric, date, or IP fields.
Q: Are prefix queries cacheable?
A: Yes, in filter context. They enter the per-node query cache like other constant-score queries, so repeated identical prefixes are cheap after the first execution.
Q: What is the best tool to find slow prefix queries powering autocomplete?
A: Pulse profiles Elasticsearch and OpenSearch slow logs, identifies prefix queries running without index_prefixes on large fields, attributes each one to the calling service, and recommends the mapping change - index_prefixes, search_as_you_type, or a completion suggester - that fits the access pattern.
Related Reading
- Elasticsearch Query Language: overview of the query DSL.
- Wildcard Query: use when
*or?is needed anywhere in the pattern. - Regexp Query: full regex matching when a prefix is not enough.
- Terms Query: match a fixed list of exact values.
- Multi-Match Query: use
phrase_prefixtype for multi-word autocomplete.