The Elasticsearch constant_score query wraps a filter clause and assigns every matching document the same score, equal to the boost parameter (default 1.0). It is the canonical way to use a filter inside a scoring query context without producing a meaningful relevance score. Use it when you need a filter to contribute a known, fixed contribution to a combined bool score, or to suppress BM25 scoring on a clause that should be a pure include/exclude check.
Syntax
{
"query": {
"constant_score": {
"filter": { /* any filter clause */ },
"boost": 1.0
}
}
}
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
filter |
query | - | Filter clause that selects matching documents. Required. |
boost |
float | 1.0 |
Constant score assigned to every matching document. |
filter accepts any query clause. The wrapped clause runs in filter context: no scoring, results cacheable as a bitset.
Examples
Assign a constant score to documents marked as featured:
GET /products/_search
{
"query": {
"constant_score": {
"filter": { "term": { "featured": true } },
"boost": 1.5
}
}
}
Combine with full-text scoring inside a bool - the constant_score clause adds a flat boost to recent items on top of the BM25 score:
GET /articles/_search
{
"query": {
"bool": {
"should": [
{ "match": { "body": "kafka tuning" } },
{ "constant_score": {
"filter": { "range": { "published_at": { "gte": "now-7d" } } },
"boost": 2.0
}}
]
}
}
}
Wrap a complex filter to keep score predictable:
GET /events/_search
{
"query": {
"constant_score": {
"filter": {
"bool": {
"must": [ { "term": { "type": "purchase" } } ],
"must_not": [ { "term": { "refunded": true } } ],
"filter": [ { "range": { "amount": { "gte": 100 } } } ]
}
}
}
}
}
Replace expensive scoring on a known-good filter with a fixed score:
GET /logs/_search
{
"query": {
"constant_score": {
"filter": { "term": { "tenant_id": "acme" } }
}
}
}
Performance and Use Notes
The wrapped filter runs in filter context regardless of where constant_score appears, which means its result bitset is cacheable in the node-level query cache. That is the primary performance benefit: a clause that would otherwise be in a bool.must (query context, scored, uncached) becomes cacheable.
For most "filter + no score" use cases, the more idiomatic form is bool.filter rather than constant_score. bool.filter produces a final score of 0.0 for the clause and is what the official Elasticsearch documentation recommends. Reach for constant_score specifically when you need the matching documents to contribute a non-zero, predictable amount to a combined score.
Filter caching strategy directly drives Elasticsearch cluster throughput. Pulse monitors filter-cache hit ratios, evictions, and cache memory usage on your Elasticsearch cluster, and surfaces queries where rewriting must into filter or constant_score would shift load off the scoring path.
Common Mistakes
- Using
constant_scoreinbool.filter- filter context already implies score 0, and wrapping withconstant_scoredoes not produce a non-zero score there. - Setting an aggressive
boost(e.g.100) and overpowering all other scored clauses in the parentbool. - Reaching for
constant_scorewhenbool.filteris the simpler, idiomatic choice. - Wrapping a match query and losing all relevance signal from the analyzed text - if you wanted relevance, do not use
constant_score. - Assuming
constant_scoredisables filter caching - it does not; the inner filter is still cacheable.
Frequently Asked Questions
Q: What is the difference between constant_score and bool filter in Elasticsearch?
A: bool.filter runs clauses in filter context and contributes 0 to the combined score. constant_score also runs the inner clause in filter context but contributes a fixed non-zero score equal to its boost. Use constant_score when the filter should add a known amount to the parent bool's score; use bool.filter when score is irrelevant.
Q: Does constant_score use the filter cache?
A: Yes. The wrapped filter executes in filter context, so its bitset is eligible for the node-level query cache. This is the main performance reason to prefer constant_score over running the same predicate in a must clause.
Q: How does the boost parameter work in a constant_score query?
A: Every document matching the inner filter receives a score equal to boost (default 1.0). The boost is the entire score - no BM25, no field-length normalization, no inverse document frequency.
Q: When should I prefer constant_score over wrapping a filter directly?
A: When the filter clause needs to contribute a non-zero score, typically inside a bool.should where it combines with full-text matches. If you do not need a score from the clause at all, bool.filter is more idiomatic.
Q: Can I use constant_score with full-text queries?
A: You can, but the relevance score from the full-text query is discarded - every match gets the same constant score. That defeats the purpose of full-text scoring, so reach for function_score if you need both relevance and custom scoring.
Q: What happens if I omit the filter parameter from constant_score?
A: The query is invalid and Elasticsearch returns a parse error. filter is required.
Related Reading
- Elasticsearch Bool Query: the standard wrapper that hosts filter context.
- Elasticsearch Term Query: the typical content of a constant_score filter.
- Elasticsearch Function Score Query: custom-scoring alternative when fixed boost is not enough.
- Elasticsearch Range Query: common range filter wrapped in constant_score.
- Elasticsearch Match Query: full-text query whose scoring would be lost inside constant_score.