Skip to content

fix(fieldNorm): count word-starts instead of space transitions to avoid over-counting leading/trailing spaces#830

Merged
krisk merged 1 commit into
krisk:mainfrom
JSap0914:fix/fieldnorm-leading-trailing-space
Jun 22, 2026
Merged

fix(fieldNorm): count word-starts instead of space transitions to avoid over-counting leading/trailing spaces#830
krisk merged 1 commit into
krisk:mainfrom
JSap0914:fix/fieldnorm-leading-trailing-space

Conversation

@JSap0914

Copy link
Copy Markdown
Contributor

Summary

fieldNorm's word-counting loop started numTokens at 1 and incremented on every space-to-non-space transition. This means a leading space was treated as a transition from an implicit word, over-counting the number of words:

Input Old numTokens New numTokens
" hello" 2 1 ✅
"hello " 2 1 ✅
" hello world " 3 2 ✅
" " 2 1 ✅
"hello world" 2 ✅ 2 ✅
"" 1 ✅ 1 ✅

The inflated count produces a lower norm value (e.g. 0.707 instead of 1.0 for a single-word field), which in turn raises the final multiplicative score — penalising any document whose stored field value happens to contain leading or trailing whitespace.

Fix

Replace the space-transition counter with a word-start counter: increment only when encountering a non-space character that follows a space or the start of the string. This is equivalent to the old logic for well-trimmed strings (same result for "hello world", "hello world", etc.) and correctly ignores stray boundary whitespace. Strings with zero real words (empty or all-whitespace) are clamped to numTokens = 1 to preserve the existing fallback.

Tests

A new test — "does not count leading or trailing spaces as word boundaries" — was failing before the fix and passes after. All 371 existing tests continue to pass.

AI-assist: this fix was generated with AI assistance.

Leading and trailing spaces were silently inflating numTokens by one
boundary each, producing a lower norm value (0.707 instead of 1.0 for a
single-word field) and therefore a higher (worse) final search score for
any document whose field value contained stray whitespace.

Root cause: the original loop started numTokens at 1 and incremented on
every space→non-space transition. A leading space was treated as a
transition from an implicit word, so ' hello' gave numTokens=2 instead
of 1. Similarly, 'hello ' transitioned word→space near the end, giving
numTokens=2 even though only one word is present.

Fix: rewrite the counter to tally word-starts (non-space character that
follows a space or the string start). This is equivalent for typical
strings — 'hello world' still yields 2 — but correctly ignores leading
and trailing whitespace. Empty and all-whitespace strings are clamped to
1 to preserve the existing fallback behaviour.

Test: add a failing test that demonstrates the bug for ' hello',
'hello ', ' hello world ', and ' ', then verify it passes after fix.
Copilot AI review requested due to automatic review settings June 22, 2026 00:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@krisk krisk left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, this one's real. " hello" was counting as 2 tokens since values reach norm.get() untrimmed and only all-whitespace gets filtered. Fix looks good and trimmed strings come out the same as before.

Tiny thing: the table says old count for " hello world " is 3, it's actually 4.

Approving, thanks!

@krisk krisk merged commit 2946f97 into krisk:main Jun 22, 2026
krisk pushed a commit to chatman-media/Fuse that referenced this pull request Jul 3, 2026
The word counter added in krisk#830 only checked for charCode 32 (plain
ASCII space), so any field that separated words with a tab or
newline instead was scored as a single long word. That gives it an
artificially better field-length norm than a same-content field
using regular spaces - e.g. 'foo\tbar\tbaz' scores 20x better than
'foo bar baz' even though they're the same three words.

Added a small isWordSeparator helper covering the usual whitespace
range (tab/LF/VT/FF/CR, space, NBSP) instead of the single charCode
check, plus a test that fails without the fix.
krisk pushed a commit that referenced this pull request Jul 3, 2026
The word counter added in #830 only checked for charCode 32 (plain
ASCII space), so any field that separated words with a tab or
newline instead was scored as a single long word. That gives it an
artificially better field-length norm than a same-content field
using regular spaces - e.g. 'foo\tbar\tbaz' scores 20x better than
'foo bar baz' even though they're the same three words.

Added a small isWordSeparator helper covering the usual whitespace
range (tab/LF/VT/FF/CR, space, NBSP) instead of the single charCode
check, plus a test that fails without the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants