Skip to content

fix(web): stop search drops major hubs like "I. P. Pavlova" - #29

Open
i11v wants to merge 1 commit into
mainfrom
claude/search-results-clutter-utm0m8
Open

fix(web): stop search drops major hubs like "I. P. Pavlova"#29
i11v wants to merge 1 commit into
mainfrom
claude/search-results-clutter-utm0m8

Conversation

@i11v

@i11v i11v commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Problem

Searching a stop name surfaced the wrong stops and could omit the one the user wanted. Reported for "Pavlov": the hub I. P. Pavlova never appeared in the results.

Root cause

searchStops truncated its candidate list with slice(0, limit) (limit = 10). That list is expanded: every stop contributes a grouped row plus one row per platform, and for a prefix match all of those rows share the same score (100).

So a handful of multi-platform prefix matches consumed the entire budget before lower-tier matches of other stops were reached. For "Pavlov", the rural Pavlov* stops and their A/B/… platform rows filled all 10 slots, and the word-boundary match I. P. Pavlova (score 80, because "Pavlov" sits after the "I. P." initials) was cut off entirely — it was never in the list to begin with.

Verified against a verbatim copy of the matcher: the current matcher only returns genuine substring matches, so the other reported symptom (top results cluttered with geographically-nearby stops that share no letters with the query, e.g. Náměstí Bratří Synků) cannot be produced by this code — that comes from a stale, PWA-cached older bundle. Shipping this fix re-hashes the JS bundle and busts that cache.

Fix

Cap the search by distinct stops, not expanded platform rows, so one stop's platforms can't crowd out another stop. Each included stop still forwards all of its platform rows (needed for platform-scoped queries like "Anděl A"). The result budget is 12 distinct stops.

With the fix, "Pavlov" now includes I. P. Pavlova; when geolocation is granted, the existing proximity scorer promotes the nearby hub toward the top.

Changes

  • packages/web/src/lib/matcher.ts — cap searchStops by distinct nodes instead of raw candidate rows.
  • packages/web/src/components/search.tsx — request up to RESULT_LIMIT (12) distinct stops for a text query.
  • packages/web/test/matcher.test.ts — regression test: a word-boundary match isn't evicted by another stop's grouped + per-platform rows.

Verification

  • vitest run packages/web — 36 passed (incl. new regression test)
  • oxlint, oxfmt --check, tsc -p packages/web — all clean

🤖 Generated with Claude Code


Generated by Claude Code

searchStops truncated its expanded candidate list with slice(0, limit),
and that list mixes each stop's grouped row with one row per platform.
A few multi-platform prefix matches therefore consumed the whole budget
and evicted lower-tier matches of *other* stops: searching "Pavlov"
filled the results with rural "Pavlov*" stops and their A/B/… rows and
dropped the word-boundary hub "I. P. Pavlova" (score 80) before it was
ever reached.

Cap by distinct nodes instead, so one stop's platforms can't crowd out
another stop, and raise the search result limit to 12 distinct stops.
Every included stop still forwards all its platform rows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YGtx4fHwV4ErdWoH7YdDL9
@github-actions

Copy link
Copy Markdown

🚀 Preview deployed: https://preview-29.tablo.run
fallback: https://tablo-pr-29.i11v.workers.dev

@i11v
i11v marked this pull request as ready for review July 14, 2026 11:53
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.

2 participants