Skip to content

fix: rank tables first and tighten search in the quick navigator - #703

Open
debba wants to merge 1 commit into
mainfrom
fix/quick-navigator-ranking
Open

fix: rank tables first and tighten search in the quick navigator#703
debba wants to merge 1 commit into
mainfrom
fix/quick-navigator-ranking

Conversation

@debba

@debba debba commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Problem

A user reported that the quick navigator on their Postgres database shows about 1.6k entries for a schema with roughly 300 tables, that typing a table name does not filter the functions away, and that the search lags.

The extra entries come from extension functions (PostGIS alone adds over a thousand to public). Three things in the palette made that hard to live with:

  • Tables, views, routines and triggers all had the same weight, so functions ranked between tables.
  • The Fuse threshold of 0.4 allows two edits on a five letter query. With 1300 function names in the index that keeps most of them in the list. In a quick probe with PostGIS-like names, users returned 218 matches, 217 of them functions.
  • Every match was rendered on each keystroke with no upper bound, including all 1.6k rows when the palette opens with an empty query.

Changes

  • objectPaletteItems.ts: each object gets a relevance from OBJECT_TYPE_RELEVANCE (table 20, view 10, routine and trigger 0). With an empty query tables come first. With a query, a table containing the text ranks above functions, but typing the exact name of a function still puts that function first, since the boost stays well below the score of a verbatim match.
  • paletteItems.ts: threshold lowered from 0.4 to 0.3. That still tolerates one typo every four characters (the existing preferenses test keeps passing). Adds MAX_VISIBLE_PALETTE_RESULTS.
  • Palette.tsx: renders at most 100 rows. The footer still shows the total match count so nothing looks missing.

The threshold change also applies to the command palette, since both share createPaletteSearch.

Tests

New cases cover the type ranking, the tighter threshold against a large set of function names, exact routine match beating a typo'd table, tables-first ordering on an empty query, and the render cap. Full suite passes (236 files, 3906 tests), plus pnpm typecheck and eslint on the touched files.

Not in this PR

  • Filtering extension-owned functions out of the Postgres routine list (via pg_depend). It would shrink the sidebar as well, so it deserves its own discussion.
  • A boost for the active schema.

On a Postgres database with extensions like PostGIS the navigator lists
well over a thousand functions next to a few hundred tables. Three things
made that painful:

- tables, views, routines and triggers had the same weight, so functions
  sat between the tables you were looking for
- the fuzzy threshold (0.4) allowed two edits on a five letter query,
  which kept most of those functions in the result list
- every match was rendered on each keystroke, with no upper bound

Tables now get a relevance boost over views, and both over routines and
triggers. The boost is small enough that typing the exact name of a
function still puts it first. The Fuse threshold drops to 0.3, which
still tolerates one typo per four characters. The list renders at most
100 rows while the footer keeps the full match count.
@kilo-code-bot

kilo-code-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (6 files)
  • src/components/modals/commandPalette/Palette.tsx
  • src/utils/objectPaletteItems.ts
  • src/utils/paletteItems.ts
  • tests/components/modals/CommandPaletteModal.test.tsx
  • tests/utils/objectPaletteItems.test.ts
  • tests/utils/paletteItems.test.ts

Notes

Reviewed all six changed files (full files read for context, scope limited to changed lines). The changes cleanly address the quick navigator ranking/performance problem described in the PR:

  • OBJECT_TYPE_RELEVANCE is typed as Record<NavigatorItem["type"], number>, so it is exhaustive over the table/view/routine/trigger union — no missing-type undefined risk. The values (20/10/0/0) stay below PINNED_PALETTE_RELEVANCE (100), so context-pinned commands still float above objects, and the existing relevance + matchScore ranking formula is unchanged.
  • The Fuse threshold drop (0.4 → 0.3) is a documented tuning change; the existing preferenses typo test and the new large-schema test both remain valid.
  • The render cap (MAX_VISIBLE_PALETTE_RESULTS = 100) is applied in Palette.tsx only — createPaletteSearch still returns the full ranked list, so other callers (and the footer's total match count via matches.length) are unaffected. Keyboard navigation cycles within itemCount = results.length and onSubmit indexes into the capped results, so there is no out-of-bounds access.
  • New tests are valid and isolated: beforeEach resets the shared connectionData mock, and the constants reuse imported values rather than hardcoding.

No security, runtime-error, logic, or edge-case issues found in the changed lines.


Reviewed by glm-5.2 · Input: 37.2K · Output: 16K · Cached: 358.5K

@deager

deager commented Sep 2, 2026

Copy link
Copy Markdown

👋🏼 Got it up on my machine and it's definitely working better, but noticed a few issues:

  1. [bug] when i erase my search then reattempt, it has the same sorting issue as before
  2. [bug] it actually doesn't seem to be respecting my active schemas*
  3. [mostly a nit] when i search for my table
Sep-01-2026 14-10-04
  • My active schema is just public, but the ☝🏼 navigator has objects from all of the possible schemas, which is definitely clogging things up a bit
Screenshot 2026-09-02 at 10 29 37 AM

@debba

debba commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

thanks for feedback and let me check them in next days

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