What's wrong
In CalculateScoreCore (FuzzySearch/Fuzzy.cs ~223-260), a subject character that re-matches the current best letter (rematch) is charged unmatchedLetterPenalty only when its newScore >= bestLetterScore. When it scores lower, no branch charges it anything. It still sets prevMatched = true, so the character after it also gets the adjacency bonus.
Whether a repeated letter costs anything therefore depends on the bonus earned by the letter it repeats. A first letter with the separator bonus (10) makes every later repeat free, because an adjacent repeat only ever scores 5.
Measured scores for pattern "ab" (current main)
| Subject |
Score |
ab |
15 |
aab |
15 |
aaaaaaaaaab |
15 |
AAAAAAAAb |
15 |
abx |
14 |
abbbbbbbbbb |
6 |
a_b |
19 |
a_bbbbbbbbbb |
19 |
aaaaaaaaaab ties the exact match and outranks abx, even though it has nine extra characters.
- Repeated
bs after ab cost 1 each, but the same repeats after a_b cost nothing.
Why it matters
The score is used for ranking, and the documented rule is that unmatched characters are penalized. Noise made of repeated letters can rank a candidate at or above an exact match, which misorders filter results. Earlier gist-derived scoring defects were fixed in #66 and #82; this one was carried over from the same original gist.
Suggested fix / acceptance criteria
Charge the penalty when a rematch loses as well:
if (newScore >= bestLetterScore)
{
if (bestLetterIdx is not null) score += unmatchedLetterPenalty;
...
}
else
{
score += unmatchedLetterPenalty;
}
This was tried on a local build. aab scores 14, aaaaaaaaaab scores 6 and a_bbbbbbbbbb scores 10; the other rows are unchanged, and all 51 existing tests still pass.
- Add regression tests asserting
Score("ab","ab") > Score("aab","ab"), Score("abx","ab") > Score("aaaaaaaaaab","ab"), and Score("a_b","ab") > Score("a_bbbbbbbbbb","ab").
What's wrong
In
CalculateScoreCore(FuzzySearch/Fuzzy.cs~223-260), a subject character that re-matches the current best letter (rematch) is chargedunmatchedLetterPenaltyonly when itsnewScore >= bestLetterScore. When it scores lower, no branch charges it anything. It still setsprevMatched = true, so the character after it also gets the adjacency bonus.Whether a repeated letter costs anything therefore depends on the bonus earned by the letter it repeats. A first letter with the separator bonus (10) makes every later repeat free, because an adjacent repeat only ever scores 5.
Measured scores for pattern
"ab"(currentmain)abaabaaaaaaaaaabAAAAAAAAbabxabbbbbbbbbba_ba_bbbbbbbbbbaaaaaaaaaabties the exact match and outranksabx, even though it has nine extra characters.bs afterabcost 1 each, but the same repeats aftera_bcost nothing.Why it matters
The score is used for ranking, and the documented rule is that unmatched characters are penalized. Noise made of repeated letters can rank a candidate at or above an exact match, which misorders filter results. Earlier gist-derived scoring defects were fixed in #66 and #82; this one was carried over from the same original gist.
Suggested fix / acceptance criteria
Charge the penalty when a rematch loses as well:
This was tried on a local build.
aabscores 14,aaaaaaaaaabscores 6 anda_bbbbbbbbbbscores 10; the other rows are unchanged, and all 51 existing tests still pass.Score("ab","ab") > Score("aab","ab"),Score("abx","ab") > Score("aaaaaaaaaab","ab"), andScore("a_b","ab") > Score("a_bbbbbbbbbb","ab").