skip to content

How does Elasticsearch's boosting query demote documents compared with excluding them via must_not?

level: seniorimportance: nice to knowfreq 30%

answer

  1. One removes, the other keeps
  2. Membership is decided by only one side
  3. Scores are scaled, never subtracted
  4. The multiplier is a required parameter
  5. Below 1.0 sinks the document

basics

~20 s

must_not removes matching documents from the result set entirely. The boosting query keeps them: documents matching its positive query are returned, and those also matching its negative query have their score multiplied by negative_boost, so they sink rather than disappear.

solid answer

~40 s

`must_not` is a hard exclusion — matching documents are gone, whatever else they had going for them. The `boosting` query is a **soft** version: it returns documents matching its `positive` query, and any of those that *also* match its `negative` query keep their place in the result set but have their score multiplied by `negative_boost`, a required parameter. With `negative_boost` below 1.0 those documents sink; they still surface when nothing better exists. That is the right model for signals that are undesirable rather than disqualifying — refurbished items, out-of-stock products, archived or low-quality documents. Two properties are worth stating: a document matching only the `negative` query is not returned at all, because `positive` alone decides membership; and the `negative` side does not add to the score, it scales what the positive side produced.

code

json · 9 lines
json
{
  "query": {
    "boosting": {
      "positive": { "match": { "title": "espresso machine" } },
      "negative": { "term": { "condition": "refurbished" } },
      "negative_boost": 0.3
    }
  }
}

go deeper

for a junior

Recall that must_not excludes documents outright while the boosting query keeps them and lowers their score, and that the boosting query names its parts positive, negative and negative_boost.

for a middle

Explain the mechanics: positive decides membership, negative only supplies a multiplier, and negative_boost scales the positive score rather than subtracting from it.

for a senior

Show the judgement call — treating a trait as a preference rather than a rule so a thin result set never becomes empty — and note the cost of moving the predicate out of cacheable filter context.

for a principal

Own the policy boundary between correctness rules that must exclude and quality signals that should demote, and know when a single multiplier stops being expressive enough for the signal set.

## The shape ```json { "boosting": { "positive": { "match": { "title": "espresso machine" } }, "negative": { "term": { "condition": "refurbished" } }, "negative_boost": 0.3 }} ``` Three parts, all required: - **`positive`** — the query that decides membership. If a document does not match this, it is not a hit, full stop. - **`negative`** — a query whose matches are *undesirable*. It has no effect on membership. - **`negative_boost`** — the multiplier applied to the score of documents that match both. A value below 1.0 demotes; the closer to zero, the harder the demotion. So the scoring rule is: score = positive score, multiplied by `negative_boost` if the negative query also matched. ## Hard exclusion versus soft demotion `bool` `must_not` answers a different question. It runs in filter context, contributes nothing to the score, and removes matching documents from the result set. There is no middle ground: a document is in or out. That is correct when the predicate is a *rule* — this user may not see this tenant's data, this document is deleted, this product is not shippable to this country. Correctness demands exclusion and no amount of relevance should override it. The `boosting` query is correct when the predicate is a *preference*. Refurbished stock is worse than new but a customer searching for a discontinued model may want it. An archived wiki page is worse than a current one but may be the only page that answers the question. Excluding these produces the worst kind of search failure: an empty or short result page where a useful, merely-imperfect answer existed. A useful test: if the result set going empty because of this predicate would be a bug, it is a demotion, not an exclusion. ## Reading the multiplier `negative_boost` multiplies rather than subtracts, which keeps the demotion proportional. A strongly-matching refurbished item at score 8.0 with `negative_boost` 0.3 lands at 2.4 — it can still beat a weak new item at 1.5. Subtraction would not have that property, and would risk pushing scores negative. Choosing the value is a relevance decision, not a correctness one. Near 1.0 the demotion is barely visible; near 0 the documents are effectively hidden without technically being excluded. Tune it against judgement data rather than picking a number once and forgetting it, and remember that if the answer is genuinely "never show these", `must_not` is simpler and cheaper, because filter context skips scoring altogether and is cacheable. ## Membership subtleties Two things trip candidates up: 1. **A document matching only `negative` is not returned.** The negative query is not a second way in. Membership is entirely `positive`'s job. 2. **The negative query is scored only to the extent of deciding match-or-not** for the multiplier; it does not add a contribution. There is no "negative score" in Elasticsearch, which is why the redirect from "can I subtract points?" to "you multiply by a factor below one" is the correct correction. ## Composing it `boosting` takes exactly one query on each side, but each may be a `bool`, so arbitrary complexity is possible: a `bool` of the real search on the positive side, a `bool` of every demotable trait on the negative side. Placing hard predicates in the positive side's `filter` array keeps them unscored and cacheable while the demotion logic sits outside. When you need several *different* demotion strengths for several different traits — refurbished at 0.5, out of stock at 0.2, low rating at 0.8 — one `boosting` query cannot express that, because it has a single multiplier. That is the point where richer score shaping becomes the right tool, and a candidate who names that boundary shows they understand the query's limits rather than reaching for it reflexively. ## Diagnosing Run with `"explain": true`: a demoted document shows the positive query's contribution and a separate multiplication by the `negative_boost` value. If a document you expected demoted shows no such factor, the negative query is not matching it — often because the negative query is a `match` against an analyzed field where you meant an exact `term`, or vice versa. ## The interview answer in one line must_not deletes, boosting demotes; boosting keeps the document reachable so a thin result set does not become an empty one, at the cost of running in query context where must_not would have been unscored and cacheable.

  • What happens to a document that matches only the negative query of a boosting query?
    It is not returned. Membership in the result set is decided entirely by the positive query; the negative query only supplies the multiplier for documents that already matched positive. Treating negative as a second entry path is a common misreading, and it also means you cannot use boosting to pull extra documents into a result set.
  • Why does negative_boost multiply the score rather than subtract from it?
    Multiplying keeps the demotion proportional and cannot drive a score negative. A strong match at 8.0 demoted by 0.3 still lands at 2.4 and can outrank a weak clean match at 1.5, which is exactly the intended behaviour for a soft signal. Subtraction would flatten strong and weak matches by the same absolute amount.
  • You need three different demotion strengths for three different traits. Can one boosting query do it?
    No. A boosting query carries exactly one negative query and one negative_boost, so every demotable trait shares the same multiplier. Nesting boosting queries to layer multipliers gets hard to reason about quickly. Multi-signal score shaping with per-signal weights is a different tool's job.
  • What is the cost of demoting rather than excluding?
    The negative side runs in query context, so it is evaluated and scored per candidate rather than reduced to a cacheable doc-id set the way a must_not clause is. You also keep more documents in the candidate pool. If the predicate is genuinely a rule rather than a preference, must_not is both simpler and cheaper.

must_not is a bouncer refusing entry; the boosting query seats those guests at the back of the room, where they are still available if the front tables are empty.

saying these in an interview costs you the question

  • Says the boosting query removes the negative matches
  • Thinks a document matching only negative is returned
  • Claims negative_boost subtracts points from the score
  • Uses boosting for hard rules like tenancy or permissions
  • Believes negative_boost is optional with a sensible default

context