Conversation
Local-mode DBSF normalized every prefetch's raw score and sorted the fused result descending. On Euclid/Manhattan collections a plain nearest-neighbour search scores with the raw distance, where a lower value is the better match, so the normalization mapped the farthest point to the highest fused score and the whole ranking came out reversed. Core normalizes the internal similarity, which is oriented "bigger is better" for every metric, so it does not have this problem. Negate the scores of the smaller-is-better sources before normalizing to match. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
✅ Deploy Preview for poetic-froyo-8baba7 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthrough
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
All Submissions:
devbranch. Did you create your branch fromdev?Changes to Core Features:
What
Local-mode
Fusion.DBSFreturns the worst matches first onEuclidandManhattancollections — the fused ranking is exactly reversed.
RRFis unaffected: it fuses ranks, not scores.Why
distribution_based_score_fusionnormalizes each source with(score - (mean - 3*std)) / (6*std)and then sorts the summed scores descending(
qdrant_client/hybrid/fusion.py). That assumes a higher input score is a better match.In core it is: DBSF runs on the internal similarity, which is oriented "bigger is better"
for every metric (Euclid/Manhattan similarity is the negated distance, converted back only
during post-processing), and
ScoreFusion::dbsf()therefore hardcodesorder: Order::LargeBetter:https://github.com/qdrant/qdrant/blob/master/lib/segment/src/common/score_fusion.rs
In local mode
LocalCollection._searchscores a plain nearest query with the raw distanceand sorts ascending when
distance_to_order(...) is SMALLER_IS_BETTER, so the prefetchresults handed to DBSF are oriented the opposite way — and normalization silently flips them.
Fix:
distribution_based_score_fusiontakes an optionalsmaller_is_betterflag per source andnegates those scores before normalizing, which is exactly the orientation core normalizes.
LocalCollectioncomputes the flag per prefetch (_prefetch_scores_are_smaller_better): onlyplain
NearestQueryon a dense/multi vector inherits the metric's direction — fusion, recommend,discovery, context, formula and sparse (always DOT) sources are all bigger-is-better.
Evidence: test fails before, passes after
ruff-format --line-length=99clean on all three touched files.Duplicate check (2026-09-19T18:25Z and re-checked 2026-09-19T18:27Z UTC)
Searched
qdrant/qdrant-clientissues and PRs in all states fordbsf,fusion,euclid,distribution based score,distribution_based_score_fusion,score fusion order.Prior DBSF work is unrelated: #871/#872/#875 (zero variance / zero division), #815/#817
(empty responses), #703 (original DBSF implementation), #1136/#1137/#1138 (
score_thresholdafter fusion), #1372/#1373 (root filters after fusion).
Open Euclid/Manhattan score-direction work is a different code path — #1370/#1378 and PRs
#1371/#1374/#1439 are about
score_thresholdand ordering for recommend/discovery/context/feedback queries in
LocalCollection.search; none of them touch fusion.Listed all 80 open PRs and their changed files: no open PR touches
qdrant_client/hybrid/fusion.py, and no open PR changes_merge_sources/ the DBSF path inqdrant_client/local/local_collection.py.Disclosure: prepared with AI assistance (Claude Code); I reviewed the change and take responsibility for it.
🤖 Generated with Claude Code