> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mixpeek.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Score Threshold

> Drop results below an absolute score and return no results when nothing qualifies

The Score Threshold stage applies an **absolute** quality gate: it drops every document whose score on a chosen field fails a minimum bar. When nothing clears the bar it returns an **empty** result set and sets `all_below_threshold` — the signal your UI uses to show a "no good results" state instead of presenting weak matches.

> [!NOTE]
>
> **Stage Category**: REDUCE (N documents → ≤ N documents)
>
> **Transformation**: keeps only documents meeting `min_score`; the set may become empty.

## Why not just normalize and filter?

`score_normalize` with `min_max` always rescales the **top** result to `1.0` — so a threshold on the normalized score can *never* reject an all-bad result set (the best match is always 1.0). Score Threshold gates on the **raw or calibrated** score, so "everything is below the bar" is expressible. Threshold on a calibrated score — the [`rerank`](https://docs.mixpeek.com/docs/retrieval/stages/rerank) cross-encoder score is ideal (`score_field: "scores.rerank"`), since it is far more absolute and comparable than a raw cosine similarity.

## When to Use

| Use Case | Description |
| - | - |
| **Suppress weak matches** | Don't show results that aren't good enough to be useful |
| **"No good results" UX** | Branch to a no-results / suggestion state via `all_below_threshold` |
| **Quality gate after rerank** | Hard-gate on the calibrated cross-encoder score |
| **Confidence cutoffs** | Only surface high-confidence matches to end users |

## When NOT to Use

| Scenario | Recommended Alternative |
| - | - |
| Rescale scores for comparison | [`score_normalize`](https://docs.mixpeek.com/docs/retrieval/stages/score-normalize) |
| Keep top-N regardless of quality | [`limit`](https://docs.mixpeek.com/docs/retrieval/stages/limit) |
| Filter by metadata fields | [`attribute_filter`](https://docs.mixpeek.com/docs/retrieval/stages/attribute-filter) |
| Reorder by score | [`sort_relevance`](https://docs.mixpeek.com/docs/retrieval/stages/sort-relevance) |

## Parameters

| Parameter | Type | Default | Description |
| - | - | - | - |
| `min_score` | float | *(required)* | Absolute minimum score a document must meet to be kept |
| `score_field` | string | `score` | Score field to gate on. Dot-paths supported (e.g. `scores.rerank`, `metadata.quality`) |
| `comparison` | string | `gte` | Keep docs whose score is `gte` (≥) or `gt` (strictly >) `min_score` |
| `missing_score` | string | `drop` | What to do with documents lacking `score_field`: `drop` or `keep` |

## Response Metadata

| Field | Description |
| - | - |
| `all_below_threshold` | `true` when **no** document met the bar (empty result set) |
| `input_count` / `output_count` | Documents in / kept |
| `dropped` | Documents removed |
| `missing_score_docs` | Documents that lacked `score_field` |

## Configuration Examples

```json Gate on the rerank score theme={null}
{
  "stage_name": "score_threshold",
  "stage_type": "reduce",
  "config": {
    "stage_id": "score_threshold",
    "parameters": {
      "min_score": 0.5,
      "score_field": "scores.rerank"
    }
  }
}
```

```json Strict cutoff on raw score (calibrate the number first) theme={null}
{
  "stage_name": "score_threshold",
  "stage_type": "reduce",
  "config": {
    "stage_id": "score_threshold",
    "parameters": {
      "min_score": 0.35,
      "comparison": "gt"
    }
  }
}
```

```json Keep docs missing the score field theme={null}
{
  "stage_name": "score_threshold",
  "stage_type": "reduce",
  "config": {
    "stage_id": "score_threshold",
    "parameters": {
      "min_score": 0.5,
      "score_field": "scores.rerank",
      "missing_score": "keep"
    }
  }
}
```

> [!WARNING]
>
> **A raw cosine score does not span 0 to 1, so do not copy a threshold between retrievers.**
> Each embedding model occupies its own narrow band, and a value that works on one model
> rejects every document on another.
>
> Measured on a production multimodal retriever (`multimodal_extractor@v2/gemini-embedding-2`,
> video segments, 27 text queries): on-topic queries scored 0.3755 to 0.4311 and off-topic
> controls scored 0.3136 to 0.3456. The separation is clean, and the whole range sits between
> 0.31 and 0.44. A `min_score` of 0.5 or 0.7 on that retriever returns zero documents for every
> query, including exact matches, and sets `all_below_threshold` every time.
>
> Read your own numbers before you pick a bar. Run the retriever without this stage, look at the
> top-1 score for a query that should match and one that should not, and put the bar between them.
> On the retriever above that puts the bar at 0.35, which was verified in both directions:
> `cars driving on a highway` keeps all 10 documents with `all_below_threshold` false, and
> `a library reading room with wooden shelves` drops all 10 and sets it true.

## "No Good Results → Suggestions" Pattern

Gate on the rerank score; when nothing qualifies, the empty result + `all_below_threshold` tells your application to fall back to [`query_expand`](https://docs.mixpeek.com/docs/retrieval/stages/query-expand) for adjacent suggestions instead of showing weak matches.

```json theme={null}
[
  {
    "stage_name": "feature_search",
    "stage_type": "filter",
    "config": {
      "stage_id": "feature_search",
      "parameters": {
        "searches": [
          {"feature_uri": "mixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1", "query": {"input_mode": "text", "value": "{{INPUT.query}}"}}
        ],
        "final_top_k": 50
      }
    }
  },
  {
    "stage_name": "rerank",
    "stage_type": "sort",
    "config": {
      "stage_id": "rerank",
      "parameters": {
        "inference_name": "BAAI__bge_reranker_v2_m3",
        "query": "{{INPUT.query}}",
        "document_field": "content"
      }
    }
  },
  {
    "stage_name": "score_threshold",
    "stage_type": "reduce",
    "config": {
      "stage_id": "score_threshold",
      "parameters": {
        "min_score": 0.5,
        "score_field": "scores.rerank"
      }
    }
  }
]
```

> [!TIP]
>
> Calibrate `min_score` empirically: run representative queries (including ones that *should* return nothing) and pick the value that separates good from bad on the rerank score. The right cutoff is query-domain specific.

## Performance

| Metric | Value |
| - | - |
| **Latency** | \< 1ms |
| **Memory** | O(N) |
| **Cost** | Free |
| **Complexity** | O(N) (single pass) |

## Related

* [Score Normalize](https://docs.mixpeek.com/docs/retrieval/stages/score-normalize.md) - Rescale scores to a common range
* [Rerank](https://docs.mixpeek.com/docs/retrieval/stages/rerank.md) - Calibrated cross-encoder scores to gate on
* [Query Expand](https://docs.mixpeek.com/docs/retrieval/stages/query-expand.md) - Adjacent suggestions when nothing qualifies
* [Limit](https://docs.mixpeek.com/docs/retrieval/stages/limit.md) - Truncate to top-N regardless of score


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.