The Elasticsearch function_score query wraps another query and modifies each matching document's _score using one or more scoring functions. It works on any indexed field referenced by a function, and is the standard tool for boosting by recency, popularity, geo-distance, or custom logic without rewriting the inner query. Use it when sorting by _score no longer reflects business relevance.
Syntax
{
"query": {
"function_score": {
"query": { "match_all": {} },
"functions": [ /* one or more function clauses */ ],
"score_mode": "multiply",
"boost_mode": "multiply",
"max_boost": 3.4028235e38,
"min_score": 0.0,
"boost": 1.0
}
}
}
If a single function is used, the functions array can be omitted and the function placed directly under function_score.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
query |
query object | match_all |
Inner query that produces the base _score. |
functions |
array | - | One or more function clauses, each optionally with a filter. |
score_mode |
string | multiply |
How function scores combine: multiply, sum, avg, first, max, min. |
boost_mode |
string | multiply |
How combined function score combines with query _score: multiply, sum, avg, replace, max, min. |
max_boost |
float | FLT_MAX |
Caps the final function-score contribution. |
min_score |
float | - | Excludes documents below this final score. |
boost |
float | 1.0 |
Query-level boost on the wrapped query. |
The five available function types are: script_score, weight, random_score, field_value_factor, and the decay functions (linear, exp, gauss).
Examples
Boost by document popularity using field_value_factor:
{
"query": {
"function_score": {
"query": { "match": { "title": "elasticsearch" } },
"field_value_factor": {
"field": "popularity",
"factor": 1.2,
"modifier": "log1p",
"missing": 1
},
"boost_mode": "multiply"
}
}
}
Recency decay on a date field:
{
"query": {
"function_score": {
"query": { "match_all": {} },
"exp": {
"published_at": { "origin": "now", "scale": "30d", "decay": 0.5 }
}
}
}
}
Combine multiple functions with per-function filters:
{
"query": {
"function_score": {
"query": { "match": { "title": "phone" } },
"functions": [
{ "filter": { "term": { "in_stock": true } }, "weight": 2 },
{ "field_value_factor": { "field": "rating", "modifier": "sqrt", "missing": 1 } },
{ "gauss": { "price": { "origin": "500", "scale": "100" } } }
],
"score_mode": "sum",
"boost_mode": "multiply",
"min_score": 0.1
}
}
}
Reproducible random scoring (good for A/B sampling):
{
"query": {
"function_score": {
"random_score": { "seed": 42, "field": "_seq_no" }
}
}
}
Performance and Use Notes
Each function executes per matching document, so function_score scales with the size of the result set produced by the inner query. Filter aggressively before applying expensive functions: narrow the inner query with bool.filter clauses, then let function_score rerank what survives. Decay and field_value_factor use doc values and are cheap; script_score is by far the most expensive option and triggers script compilation if params is not used to keep the source stable.
Poorly tuned scoring functions often manifest as slow shard queries and rising CPU. The manual loop - profile the slow log, identify which function_score clause is dominating, check each function's filter scope, decide which script_score could be replaced by a native function - is exactly what Pulse automates.
Common Mistakes
- Using
boost_mode: replaceand then wondering why the inner query's relevance signal disappeared. - Applying
field_value_factorto a field without doc values (e.g.textwithoutkeywordsubfield), causing failures or high heap. - Forgetting
missingonfield_value_factor; documents lacking the field throw an error by default. - Stacking many functions with
score_mode: sumand producing scores dominated by one feature. - Using
random_scorewithout aseed, producing non-reproducible result orders between requests.
Find Slow function_score Queries with Pulse
Pulse is an AI DBA for Elasticsearch and OpenSearch that continuously profiles production query traffic. For function_score queries specifically, Pulse:
- Identifies
function_scorequeries whose inner clause matches too many documents, so each function runs per-doc on an inflated set - Flags
field_value_factorclauses on fields without doc values (often atextfield missing a.keywordsub-field) andfield_value_factorwithoutmissing, which throws on documents lacking the field - Detects nested
script_scorefunctions that are not parameterized, so per-requestsourcestrings thrash the script compilation cache - Traces each slow
function_scoreback to the calling service via slow-log and APM correlation - Recommends concrete rewrites: pre-filter the inner query with
bool.filter, replacescript_scorefunctions withfield_value_factoror decay (linear/exp/gauss) where possible, parameterize remaining scripts, switchboost_modeaway fromreplaceif the inner relevance signal still matters, and cap withmax_boostormin_score - Tracks latency improvement after the change
This converts the manual scoring-debug loop into a continuous optimization workflow.
Frequently Asked Questions
Q: What is the difference between score_mode and boost_mode in function_score?
A: score_mode controls how multiple function clauses combine into a single function score. boost_mode controls how that combined function score then combines with the inner query's _score. Both default to multiply.
Q: What are the five function types available in Elasticsearch function_score?
A: The function_score query supports script_score, weight, random_score, field_value_factor, and decay functions (linear, exp, gauss). Decay functions are typically used for numeric, date, or geo fields.
Q: When should I use function_score vs script_score query?
A: Use function_score when standard functions (decay, popularity boost, weighted filters) cover the requirement. Use the standalone script_score query when scoring is pure custom math and you do not need to combine multiple functions or per-function filters.
Q: Does function_score work with filter context?
A: No. The function_score query runs in query context because it computes scores. Filters that do not affect scoring should live in a bool.filter inside the wrapped query.
Q: How do I debug a function_score query in Elasticsearch?
A: Add "explain": true to the search request or call the _explain API for a specific document. Elasticsearch returns a tree showing each function's contribution and how score_mode / boost_mode combined them.
Q: Why is my function_score query slow?
A: Most slow function_score queries either match too many documents in the inner query or use a script_score function whose source changes per request. Narrow the inner query with filters, parameterize scripts so they cache, and prefer decay or field_value_factor over scripts when possible.
Q: What is the best tool to find slow function_score queries in production?
A: Pulse profiles Elasticsearch and OpenSearch slow logs, isolates function_score queries with bloated inner clauses or non-parameterized nested scripts, attributes each to the calling service, and recommends pre-filtering, native-function replacements for script_score, and proper missing handling on field_value_factor.
Related Reading
- Elasticsearch Script Score Query: standalone custom-scoring query.
- Elasticsearch Script Query: script-based filter (not scoring).
- Elasticsearch Constant Score Query: assign a fixed score to a filter.
- Elasticsearch Bool Query: combine queries with must/should/filter.
- Elasticsearch Match Query: full-text query commonly wrapped by function_score.