Open-source Spryker search tools
Explainable, tunable search relevance for Spryker.
Debug and improve product ranking in Spryker with transparent scoring for Elasticsearch and OpenSearch.
finalScore =
Normalized text relevance + normalized business signals.
Then, query specificity shifts α →
d = 2 · specificitypspecificityp + kspecp − 1
specificity = β · max(log(catalog sizedocuments containing the term)) + (1 − β) · harmonicMean(log(catalog sizedocuments containing the term))
1 / 5
Relevance Dataset
Every optimization starts with a representative collection of search queries and manually judged search results.
Each query is associated with products and their expected relevance. The quality of the optimized formula depends directly on the quality and diversity of this dataset — it should cover common searches, edge cases and business-critical queries.
The dataset is independent from the optimization algorithm and can continuously evolve as the shop changes.
2 / 5
Rank Evaluation
Rank Eval replays every query from the dataset against the current formula and compares the returned order to the expected judgments.
Elasticsearch/OpenSearch Rank Eval executes every query of the relevance dataset using the current scoring formula. Metrics such as nDCG quantify how well the current parameter set performs across the entire dataset.
Rank Eval provides the objective fitness value that drives the optimizer.
3 / 5
CMA-ES Optimizer
Covariance Matrix Adaptation Evolution Strategy searches for an optimal parameter set without requiring derivatives or manual tuning.
Multiple parameter candidates are evaluated using Rank Eval each generation. Better candidates survive, influence the next generation, and the search distribution shrinks and reorients as it converges toward the optimum.
Unlike manual trial-and-error, CMA-ES explores the complete parameter space automatically.
4 / 5
Optimal Parameters
After the optimization completes, the best performing parameter set becomes the new runtime configuration for the adaptive scoring formula.
Typical optimized values include the specificity exponent, text weight, business signal weights, and the maximum specificity shift — the same symbols used throughout this formula.
Optimization happens offline. Runtime searches only use the resulting parameter set and remain deterministic and fast.
5 / 5
Adaptive Formula
The optimized parameters are loaded by the runtime search engine and applied to every query — deterministically, with no optimizer involved at request time.
Query specificity dynamically balances textual relevance and business signals while preserving a fully explainable ranking. Whenever the catalog or business priorities change, the optimization process can simply run again to produce a new parameter set.
Runtime ranking and offline optimization are intentionally separated. Search requests never execute the optimizer.
1 / 5
Analyze Query Terms
Every matched term is looked up in the index's own term statistics — how many documents contain it, out of how many total.
A term that appears in almost every product (chair) carries little information about intent. A term that appears in almost none (gsr) is strong evidence the customer knows exactly what they want. A term with zero real matches anywhere in the catalog is skipped entirely, not treated as maximally rare.
idfterm = log(catalog size / documents containing the term)
2 / 5
Blend into Raw Specificity
A query's per-term scores are combined into one number — weighted toward whichever term carries the strongest evidence.
The harmonic mean of every term's idf is pulled down hard the moment even one term is common, while the blend weight β lets the single rarest term still dominate when it's a strong enough signal on its own. A single-term query skips the blend — its own idf is the raw specificity.
specificity = β · max(idf) + (1 − β) · harmonicMean(idf)
3 / 5
Normalize — the same curve, again
Raw specificity is squashed through the exact same saturating shape used for Elasticsearch relevance — x / (x + k) — just with its own saturation point.
A curve exponent p can additionally steepen or soften the transition around the saturation point. It defaults to 1, which collapses back to the plain, unshaped ratio — the identical formula from step 2 of the main explorer, applied to a second signal.
normalize(x) = xp / (xp + kspecp)
4 / 5
Shift the Balance
Normalized specificity becomes a signed deviation from neutral, shaped and scaled into a nudge on α.
d = 2 · normalize(specificity) − 1 centers a typical query at exactly zero. The exponent γ shapes how sensitively the shift reacts near that center — dampened for small deviations when γ > 1, amplified when γ < 1 — and the magnitude m caps how far specificity alone can ever move α, in either direction.
αeff = α + m · sign(d) · |d|γ
5 / 5
Calibrate the Parameters
kspec, β, p, γ and m aren't hand-tuned guesses — they're calibrated the same way the rest of the formula is.
Search Ranking Optimizer's saturation-point calibration has a dedicated specificity mode: it samples real queries and suggests kspec directly from their observed distribution. The remaining shift parameters are part of the same parameter space the automated optimizer already searches — see Optimization above.
Same calibration + optimization pipeline, applied to specificity's own parameters.
01 / 05
How a result gets its score
Every hit is a convex combination of normalized text relevance and a weighted sum of business signals.
The model has exactly two terms. α decides how much influence text relevance receives; 1 − α gives the remaining influence to business signals.
Because both terms are normalized, their contributions remain comparable and the final score stays interpretable.
Implemented by Search Ranking · explained by Search Debug
02 / 05
Normalize Elasticsearch relevance
Elasticsearch's unbounded _score is squashed into a stable range from zero up to—but never reaching—one.
The transformation preserves the ordering of results while preventing large raw scores from overwhelming every other signal.
It uses the same saturation shape that BM25 applies to term frequency, here applied to the complete document score.
score / (score + k)
03 / 05
Choose the saturation point
The parameter k controls where normalized relevance reaches exactly 0.5.
When score = k, the relevance term equals 0.5. Lower values saturate earlier; higher values preserve more differentiation between large Elasticsearch scores.
Rather than choosing k by guesswork, Search Ranking can estimate a practical starting point from representative searches. Import a CSV of typical queries and define the depth to inspect; the package executes the set, samples the highest-ranked results, analyzes their raw _score values, and recommends the observed mean as the initial saturation point.
Representative queries + sampling depth → observed _score distribution → suggested k
04 / 05
Add normalized business signals
The second term combines product-level signals such as click-through rate, conversion rate, availability, or margin.
Each metric is transformed into a value between zero and one before it enters the formula. This makes signals with very different units comparable.
Search Ranking derives an aggregate frequency distribution from the metric data and evaluates the available normalization functions against that observed shape. The analysis view presents the empirical distribution beside the fitted curve and recommends the closest match; the selected expression remains fully configurable.
A low-weight random() signal is also worth including in the business term. It breaks deterministic ties, introduces controlled variation, and prevents the same products from becoming permanently locked into identical positions.
Distribution analysis → normalization-function suggestion · low-weight random() for controlled variation
05 / 05
Keep signal weights comparable
Entered signal weights are force-normalized so their sum always equals one.
Editors can express relative importance without manually maintaining a perfect total. A weight of 30 and a weight of 10 become 0.75 and 0.25.
This normalization runs once when configuration is published, keeping the query itself simple.
wi = enteredWeighti / Σ enteredWeightj
Roadmap
Search Relevance v1 targets the capabilities available in OpenSearch 1.3, which has been the default for Spryker installations for several years.
With Spryker now enabling OpenSearch 3.x, a new generation of ranking features becomes practical. Search Relevance v2 will explore reranking, hybrid retrieval, Learning-to-Rank and other capabilities that previously weren't available.
Origins
This project's starting point is Spryker's own Data-driven ranking best-practice document — which itself documents a real system, not a Spryker invention: the ranking approach originally designed and implemented by Martin Loetsch and Krešimir Slugan at Contorion. Spryker's core never shipped an implementation of it, only the write-up.
search-ranking and search-ranking-optimizer are that idea built out and adapted for Spryker's actual plugin/facade architecture, then extended well past the original scope — locale/store scoping, specificity-aware relevance weighting, and automated black-box optimization among them.