Skip to content

Exact match scores lower than the same text with a suffix (Score("itemMap","item") = 32 > Score("item","item") = 25) #91

Description

@matt-edmondson

What's wrong

In CalculateScoreCore (FuzzySearch/Fuzzy.cs:195-259), a character can still count as a rematch after the whole pattern has matched (hasPatternChar == false), as long as it is another copy of the last pattern letter:

bool rematch = bestLetterIdx is not null && CodepointsEqual(subject, bestLetterIdx.Value, ..., subject, strIdx, ...);

When that later copy falls on a camelCase or separator boundary, its newScore beats the original letter's score, so it replaces the original:

  • it earns +10 for the boundary
  • it often earns +5 for adjacency as well, because prevMatched was set by the very letter it is replacing, so this bonus is spurious

The replacement costs only -1, and each extra character costs -1. The net result is that trailing text raises the score above the exact match.

Reproduction

Observed scores from a scratch test:

pattern subject score
ab ab 15
ab ab_b 18
item item 25
item itemMap 32
list list 25
list listTools 30
test test 25
test testTest 32

Why it matters

Type-ahead lists built on Score, including TextFilter's Rank, show an exact name below longer names that start with it. For example, typing list ranks listTools above list.

Relation to #89

#89 touches the same rematch branch, but for the opposite case: a rematch that loses. As a check, I applied #89's proposed else { score += unmatchedLetterPenalty; } locally. itemMap stayed at 32 and testTest only went from 32 to 31, so fixing #89 does not fix this.

Suggested fix

  • Allow a rematch to replace the best letter only while the pattern is still being matched (hasPatternChar).
  • At minimum, don't award the adjacency bonus when the previous match is the letter being replaced.
  • Add regression tests:
    • Score("item","item") > Score("itemMap","item")
    • Score("list","list") > Score("listTools","list")

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingreadyFully specified; implement as written

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions