Skip to content

Rank() sorts non-matching items above real matches (e.g. "hell" ranks above "say hello …" for "hello") #123

Closed

Description

@matt-edmondson

What's wrong

TextFilter.Rank (TextFilter/TextFilter.cs:204-217) calculates isMatch for every item but never uses it. It sorts on score alone:

return items.Select(item => { bool isMatch = IsMatch(..., out int score, TextFilterType.Fuzzy); return (item, isMatch, score); })
	.OrderByDescending(t => t.score)
	.Select(t => t.item);

When the pattern isn't fully present, FuzzySearch's score only reflects how far the partial match got. A short near-miss avoids the penalties for skipped and extra characters that a real match inside a longer string pays. As a result, non-matches regularly score higher than genuine matches.

Keeping non-matches in the output is intended; the existing test keeps "world" for "helo". The bug is that they sort above real matches.

Reproduction

Observed scores from a scratch test:

pattern item isMatch score Rank order
abc axxxxxxbxxxxxxc true -2
abc ab false 15 ab, axxxxxxbxxxxxxc
readme docs/ReleaseNotes/readme.md true 6
readme read false 25 read, docs/ReleaseNotes/readme.md
config src/app/settings/config.json true 3
config conf false 25 conf, src/app/settings/config.json
hello say hello to everyone in the room true 5
hello hell false 25 hell, say hello to everyone in the room

Why it matters

Rank is the API for type-ahead and file-picker lists. Typing config puts conf ahead of the config.json you are looking for, and readme puts read ahead of readme.md.

Suggested fix

.OrderByDescending(t => t.isMatch)
.ThenByDescending(t => t.score)

The existing Rank tests still pass with this change, because "world" is a non-match and already sorts last.

Add a regression test: Rank(["hell", "say hello to everyone"], "hello") returns "say hello to everyone" first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions