fix: keep the correct top-N under limit when scores tie#835
Closed
spokodev wants to merge 1 commit into
Closed
Conversation
9d899fc to
572811d
Compare
search(query, { limit: N }) could return a different, worse-ordered top-N
than search(query).slice(0, N) when scores tie at the cutoff. The heap that
selects the top-N ordered and evicted by raw score only, ignoring the
tie-break and any custom sortFn, so on a tie the heap could drop the item
the sort order says to keep.
Give MaxHeap the sort comparator and order and evict by it, so the retained
top-N matches a full sort followed by a slice and a custom sortFn is honored.
dist regenerated with npm run build.
572811d to
a3a2af7
Compare
krisk
added a commit
that referenced
this pull request
Jul 13, 2026
search(query, { limit: N }) selected the top-N with a max-heap that
ordered and evicted by raw score alone, ignoring the sortFn tie-break
and any custom sortFn. On a score tie at the cutoff it could drop the
item a full sort would keep, so the result differed from
search(query).slice(0, N).
Order the heap by the sort comparator and break comparator ties by item
index, applying the same tie-break to the full-sort path. The retained
top-N is now identical to a full sort followed by a slice for every
index, including reordered parseIndex records, and a custom sortFn is
honored. Gate the heap on shouldSort so shouldSort:false keeps
collection order.
Remove the now-redundant MaxHeap.shouldInsert (insert already guards)
and consolidate the sortFn casts into one typed assertion.
Reported in #835.
Owner
|
Your diagnosis was right: the heap ordered and evicted by raw I've extended it a fair bit past your branch, so I'm landing it as a separate commit rather than merging directly. The same root cause turned up in a few more spots:
Also dropped Closing in favor of that commit. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
search(query, { limit: N })can return a different, worse-ordered top-N thansearch(query).slice(0, N)when scores tie at the cutoff. The heap that selects the top-N orders and evicts by rawscoreonly, so it ignores the tie-break and any customsortFn.The documented contract, and the repo's own
limit results match top-N of unlimited resultstest, is that a limited search returns the same items in the same order as an unlimited search truncated to N. The defaultsortFnbreaks a score tie by ascendingrefIndex, but the heap evicts whatever sits at its root, which on a tie can be the lowest-refIndex(best) item, so the correct item is dropped. A customsortFnis never consulted during eviction at all.Repro
Fix
Give
MaxHeapthe sort comparator and order and evict by it, so the worst retained item is the one that sorts last. The retained top-N is then identical to a full sort followed by a slice, and a customsortFnis honored.src/is the source of truth;dist/is rebuilt withnpm run build.Added a tie-break case to
test/optimizations.test.js. It fails before the change; the full suite (372) stays green, and typecheck, lint and format:check pass.