for targeted boosting
Pure BM25 scoring only knows text relevance, no business goals. The Function Score Query closes this gap by combining the textual score with additional factors such as sales figures, stock level, or freshness. Anyone who masters field_value_factor and decay functions can steer search results deliberately by business logic without sacrificing textual relevance.
Table of Contents
- 1. Why pure BM25 scoring is not enough
- 2. Basic structure of the Function Score Query
- 3. field_value_factor: bringing numeric fields in directly
- 4. Decay functions: gauss, exp, and linear
- 5. Configuring boost_mode and score_mode correctly
- 6. Practical example: boosting by popularity
- 7. Practical example: combining stock and freshness
- 8. random_score for A/B tests and variation
- 9. Pitfalls when using the Function Score Query
- 10. Summary
- 11. FAQ
1. Why pure BM25 scoring is not enough
BM25 computes relevance purely from textual characteristics: how often a search term appears, how rare it is across the whole index, and how long the searched field is. For pure text search, that is sufficient, but in e-commerce and many other applications, additional factors matter that BM25 fundamentally does not know about: sales figures, stock level, customer rating, freshness, or margin. This is exactly where the Function Score Query comes in, extending the pure text score with additional, freely definable functions.
The Function Score Query solves a problem every experienced search developer knows: a product with a perfect text match, but out of stock for months, should in most cases not rank above an available, almost equally matching product. Without additional boosting, however, BM25 would do exactly that, because availability plays no role for the pure text algorithm.
The Function Score Query is therefore one of the most important tools for turning a purely textually relevant search into a search that makes business sense. It combines a base query, usually a Bool Query with match and filter clauses, with a list of functions that modify, amplify, or dampen the original score.
2. Basic structure of the Function Score Query
Structurally, a Function Score Query consists of two main parts: the query, which delivers the textual base score, and the functions array, which holds a list of boosting functions. Each function in this array can optionally be bound to its own filter, so that it only applies to documents that satisfy that filter. Without its own filter, a function affects every document in the result set.
The final score of a document is produced in two steps: first, the results of all functions are combined into a single function score through the score_mode parameter, for example by summing, multiplying, or averaging. Then this function score is combined with the original query score through the boost_mode parameter, again selectable between multiplication, sum, maximum, and other options. This two-stage system gives full control over how strongly business factors are allowed to influence textual relevance.
A common use case for the Function Score Query is to let the base query be a normal Bool Query with match and filter clauses, while the functions are used exclusively for fine boosting. This separation between "what is relevant" and "what should be additionally weighted" keeps the configuration understandable, even when several functions are active at once.
GET /products/_search
{
"query": {
"function_score": {
"query": {
"match": { "title": "running shoes" }
},
"functions": [
{
"field_value_factor": {
"field": "sales_count",
"modifier": "log1p",
"factor": 0.5
}
}
],
"boost_mode": "sum",
"score_mode": "sum"
}
}
}
// The BM25 text score is added to a boost derived from sales_count
// modifier log1p prevents runaway scores from very high sales numbers
3. field_value_factor: bringing numeric fields in directly
The field_value_factor function is the most direct way to bring a numeric field into the score. It reads the value of a field, such as sales figures or customer rating, optionally multiplies it by a factor, and optionally applies a modifier before the result flows into the overall score. Without a modifier, a field value of 10000 would skew the score by a factor of 10000, which is practically never desired.
The log1p modifier calculates the natural logarithm of 1 + field value and thereby dampens large values much more than small ones, which is almost always the right choice for sales figures or click counts: the difference between 10 and 100 sales should be noticeable, the difference between 10000 and 10090 sales practically not. Other available modifiers include sqrt for milder dampening and log2p for stronger dampening.
Important with field_value_factor is handling missing or null-valued fields: by default, Elasticsearch throws an error when the referenced field is missing on a document. The missing parameter defines a fallback value for this case and should always be set in production configurations to avoid runtime errors on incomplete data.
GET /products/_search
{
"query": {
"function_score": {
"query": { "match_all": {} },
"functions": [
{
"field_value_factor": {
"field": "rating",
"factor": 1.2,
"modifier": "sqrt",
"missing": 1
}
}
],
"boost_mode": "multiply"
}
}
}
// missing: 1 provides a fallback for documents without a rating field
// sqrt dampens the impact of ratings less aggressively than log1p
4. Decay functions: gauss, exp, and linear
While field_value_factor rises monotonically with the field value, there are use cases where relevance should decrease with increasing distance from a reference point, instead of rising linearly. For this, the Function Score Query offers three decay functions: gauss, exp, and linear. All three take an origin value, a scale value, and optionally a decay value, and calculate from these how much the score decreases with increasing distance from the reference point.
The classic use case for decay functions is freshness: a newly published article or a newly arrived product should get a relevance bonus that decreases with increasing age, but does not drop abruptly to zero. With gauss on the field published_date, origin set to the current date, and scale set to, say, 30 days, articles from the last few weeks get a gently fading bonus, while very old articles barely benefit anymore.
The difference between the three decay functions lies in the shape of the decay curve: gauss falls slowly at first and then faster, exp falls exponentially from the start, linear falls evenly down to zero at twice the scale distance. For geographic proximity, for example boosting stores or warehouse locations near the user's position, decay functions are likewise the standard tool, applied to a geo_point field instead of a date field.
GET /articles/_search
{
"query": {
"function_score": {
"query": { "match": { "content": "elasticsearch update" } },
"functions": [
{
"gauss": {
"published_date": {
"origin": "now",
"scale": "30d",
"decay": 0.5
}
}
}
],
"boost_mode": "multiply"
}
}
}
// Articles published 30 days ago retain 50% of the freshness boost
// the gaussian curve fades gently, older articles score progressively lower
5. Configuring boost_mode and score_mode correctly
The score_mode parameter determines how multiple function results within the Function Score Query are combined into a single function score. Options include multiply (default), sum, avg, max, min, and first. With several functions each bound to their own filter, first is particularly useful because only the result of the first matching function is used, which suits mutually exclusive categories.
The boost_mode parameter determines how this combined function score is merged with the original query score. multiply is the default and works well when the function score should act as a relative amplifier. sum is suitable when text relevance and business factors should flow in additively on equal footing. replace ignores the query score entirely and uses only the function score, which can make sense for pure sorting use cases without text search.
A common configuration mistake is choosing multiply as the boost_mode without normalizing the function values beforehand. A function score of 0 multiplies the overall score to zero and effectively removes the document from meaningful ranking positions, even when text relevance was high. The min_score parameter at the function level or a carefully chosen missing value reliably prevents this problem.
6. Practical example: boosting by popularity
A typical e-commerce scenario combines text relevance with sales figures and customer rating in a single Function Score Query. The base query searches for the product title, while two field_value_factor functions bring in sales figures and rating. Both function values are added through score_mode: "sum" and then merged with the text score through boost_mode: "multiply", so that a product with no text match at all does not appear in results despite high popularity.
This structure ensures that popularity influences the ordering within the textually relevant results, but never pulls a fully irrelevant product into the hit list. This is exactly what distinguishes the Function Score Query from a plain sort by popularity: text relevance remains the entry barrier, boosting only acts within the already relevant hits.
| Function | Purpose | Typical field |
|---|---|---|
| field_value_factor | Weight numeric values directly | sales_count, rating, margin |
| gauss | Gently fading bonus around a point | published_date, geo_point |
| exp | Faster fading bonus | published_date, event_date |
| linear | Evenly fading bonus | price, distance |
| random_score | Controlled random variation | A/B tests, result diversity |
7. Practical example: combining stock and freshness
A second practical example combines three functions: a filter-bound function that penalizes out-of-stock products with a fixed low weight instead of excluding them entirely, a field_value_factor function for sales figures, and a gauss decay function for how recently the product listing was updated. This combination allows out-of-stock products to remain visible, for example for interested users who can set up a back-in-stock notification, without placing them among the top results.
The advantage of handling out-of-stock products with a low weight instead of a hard filter lies in flexibility: should it turn out that users are still interested in these products, the weight can be adjusted without changing the filter logic entirely. This flexibility is one of the biggest practical advantages of the Function Score Query over rigid filter rules in the Bool Query.
GET /products/_search
{
"query": {
"function_score": {
"query": { "match": { "title": "running shoes" } },
"functions": [
{
"filter": { "term": { "in_stock": false } },
"weight": 0.1
},
{
"field_value_factor": {
"field": "sales_count",
"modifier": "log1p",
"missing": 0
}
},
{
"gauss": {
"updated_at": { "origin": "now", "scale": "60d" }
}
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
// Out-of-stock products get a heavy 0.1 weight penalty, not full exclusion
// sales and freshness combine additively before multiplying the text score
8. random_score for A/B tests and variation
The random_score function adds a controlled but deterministic random component to the score, based on a seed value. Unlike true randomness, the same seed delivers the same order on every request, which matters for consistent pagination, while different seeds can let different users or sessions see different result orderings. This is especially useful for A/B testing ranking changes or for introducing artificial diversity into the hit list for very similarly scored products.
In practice, random_score is rarely used alone, but is usually added to an existing Function Score Query with a low weight, to produce a slight but reproducible variation when scores would otherwise be identical. Without this measure, products with identical scores would always appear in the same order, often determined by the document ID, which over time can lead to a perceived favoring of individual products.
Mironsoft
Elasticsearch and OpenSearch search solutions for demanding data volumes
Search results that ignore your business goals?
We configure Function Score Queries that bring popularity, stock level, and freshness deliberately into search relevance, without sacrificing textual precision.
Boosting strategy
Define field_value_factor and decay functions for your catalog
Configuration
Calibrate boost_mode and score_mode correctly for your business logic
A/B testing
Set up random_score and ranking variants for data-driven decisions
9. Pitfalls when using the Function Score Query
The most common pitfall is unnormalized boosting: a field with a very large value range, such as sales figures between 0 and 100000, dominates the entire score without a modifier and factor, and makes textual relevance practically irrelevant. The solution is consistent use of modifiers such as log1p and carefully chosen factor values, ideally tested against real data distribution, not guessed by feel.
A second pitfall concerns performance: every additional function in a Function Score Query, especially decay functions on large data sets, costs extra compute time per document. On very large indices with many concurrent search requests, the number of functions should be kept deliberately small and regularly reviewed for actual business impact, instead of adding functions "just in case". A third pitfall is missing missing values in field_value_factor, which leads to runtime errors instead of meaningful fallback values on incomplete data.
10. Summary
The Function Score Query extends the pure BM25 text score with freely definable functions that bring in business signals such as popularity, stock level, or freshness. field_value_factor weights numeric fields directly, usually dampened by a modifier such as log1p. Decay functions like gauss, exp, and linear produce a bonus that fades with increasing distance from a reference point, classically for freshness or geographic proximity.
The score_mode and boost_mode parameters control how multiple functions are combined and merged with the original text score, with unnormalized boosting being the most common configuration mistake. Used correctly, the Function Score Query allows keeping text relevance as the entry barrier while business factors take over fine-grained ordering within the relevant hits, a pattern nearly every production e-commerce search uses in some form.
Function Score Query for boosting, the essentials at a glance
field_value_factor
Bring numeric fields in directly, dampen with log1p or sqrt, set a missing value.
Decay functions
gauss, exp, and linear for gently fading bonuses, classic for freshness and geographic proximity.
boost_mode and score_mode
Control how functions are combined and merged with the text score. Calibrate carefully.
Most common mistake
Unnormalized boosting dominates text relevance. Always work with modifiers and tested factors.