When a shopper searches a WooCommerce catalog, Elasticsearch does more than identify products containing the search terms. It calculates a relevance score for each matching document and uses that score to order the results. Understanding how _score is produced helps agencies diagnose poor search results, tune product queries, and explain ranking behavior to store owners.
What the _score value represents
The _score is a numeric estimate of how well a document matches a query. A higher score generally means a stronger match, but the number has no universal meaning outside the request that produced it. A score of 8.4 is not automatically better than a score of 4.2 from a different query, index, or shard configuration.
For example, a WooCommerce product with the title Waterproof Hiking Boots should normally rank above a product whose long description mentions “boots” once. The first product has a stronger match in an important field and may receive a higher score.
BM25 and the factors behind relevance
Elasticsearch uses the BM25 similarity algorithm by default for most text fields. BM25 considers several factors:
- Term frequency: how often a search term appears in the field.
- Inverse document frequency: how rare the term is across the index. A distinctive term usually carries more weight than a common term.
- Field-length normalization: a match in a short field can be more significant than the same match buried in a very long field.
- Query structure: clauses, boosts, minimum matches, and compound queries all affect the final score.
Suppose a shopper searches for merino wool sweater. A product matching all three terms in its name is likely to score better than one matching only “wool” in its description. However, the exact result depends on the mappings, analyzers, query type, and other documents in the index.
Query context versus filter context
Queries run in query context when Elasticsearch must determine how well each document matches. These clauses contribute to _score. A match, multi_match, or match_phrase clause is commonly used this way.
Clauses in filter context answer yes-or-no questions. They restrict the result set but do not calculate relevance scores. Filters are appropriate for exact WooCommerce attributes such as stock status, product visibility, category IDs, brand IDs, and price ranges.
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "waterproof hiking boots",
"fields": ["name^4", "short_description^2", "description"]
}
}
],
"filter": [
{ "term": { "stock_status": "instock" } },
{ "term": { "visibility": "catalog" } },
{ "range": { "price": { "gte": 50, "lte": 250 } } }
]
}
}
}
In this example, text relevance determines the score, while stock status, visibility, and price only determine which products are eligible. This separation generally improves performance and prevents business constraints from distorting text relevance.
Field boosts and WooCommerce product data
WooCommerce product data is often indexed into several fields. Agencies commonly give the product name the greatest weight, followed by SKU, short description, attributes, and full description. The caret syntax applies a boost:
{
"multi_match": {
"query": "red running shoes",
"fields": [
"name^5",
"sku^3",
"short_description^2",
"attributes^2",
"description"
],
"type": "best_fields"
}
}
With best_fields, the best matching field has a strong influence on the document score. This is useful when a product name match should outweigh scattered matches across several description fields. Test boosts with real catalog searches rather than assuming that a five-times boost will produce a predictable five-times ranking improvement.
Phrase matching and term matching
A standard match query analyzes the search text and can match the terms independently. A match_phrase query requires the terms to occur in the specified order, subject to the configured slop.
{
"query": {
"bool": {
"should": [
{
"match_phrase": {
"name": {
"query": "wireless keyboard",
"boost": 4
}
}
},
{
"multi_match": {
"query": "wireless keyboard",
"fields": ["name^3", "description"]
}
}
],
"minimum_should_match": 1
}
}
}
The phrase clause rewards products whose name contains the complete phrase, while the broader clause keeps products with relevant but non-exact wording eligible. This pattern can work well for searches involving product types, model names, or common multi-word attributes.
Inspecting scores with the Explain API
When a product ranks unexpectedly, use the Explain API to inspect the score calculation for a specific document. The response can show which terms matched, how rare they are, and how field boosts or Boolean clauses contributed.
GET products/_explain/12345
{
"query": {
"multi_match": {
"query": "waterproof hiking boots",
"fields": ["name^4", "short_description^2", "description"]
}
}
}
The Explain API is intended for diagnostics and can be expensive on production workloads. Use it against representative documents and queries, preferably in a staging environment or during controlled troubleshooting. For result-level inspection, add "explain": true to a search request, but avoid enabling it for normal storefront traffic.
Why scores can look surprising
- Scores depend on the indexed collection: term rarity is calculated from index statistics, so adding or removing products can change scores.
- Analyzers change matching: stemming, stop words, synonyms, lowercasing, and tokenization affect which terms are available to score.
- Long descriptions dilute signals: important matches can be less prominent when copied into large content fields.
- Shard-level statistics can differ: distributed searches may produce small score variations because term statistics are calculated per shard unless configured otherwise.
- Sorting can hide relevance: sorting by price, popularity, or a WooCommerce custom field replaces relevance ordering unless the implementation explicitly combines the values.
Combining relevance with business signals
Catalog search often needs to combine textual relevance with inventory, popularity, margin, or sales performance. A function_score query can modify the text score, but it should be introduced carefully. A strong popularity multiplier can cause a popular but weakly matching product to outrank a highly relevant product.
{
"query": {
"function_score": {
"query": {
"multi_match": {
"query": "running jacket",
"fields": ["name^4", "short_description^2", "description"]
}
},
"field_value_factor": {
"field": "sales_count",
"modifier": "log1p",
"factor": 0.2,
"missing": 0
},
"boost_mode": "multiply",
"max_boost": 3
}
}
}
log1p reduces the effect of very large sales counts, while max_boost limits how much the function can influence ranking. For more predictable behavior, compare alternatives with offline search tests and review the top results for important WooCommerce queries.
Practical relevance testing for agencies
- Build a test set of real searches, including exact product names, SKUs, attributes, misspellings, and broad category terms.
- Record expected products or acceptable result groups for each query.
- Capture the top results,
_score, and applied filters before changing mappings or boosts. - Change one ranking variable at a time, such as a field boost or phrase clause.
- Use the Explain API for individual anomalies instead of guessing from the numeric score alone.
- Verify that sorting, pagination, availability filters, and index refresh behavior do not obscure the ranking change.
For a WooCommerce search integration, relevance work should be evaluated at the result-set level. The goal is not to maximize an abstract score; it is to place the most useful products near the top while preserving exact SKU matches, valid stock rules, category constraints, and the store’s merchandising requirements.