diff --git a/apps/desktop/src/lib/table/objectBrowserRows.ts b/apps/desktop/src/lib/table/objectBrowserRows.ts index d963f9c4d0..3e4bef60c9 100644 --- a/apps/desktop/src/lib/table/objectBrowserRows.ts +++ b/apps/desktop/src/lib/table/objectBrowserRows.ts @@ -231,9 +231,14 @@ export function filterObjectBrowserRows(rows: ObjectBrowserRow[], query: string) if (!q) return rows; const regex = parseSlashDelimitedRegexQuery(query.trim()); if (regex) { - return rows.filter((row) => [row.displayName, row.name, row.type, row.comment].filter(Boolean).some((value) => regex.test(String(value)))); + // Object type labels ("TABLE", "VIEW", "PROCEDURE", ...) are deliberately + // not searchable: a query like "TAB" would otherwise match every TABLE row + // via its type label (issue #6488). The type filter buttons cover type + // scoping, and every other search path (sidebar tree, backend metadata) + // matches names and comments only. + return rows.filter((row) => [row.displayName, row.name, row.comment].filter(Boolean).some((value) => regex.test(String(value)))); } - return rows.filter((row) => [row.displayName, row.name, row.type, row.comment].filter(Boolean).some((value) => String(value).toLowerCase().includes(q))); + return rows.filter((row) => [row.displayName, row.name, row.comment].filter(Boolean).some((value) => String(value).toLowerCase().includes(q))); } export function countObjectBrowserRowsByFilter(rows: ObjectBrowserRow[]): ObjectBrowserFilterCounts { diff --git a/packages/app-tests/objectBrowserRows.test.ts b/packages/app-tests/objectBrowserRows.test.ts index fe00aec7aa..2b99652084 100644 --- a/packages/app-tests/objectBrowserRows.test.ts +++ b/packages/app-tests/objectBrowserRows.test.ts @@ -94,7 +94,7 @@ test("object browser rows normalize space separated materialized views", () => { ); }); -test("object browser search matches names, types, and comments but not schema names", () => { +test("object browser search matches names and comments but not schema names", () => { const rows = buildObjectBrowserRows({ objects: [ { name: "users", object_type: "TABLE", schema: "exam_hub", comment: "account records" }, @@ -112,6 +112,37 @@ test("object browser search matches names, types, and comments but not schema na ); }); +test("object browser search matches names, never object type labels", () => { + const rows = buildObjectBrowserRows({ + objects: [ + { name: "tab_history", object_type: "TABLE", schema: "public" }, + { name: "user_accounts", object_type: "TABLE", schema: "public", comment: "user account records" }, + { name: "v_sales_summary", object_type: "VIEW", schema: "public" }, + { name: "refresh_sales", object_type: "PROCEDURE", schema: "public" }, + ], + database: "app", + fallbackSchema: "public", + needsSchema: true, + }); + + const names = (query: string) => filterObjectBrowserRows(rows, query).map((row) => row.name); + + // "TAB" must not surface every TABLE row through its type label (issue #6488). + assert.deepEqual(names("TAB"), ["tab_history"]); + assert.deepEqual(names("tab"), ["tab_history"]); + assert.deepEqual(names("Tab"), ["tab_history"]); + assert.deepEqual(names("TABLE"), []); + assert.deepEqual(names("VIEW"), []); + // Name prefix and middle-substring matching keep working. + assert.deepEqual(names("sales"), ["v_sales_summary", "refresh_sales"]); + assert.deepEqual(names("history"), ["tab_history"]); + // Comment matching keeps working. + assert.deepEqual(names("accounts"), ["user_accounts"]); + // Non-existent keywords return nothing; empty queries keep everything. + assert.deepEqual(names("TAB1"), []); + assert.equal(names(" ").length, rows.length); +}); + test("object browser search supports slash-delimited regular expression queries", () => { const rows = buildObjectBrowserRows({ objects: [