-
Notifications
You must be signed in to change notification settings - Fork 2
systems merging and ranking
Active contributors: Magnus Hedemark
Merges results from multiple engines, deduplicates by normalized URL, and ranks results. V1 uses presence-weighted ranking. The Ranker interface is pluggable for future strategies.
| Type | File | Description |
|---|---|---|
Ranker |
slopsearx/merger.py |
Abstract base class for ranking strategies. The rank() method receives engine results and returns a ranked, deduplicated list. Designed to be pluggable from V1 so the strategy can be swapped without architecture changes. |
PresenceRanker |
slopsearx/merger.py |
V1 presence-weighted ranking. Normalizes URLs, deduplicates by normalized URL, boosts results that appear in multiple engines, and assigns positions by final score. |
_normalise_url |
slopsearx/merger.py |
Strips tracking parameters (utm_*, fbclid, gclid) from URLs for deduplication purposes. |
build_engine_status |
slopsearx/merger.py |
Builds per-engine status map from adapter responses: result count, latency, and status value. |
extract_unresponsive |
slopsearx/merger.py |
Extracts failing engines as [engine_name, reason] pairs for the SearXNG-compatible unresponsive_engines field. |
build_meta |
slopsearx/merger.py |
Builds the meta.* extension field with response time, cached flag, query ID, and per-engine status. |
merge_results |
slopsearx/merger.py |
Convenience wrapper for backward compatibility. Dispatches to the configured Ranker. |
Before deduplication, every result URL passes through _normalise_url(). The function:
- Parses the URL with
urllib.parse.urlparse() - Strips known tracking parameters:
utm_source,utm_medium,utm_campaign,utm_term,utm_content,fbclid,gclid - Reconstructs the URL without those parameters
This ensures that the same article linked with different tracking tags is treated as one result.
The deduplication strategy is first-occurrence-wins. When two engines return the same normalized URL:
- The first URL encountered keeps its position
- The second engine name is added to the result's
enginesset - The result's primary
enginefield stays as the original engine
if norm_url in seen:
existing = seen[norm_url]
existing.engines.add(engine_name)
existing.score = 1.0 * len(existing.engines)The V1 ranking algorithm assigns each result a base score of 1.0. When a result appears in multiple engines, its score is boosted by the number of engines that returned it:
score = 1.0 * number_of_engines
Results are then sorted by score descending. This biases toward consensus: a URL returned by 3 different engines is ranked above a URL returned by only 1 engine, regardless of which engine produced each.
Each engine can declare a per-request result budget via PresenceRanker.__init__(per_engine_budget). This caps how many results from a single engine contribute to the final ranking. The budget prevents a single engine with many low-quality results from dominating the merged output.
Presence-weighted ranking is deliberately simple. It is not better than any individual engine's ranking. It provides breadth (coverage gain from multiple engines) at the cost of precision (less accurate ordering than any single engine). The ranking is presence-weighted, not quality-weighted.
Three helper functions build the SearXNG-compatible metadata returned alongside results:
-
build_engine_status()assembles per-engine diagnostic data: result count, latency in milliseconds, and status -
extract_unresponsive()collects engines that returned non-OK statuses into[engine_name, reason]pairs -
build_meta()bundles everything into themeta.*extension dict:response_time_ms,cached,query_id,engine_status
-
Search flow: After
asyncio.gather()dispatches to all engines, the server collectsAdapterResponseobjects, groups results by engine, and passes them to_ranker.rank(). -
Response formatting: The ranked result list feeds into
format_json()orformat_yaml_markdown()for serialization. -
Metadata:
build_meta()output is injected into the response asmeta.*extension fields.
- Adding a new ranking strategy: subclass
Rankerand implementrank() - Adjusting deduplication behavior: modify
_normalise_url()to strip or retain additional parameters - Changing metadata structure: modify
build_engine_status()orbuild_meta()
| File | Description |
|---|---|
slopsearx/merger.py |
All ranking logic: Ranker, PresenceRanker, URL normalization, metadata helpers |