Skip to main content

GitHub release License: AGPL-3.0 Python SearXNG Docker

English | 中文

SearXNG BM25 Reranker

An external SearXNG plugin that reranks search results using BM25 text relevance scoring to improve search quality.

Features

  • BM25F Multi-Field Weighting — Title weight 2.0, content weight 1.0, prioritizing title-matching results
  • RRF Fusion Ranking — Combines BM25 scores with original engine rankings via Reciprocal Rank Fusion, rather than replacing them
  • CJK Tokenization — Built-in zero-dependency CJK tokenizer (unigram + bigram) covering Chinese ideographs and Japanese kana, no jieba required
  • HTML-Aware Scoring — Strips markup and decodes entities before tokenizing, so tag noise (div, href, www) never dilutes relevance
  • Zero External Dependencies — Core BM25 engine from zerodep/sparse_search, pure standard library
  • Plug and Play — Standard SearXNG external plugin, deployable via Docker image or pip install

How It Works

Search request → Engines return results → [post_search hook]
                                                ↓
                                   Build temporary BM25F index (title + content)
                                                ↓
                                   BM25 retrieval with original query
                                                ↓
                                   RRF fusion (engine ranking + BM25 ranking, k=20)
                                                ↓
                                   Rewrite positions to influence scoring → Reranked results

The plugin hooks into the post_search phase, after all engine results are collected but before final scores are calculated. By rewriting each result's positions list, it influences SearXNG's built-in calculate_score() formula (weight / position), achieving non-invasive reranking.

Installation

Use the pre-built image from Docker Hub, which tracks searxng/searxng:latest with the plugin pre-installed:

services:
  searxng:
    image: oaklight/searxng:latest
    restart: always
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - ./searxng:/etc/searxng:rw
    environment:
      - SEARXNG_BASE_URL=https://search.example.com

Option 2: Inline Build in Docker Compose

Build from the official SearXNG image with the plugin installed at compose time:

services:
  searxng:
    build:
      dockerfile_inline: |
        FROM searxng/searxng:latest
        RUN /usr/local/searxng/.venv/bin/python3 -m ensurepip && \
            /usr/local/searxng/.venv/bin/python3 -m pip install --no-cache-dir searxng-bm25-reranker && \
            /usr/local/searxng/.venv/bin/python3 -m pip uninstall -y pip
    restart: always
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - ./searxng:/etc/searxng:rw
    environment:
      - SEARXNG_BASE_URL=https://search.example.com

Plugin Registration

For both options, register the plugin in settings.yml:

plugins:
  searxng_bm25_reranker.SXNGPlugin:
    active: true

Then start or restart the container:

docker compose up -d

Configuration

The plugin works out of the box with no additional configuration. Every value below can be overridden per instance in settings.yml in a top-level block keyed by the plugin id; all must be numbers >= 1, and rrf_k must be a whole number.

Parameter Default Description
title_weight 2.0 BM25F weight for the title field
content_weight 1.0 BM25F weight for the content field
rrf_k 20 RRF fusion constant
engine_weight 1.0 Weight of the original engine ranking in the fusion
bm25_weight 1.0 Weight of the BM25 ranking in the fusion

Fixed and not exposed: Okapi BM25 with k1=1.5, b=0.75, delta=0.

rrf_k defaults to 20 rather than the Cormack et al. value of 60 because that constant was tuned on retrieval runs with thousands of candidates, while a SearXNG page holds roughly 30 results. At 60, the gap between rank 1 and rank 30 collapses to 1.48x and mutes the relevance signal. A value far above 20 flattens the differences further and eventually reduces the rerank to engine order.

bm25_reranker:
  rrf_k: 10
  bm25_weight: 2.0

The block is top-level and keyed by the plugin id -- SearXNG's convention for plugin-specific options (the in-tree hostnames plugin works the same way). PluginCfg only carries active, so putting these keys under plugins: makes SearXNG fail to start rather than fall back.

Raising bm25_weight lets text relevance outweigh the original engine order more aggressively; raising engine_weight does the opposite.

URL priority

url_priority multiplies the fused score of every result whose URL matches a regular expression, so a domain can be pushed up or down without touching the ranking code. A multiplier below 1.0 demotes, above 1.0 promotes, and 0.0 sends the result to the bottom. Every matching pattern applies, so overlapping entries compound.

The block goes at the top level of settings.yml, keyed by the plugin id. It does not go under plugins:, which only accepts active:

bm25_reranker:
  url_priority:
    '(.*\.)?arxiv\.org$': 1.5
    '(.*\.)?github\.com$': 1.2
    '(.*\.)?pinterest\.com$': 0.1
    '(.*\.)?facebook\.com$': 0.0

plugins:
  searxng_bm25_reranker.SXNGPlugin:
    active: true

An invalid regular expression, or a multiplier that is not a number or is negative, is skipped with a warning in the log. A YAML boolean counts as "not a number", so writing false does not switch a rule off; it is skipped too. A typo therefore degrades to "no multiplier" instead of breaking search.

Patterns are matched with re.search, so an unanchored pattern matches anywhere in the URL, query string included. Anchor with $ as the examples do to mean the whole domain. The setting is read when the plugin loads, so changing it needs a restart.

Project Structure

src/searxng_bm25_reranker/
├── __init__.py          # SXNGPlugin class, post_search reranking logic
├── _tokenizer.py        # CJK-aware tokenizer (unigram + bigram)
└── _vendor/
    └── sparse_search.py # BM25 engine from zerodep (vendored)

tests/
├── conftest.py          # SearXNG API stub (it is not a dependency)
├── conftest.py          # SearXNG API stub (it is not a dependency)
├── test_rerank.py
├── test_tokenizer.py
└── test_url_priority.py

Run the tests with pytest.

Acknowledgements

License

AGPL-3.0

Metadata

Release files for searxng-bm25-reranker 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for searxng-bm25-reranker 0.2.0
File Size Uploaded Provenance Actions
searxng_bm25_reranker-0.2.0.tar.gz 42.2 kB Without provenance Details for searxng_bm25_reranker-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for searxng-bm25-reranker 0.2.0
File Interpreter ABI Platform Actions
searxng_bm25_reranker-0.2.0-py3-none-any.whl Python 3 none any Details for searxng_bm25_reranker-0.2.0-py3-none-any.whl

Total release size: 76.4 kB

Release files / searxng_bm25_reranker-0.2.0.tar.gz

Download URL searxng_bm25_reranker-0.2.0.tar.gz
Size 42.2 kB
Tags Source
SHA-256 checksum
How to use checksums
acce4fe996125302e5acc04e58e0a91b2618c7669308e407805b91dc8b1d6680
BLAKE2b-256 checksum
How to use checksums
c7d34a521b7b7424abd80096af87657b95347a04db6e602834d42ba8d836444d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / searxng_bm25_reranker-0.2.0-py3-none-any.whl

Download URL searxng_bm25_reranker-0.2.0-py3-none-any.whl
Size 34.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2dc188ff50bd0071319d309631876044a9604d074b065fb1aaa2ceeac2ec69c4
BLAKE2b-256 checksum
How to use checksums
4a2be72f0707d290be13813833318148a06cfd9846eeeb6d54a953e64245440c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Security for searxng-bm25-reranker 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page