mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 03:25:05 +00:00
fix(quick-open): index grouped database objects
This commit is contained in:
@@ -1394,6 +1394,12 @@ async function handleQuickOpenSelect(item: any) {
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (item.type === "schema") {
|
||||
const dbNode = findTreeNodeById(connectionStore.treeNodes, `${item.connectionId}:${item.database}`);
|
||||
if (dbNode && !dbNode.isExpanded) await connectionStore.loadSchemas(item.connectionId, item.database);
|
||||
const schemaNode = findTreeNodeById(connectionStore.treeNodes, `${item.connectionId}:${item.database}:${item.schema}`);
|
||||
if (schemaNode && !schemaNode.isExpanded) await connectionStore.loadTables(item.connectionId, item.database, item.schema);
|
||||
return;
|
||||
} else if (item.type === "table" || item.type === "view" || item.type === "materialized_view") {
|
||||
// Open the table/view in a data tab
|
||||
await openTableTarget({
|
||||
|
||||
@@ -89,6 +89,8 @@ function getTypeLabel(type: string): string {
|
||||
return t("common.connection");
|
||||
case "database":
|
||||
return t("common.database");
|
||||
case "schema":
|
||||
return t("common.schema");
|
||||
case "table":
|
||||
return t("common.table");
|
||||
case "view":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, beforeEach, vi } from "vitest";
|
||||
import { useQuickOpen, type QuickOpenItem } from "@/composables/useQuickOpen";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { useQuickOpen } from "@/composables/useQuickOpen";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
|
||||
vi.mock("@/stores/connectionStore", () => ({
|
||||
@@ -110,6 +110,102 @@ describe("useQuickOpen", () => {
|
||||
});
|
||||
|
||||
describe("filtering and searching", () => {
|
||||
it("indexes database objects under connection groups", () => {
|
||||
const mockStore = {
|
||||
connections: [{ id: "conn1", name: "Grouped PG", db_type: "postgres" }],
|
||||
treeNodes: [
|
||||
{
|
||||
id: "group1",
|
||||
type: "connection-group",
|
||||
label: "Production",
|
||||
children: [
|
||||
{
|
||||
id: "conn1",
|
||||
connectionId: "conn1",
|
||||
type: "connection",
|
||||
label: "Grouped PG",
|
||||
children: [
|
||||
{
|
||||
id: "conn1:postgres",
|
||||
connectionId: "conn1",
|
||||
type: "database",
|
||||
database: "postgres",
|
||||
label: "postgres",
|
||||
children: [
|
||||
{
|
||||
id: "conn1:postgres:public",
|
||||
connectionId: "conn1",
|
||||
type: "schema",
|
||||
database: "postgres",
|
||||
schema: "public",
|
||||
label: "public",
|
||||
children: [
|
||||
{
|
||||
id: "conn1:postgres:public:users",
|
||||
connectionId: "conn1",
|
||||
type: "table",
|
||||
database: "postgres",
|
||||
schema: "public",
|
||||
label: "users",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
vi.mocked(useConnectionStore).mockReturnValue(mockStore as any);
|
||||
|
||||
const { filteredItems, setQuery } = useQuickOpen();
|
||||
|
||||
setQuery("users");
|
||||
expect(filteredItems.value.map((item) => item.label)).toContain("users");
|
||||
});
|
||||
|
||||
it("includes schemas as quick open results", () => {
|
||||
const mockStore = {
|
||||
connections: [{ id: "conn1", name: "PG", db_type: "postgres" }],
|
||||
treeNodes: [
|
||||
{
|
||||
id: "conn1",
|
||||
connectionId: "conn1",
|
||||
type: "connection",
|
||||
label: "PG",
|
||||
children: [
|
||||
{
|
||||
id: "conn1:postgres",
|
||||
connectionId: "conn1",
|
||||
type: "database",
|
||||
database: "postgres",
|
||||
label: "postgres",
|
||||
children: [
|
||||
{
|
||||
id: "conn1:postgres:analytics",
|
||||
connectionId: "conn1",
|
||||
type: "schema",
|
||||
database: "postgres",
|
||||
schema: "analytics",
|
||||
label: "analytics",
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
vi.mocked(useConnectionStore).mockReturnValue(mockStore as any);
|
||||
|
||||
const { filteredItems, setQuery } = useQuickOpen();
|
||||
|
||||
setQuery("analytics");
|
||||
expect(filteredItems.value).toEqual(expect.arrayContaining([expect.objectContaining({ type: "schema", label: "analytics" })]));
|
||||
});
|
||||
|
||||
it("should filter items based on search query", () => {
|
||||
const mockStore = {
|
||||
connections: [
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useConnectionStore } from "@/stores/connectionStore";
|
||||
|
||||
export interface QuickOpenItem {
|
||||
id: string;
|
||||
type: "connection" | "database" | "table" | "view" | "materialized_view" | "procedure" | "function" | "sequence" | "package" | "package-body";
|
||||
type: "connection" | "database" | "schema" | "table" | "view" | "materialized_view" | "procedure" | "function" | "sequence" | "package" | "package-body";
|
||||
label: string;
|
||||
description?: string;
|
||||
connectionId: string;
|
||||
@@ -87,7 +87,9 @@ export function useQuickOpen() {
|
||||
// Add databases and tables from tree nodes
|
||||
// Filter tree nodes by connection
|
||||
for (const conn of connections) {
|
||||
const connectionTreeNodes = treeNodes.filter((node) => node.connectionId === conn.id);
|
||||
// Connections may live under sidebar groups, so locate their tree recursively.
|
||||
const connectionTreeNode = findConnectionTreeNode(treeNodes, conn.id);
|
||||
const connectionTreeNodes = connectionTreeNode?.children || treeNodes.filter((node) => node.connectionId === conn.id);
|
||||
if (connectionTreeNodes.length === 0) continue;
|
||||
|
||||
// Process tree nodes to extract databases and tables
|
||||
@@ -121,9 +123,20 @@ export function useQuickOpen() {
|
||||
});
|
||||
}
|
||||
|
||||
// Schema nodes - skip them but process their children
|
||||
if (node.type === "schema" && node.children) {
|
||||
processDatabaseTreeNodes(node.children, conn, items);
|
||||
// Schema nodes are navigable results and also contain database objects.
|
||||
if (node.type === "schema" && node.database && node.schema) {
|
||||
items.push({
|
||||
id: `schema-${conn.id}-${node.database}-${node.schema}`,
|
||||
type: "schema",
|
||||
label: node.label || node.schema,
|
||||
description: `${conn.name} / ${node.database}`,
|
||||
connectionId: conn.id,
|
||||
database: node.database,
|
||||
schema: node.schema,
|
||||
connectionName: conn.name,
|
||||
searchText: `${conn.name} ${node.database} ${node.schema}`,
|
||||
});
|
||||
if (node.children) processDatabaseTreeNodes(node.children, conn, items);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -262,6 +275,17 @@ export function useQuickOpen() {
|
||||
}
|
||||
}
|
||||
|
||||
function findConnectionTreeNode(nodes: any[], connectionId: string): any | undefined {
|
||||
for (const node of nodes) {
|
||||
if (node.type === "connection" && node.connectionId === connectionId) return node;
|
||||
if (node.children) {
|
||||
const match = findConnectionTreeNode(node.children, connectionId);
|
||||
if (match) return match;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const filteredItems = computed((): MatchedItem[] => {
|
||||
if (!searchQuery.value.trim()) {
|
||||
return allItems.value.map((item) => ({
|
||||
@@ -293,16 +317,17 @@ export function useQuickOpen() {
|
||||
const typeOrder = {
|
||||
connection: 0,
|
||||
database: 1,
|
||||
table: 2,
|
||||
view: 3,
|
||||
materialized_view: 4,
|
||||
procedure: 5,
|
||||
function: 6,
|
||||
sequence: 7,
|
||||
package: 8,
|
||||
"package-body": 9,
|
||||
schema: 2,
|
||||
table: 3,
|
||||
view: 4,
|
||||
materialized_view: 5,
|
||||
procedure: 6,
|
||||
function: 7,
|
||||
sequence: 8,
|
||||
package: 9,
|
||||
"package-body": 10,
|
||||
};
|
||||
return (typeOrder[a.type] ?? 10) - (typeOrder[b.type] ?? 10);
|
||||
return typeOrder[a.type] - typeOrder[b.type];
|
||||
});
|
||||
|
||||
return matched;
|
||||
|
||||
Reference in New Issue
Block a user