Boosting e-commerce search by profit and popularity with the function score query in Elasticsearch

Discover how to optimize e-commerce search by blending BM25 relevance with profit margin and popularity signals in Elasticsearch using the function_score query.

Test Elastic's leading-edge, out-of-the-box capabilities. Dive into our sample notebooks, start a free cloud trial, or try Elastic on your local machine now.

Overview

In this article, you will learn how to combine BM25 relevance with real business metrics like profit margin and popularity using Elasticsearch’s function_score query. This step-by-step guide shows how to control scaling with logarithmic boosts and allows full explainability for each ranking calculation.

Introduction

In many use cases, search results focus on lexical (keyword) and semantic (meaning-based) analysis to find the content that most accurately and authoritatively answers a user’s query. However, e-commerce search is a bit more complex.

Results must reflect the shopper’s intent and incorporate business objectives such as profit margin, product popularity, or other factors that don’t always directly align with purely lexical or semantic matching.

While text relevance ensures customer satisfaction, ranking by profitability and popularity turns search into a business optimization engine.

In order to demonstrate how business signals can be incorporated into search results, in this post we’ll explore:

  1. How to boost product rankings by margin (profitability 0% to 200% in the demo data below).
  2. How to extend that same logic to include popularity (number of sales).

Once you understand how to boost by margin and popularity, extending search to incorporate other signals is straightforward.

Setup

Below is a small dataset you can paste directly into Dev Tools to follow along.

Each document represents a product with:

  • margin: profit margin (percent)
  • popularity: relative sales volume (e.g. weekly average, or last week’s sum)

Ranking without margin

We can see how baseline results look by executing a simple query for “McCain chips” that does not take into consideration margin, as follows:

Which returns the following results:

As you can see from the above results, the high margin version of the chips is 3rd in the results because the ordering does not consider margin.

Ranking by margin

Without any additional context, all sizes of “McCain chips” look equally relevant — but from a business perspective, it is possible that the higher-margin items should rank higher.

ProductMargin (%)Description
McCain Home Chips 500g – High Margin200%small pack
McCain Home Chips 1kg100%mid pack
McCain Home Chips 1.5kg50%family pack

We’ll use Elasticsearch’s function_score query to apply a margin-based boost.

The above query results in the following, which reflect the impact of the margin boosting on the score. Notice that, as we intended, the high-margin McCain Chips have been boosted to the 1st position in the results.

Understanding the formula

The function_score query allows us to apply a smooth, interpretable boost based on margin without overwhelming BM25’s lexical relevance.

Here’s how it works:

  • margin_boost = ln(1 + margin × factor)
  • boost = 1 + margin_boost
  • final_score = BM25 × boost

Where the query is specified with the following fields:

  • field_value_factor – uses a document field to influence scoring without scripting overhead.
  • modifier: “ln1p” – computes ln(1 + margin × factor)
    • Note: ln1p(x) is shorthand for ln(1 + x).
  • factor – controls scale; 0.0085 caps boosts near 2× at margin=200.
  • weight: 1 – ensures a minimum boost of 1 for neutral items.
  • score_mode: “sum” – adds constant 1 (from that standalone “weight” : 1) and the margin_boost together.
  • boost_mode: “multiply” – multiplies BM25 by the computed boost.

Why was that formula chosen?

The logarithmic (ln1p) scaling behaves well across real-world data:

  • It grows fast at small margins (rewarding incremental gains).
  • It flattens at high margins (preventing runaway scores).
  • It’s continuous and interpretable — no thresholds or discontinuities.
Marginln(1 + margin × 0.0085)Boost (≈1+ln1p)Boost Multiplier
50.0421.04×1.04
500.351.35×1.35
1000.631.63×1.63
2000.991.99×1.99

Ranking by margin and popularity

We can extend the same logic to add a popularity boost. Here, we tune the popularity factor so that the boost increases by roughly +1.0, at a popularity of 10,000. (These thresholds depend on your dataset’s scale.)

Which returns results with the most popular product in 1st place, even though it is not the highest margin, as follows — in this case, the impact of the popularity boost has pushed up McCain Home Chips 1.5kg to 1st place.

What the resulting boosts look like

The “factors” are tuned to add +1.0 to the boost at the assumed maximums. These are calculated to satisfy the following formulas:

Then:

Each cell in the table below represents the total BM25 multiplier for various margin and popularity values.

How to read the table:

  • The first column (popularity = 0) isolates the margin effect.
  • Moving right, popularity increases the boost — but since its weight is 0.5, its contribution to the summed boost is halved.
  • Even at extreme values (popularity = 100,000), the boost flattens due to logarithmic scaling.

Tuning

If you find popularity can spike very high (e.g., 100k+) and you don’t want boosts above some ceiling, you can:

  • Lower the popularity factor further, or
  • Add “max_boost”: <cap> to function_score, or
  • Split weights, e.g. “weight”: 0.25 on popularity and “weight”: 1 on margin (still with score_mode: “sum”), if you want one to dominate less.

Using rank_feature for similar use cases

At first glance, rank_feature and rank_features look like a natural choice for incorporating numeric signals such as popularity, recency, or even profit margin. They are fast, compressed, and easy to operationalize — which is why many teams reach for them first.

However, they are not a good fit for this type of scoring model, for the following reasons:

1. Rank-feature contributions are strictly additive

The score takes the form:

This means the effect of the boost changes dramatically depending on the scale of the BM25 score.

  • When BM25 is small, the boost dominates the ranking.
  • When BM25 is large, the identical boost becomes negligible.

We need consistent, proportional behavior instead.

2. Impossible to express “percentage-based” or multiplicative logic

This article’s model requires expressing things like:

  • “Popularity increases relevance by ~20%.”
  • “Margin strengthens relevance but never overrides it.”

rank_feature cannot do this. It does not support multiplicative shaping of the BM25 score.

3. Combining multiple signals becomes unstable and hard to tune

If you try to combine margin, popularity, availability, or other business metrics via rank_features, each feature adds another independent additive term. These interact in opaque ways, making tuning brittle and unpredictable.

4. Bottom line

rank_feature is great for simple additive numeric boosts. It is not suitable when you need:

  • stable behavior across queries
  • proportional / multiplicative effects
  • explainable blending of multiple signals

For this reason, the article uses function_score instead, because it provides explicit, controlled scoring that behaves consistently regardless of BM25 scale.

Wrapping up

Elastic’s function_score query makes it simple to transform search ranking from content relevance into business-aware optimization.

By combining BM25 relevance with economic signals like margin and popularity, you can:

  • Align search with real business outcomes.
  • Tune scaling via a single parameter (factor).
  • Maintain full explainability through _explain.

Once this foundation is in place, you can extend it to Stock levels (reduce the ranking of low-stock products), Recency (prioritize new products), or other business-critical signals that you want to take into consideration. Each new signal simply adds to the boost which is then multiplied by the base BM25 relevance score.

Related Content

Ready to build state of the art search experiences?

Sufficiently advanced search isn’t achieved with the efforts of one. Elasticsearch is powered by data scientists, ML ops, engineers, and many more who are just as passionate about search as you are. Let’s connect and work together to build the magical search experience that will get you the results you want.

Try it yourself