Skip to content

feat: add fuzzy search with fuse.js#3829

Merged
MarkusNeusinger merged 4 commits intomainfrom
claude/improve-search-matching-bAwKR
Jan 14, 2026
Merged

feat: add fuzzy search with fuse.js#3829
MarkusNeusinger merged 4 commits intomainfrom
claude/improve-search-matching-bAwKR

Conversation

@MarkusNeusinger
Copy link
Copy Markdown
Owner

Summary

  • Add fuse.js for typo-tolerant fuzzy matching ("scater" → "scatter", "heatmp" → "heatmap")
  • Enable extended search for multi-word AND queries ("hi k" → "histogram-kde")
  • Search spec titles in addition to spec_id
  • Show spec title as tooltip in dropdown for spec category
  • Add "fuzzy" divider label between exact and fuzzy matches in dropdown

Configuration

  • FUZZY_THRESHOLD: 0.3 (max 30% dissimilarity)
  • EXACT_THRESHOLD: 0.1 (score < 0.1 = exact match)
  • minMatchCharLength: 1 (single characters allowed)

Test plan

  • Search "scater" → finds "scatter-*" specs
  • Search "heatmp" → finds "heatmap-*" specs
  • Search "hi k" → finds "histogram-kde"
  • Exact matches appear above "fuzzy" divider
  • Spec tooltips show full title on hover

Closes #3828

🤖 Generated with Claude Code

claude and others added 2 commits January 14, 2026 17:11
…anking

Enhance the search functionality to support flexible multi-word queries
and better match results:

Backend changes:
- Add spec titles to /plots/filter API response for richer search context
- Update _collect_all_images to include title field in image dicts

Frontend changes:
- Implement multi-word search: all query words must match but can appear
  anywhere in the value (not necessarily consecutive)
- Add relevance scoring to prioritize exact matches, prefix matches, and
  substring matches over multi-word matches
- Add title field to PlotImage interface for future search enhancements

Examples of improved matching:
- "scatter basic" now matches: scatter-basic, basic-scatter, scatter-basic-3d
- "bar horiz" now matches: bar-horizontal, bar-grouped-horizontal
- Results ranked by relevance: exact > starts-with > contains > multi-word

Fixes the issue where searching for two short words only worked with
exact consecutive matches.
- Add fuse.js for typo-tolerant fuzzy matching (threshold: 0.3)
- Enable extended search for multi-word AND queries
- Search spec titles in addition to spec_id
- Show spec title as tooltip in dropdown
- Add "fuzzy" divider label between exact and fuzzy matches
- Add specTitles mapping to API response

Closes #3828

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 14, 2026 22:51
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds fuzzy search functionality to the filter system using fuse.js, enabling typo-tolerant matching for filter values. The implementation includes backend support for spec titles and frontend UI enhancements to display exact vs. fuzzy matches with tooltips.

Changes:

  • Added fuse.js dependency for fuzzy search with configurable thresholds (0.3 for matching, 0.1 for exact classification)
  • Introduced spec title search alongside spec_id matching and visual separators between exact/fuzzy results
  • Extended backend API to return spec titles mapping for enhanced search context

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
app/package.json Added fuse.js ^7.1.0 dependency
app/yarn.lock Lock file update for fuse.js
app/src/utils/fuzzySearch.ts New fuzzy search utility with fuse.js configuration and match type classification
app/src/utils/filters.ts Refactored getSearchResults to use fuzzy matching with exact/fuzzy result grouping
app/src/utils/index.ts Export fuzzySearch utilities
app/src/types/index.ts Added title field to PlotImage and specTitles to FilteredPlotsResponse
app/src/components/FilterBar.tsx Updated to display fuzzy results with divider and spec title tooltips
app/src/hooks/useFilterState.ts Pass through specTitles from API response
app/src/hooks/useFilterFetch.ts Fetch and manage specTitles state
api/schemas.py Added specTitles field to FilteredPlotsResponse schema
api/routers/plots.py Generate spec_id to title mapping and include title in image dicts

Comment on lines +1 to +5
/**
* Fuzzy search utilities using fuse.js.
*
* Provides typo-tolerant search for filter values.
*/
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The new fuzzySearch utility module lacks test coverage. Given that this is core search functionality with specific threshold configurations (FUZZY_THRESHOLD=0.3, EXACT_THRESHOLD=0.1), tests should verify the match type classification logic and ensure typo tolerance works as expected (e.g., 'scater' → 'scatter').

Copilot uses AI. Check for mistakes.
Comment thread app/src/utils/filters.ts
Comment on lines 86 to +112
export function getSearchResults(
filterCounts: FilterCounts | null,
activeFilters: ActiveFilters,
searchQuery: string,
selectedCategory: FilterCategory | null
): { category: FilterCategory; value: string; count: number }[] {
if (!filterCounts) return [];
selectedCategory: FilterCategory | null,
specTitles: Record<string, string> = {}
): SearchResult[] {
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The refactored getSearchResults function now includes fuzzy matching logic and spec title searching, but there are no tests for this updated behavior. Tests should verify that exact matches are sorted before fuzzy matches, that spec titles are searched for the 'spec' category, and that the matchType field is correctly assigned.

Copilot uses AI. Check for mistakes.
MarkusNeusinger and others added 2 commits January 15, 2026 00:01
- Add vitest as test framework
- Add tests for fuzzySearch.ts (match type, typo tolerance)
- Add tests for getSearchResults (sorting, filtering, spec titles)
- All 19 tests passing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add test-frontend job to ci-tests.yml
- Add @vitest/coverage-v8 for coverage reporting
- Add vitest.config.ts with coverage configuration
- Upload frontend coverage to Codecov with 'frontend' flag
- Only runs when app/ files change
- Add app/coverage/ to .gitignore

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 14, 2026 23:06
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 14, 2026

Codecov Report

❌ Patch coverage is 77.41935% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
app/src/utils/fuzzySearch.ts 54.54% 5 Missing ⚠️
app/src/utils/filters.ts 90.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 17 changed files in this pull request and generated 3 comments.

keys: ['value', 'title'],
threshold: FUZZY_THRESHOLD,
distance: 100,
minMatchCharLength: 1,
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

Setting minMatchCharLength: 1 can lead to many false positives with single-character searches. Consider setting this to at least 2 to improve search quality and performance, especially for large datasets. Single-character searches are rarely useful for finding specific plot specifications.

Suggested change
minMatchCharLength: 1,
minMatchCharLength: 2,

Copilot uses AI. Check for mistakes.
Comment on lines +744 to +750
return specTitle ? (
<Tooltip key={`${category}-${value}`} title={specTitle} placement="right" arrow>
<span>{menuItem}</span>
</Tooltip>
) : (
menuItem
);
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

Wrapping MenuItem in a <span> is necessary for Tooltip to work, but this duplicates the key attribute (it's already on menuItem at line 713). The wrapping span should not have a key prop. Remove the key from the ternary expression at line 745 since the menuItem already has the unique key.

Copilot uses AI. Check for mistakes.
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./app/coverage/coverage-final.json
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

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

The coverage file path should be relative to the working directory. Since the test runs in working-directory: app, the path should be ./coverage/coverage-final.json (without the app/ prefix), or the working-directory should be removed from the codecov action. Verify that coverage upload works correctly in CI.

Copilot uses AI. Check for mistakes.
@MarkusNeusinger MarkusNeusinger merged commit c6773ae into main Jan 14, 2026
13 of 15 checks passed
@MarkusNeusinger MarkusNeusinger deleted the claude/improve-search-matching-bAwKR branch January 14, 2026 23:19
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.

Fuzzy Search: Typo-tolerant search with fuse.js

3 participants