feat(sidebar): add local table search

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-07-05 00:09:04 +08:00
1 parent 8a1a791927
commit fdaa949fb1
15 files changed
+421 -17

No files matched your search

+64 -8
View File
@@ -165,6 +165,9 @@ export type TreeClipboard =
interface LoadTreeOptions {
force?: boolean;
expectedSidebarSearchQuery?: string;
searchFilter?: string;
sidebarTableSearchParentId?: string;
expectedSidebarTableSearchQuery?: string;
}
interface PersistedTreeChildrenLoadResult {
@@ -218,6 +221,7 @@ export const useConnectionStore = defineStore("connection", () => {
const mongoCompletionFieldsCache = ref<Record<string, MongoCompletionField[]>>({});
const schemaListCache = ref<Record<string, string[]>>({});
const sidebarSearchQuery = ref("");
const sidebarTableSearchQueries = ref<Record<string, string>>({});
const completionTableIndex = new Map<string, { touched: number; tables: SqlCompletionTable[] }>();
const completionObjectIndex = new Map<string, { touched: number; objects: SqlCompletionObject[] }>();
const completionColumnIndex = new Map<string, { touched: number; columns: SqlCompletionColumn[] }>();
@@ -974,7 +978,7 @@ export const useConnectionStore = defineStore("connection", () => {
if (!options.node.connectionId || !options.node.database) {
return { children: [], objectCount: 0, hasMore: false, nextOffset: options.offset };
}
const searchFilter = options.searchFilter || sidebarSearchQuery.value || undefined;
const searchFilter = (options.searchFilter ?? sidebarSearchQuery.value) || undefined;
const fetchLimit = searchFilter ? options.pageSize : options.pageSize + 1;
const tables = await api.listTables(options.node.connectionId, options.node.database, options.querySchema, searchFilter, fetchLimit, searchFilter ? undefined : options.offset, options.objectTypes);
const hasMore = searchFilter ? false : tables.length > options.pageSize;
@@ -1007,7 +1011,7 @@ export const useConnectionStore = defineStore("connection", () => {
pageSize: number;
searchFilter?: string;
}): Promise<{ children: TreeNode[]; objectCount: number; hasMore: boolean; nextOffset: number }> {
const searchFilter = options.searchFilter || sidebarSearchQuery.value || undefined;
const searchFilter = (options.searchFilter ?? sidebarSearchQuery.value) || undefined;
const fetchLimit = searchFilter ? options.pageSize : options.pageSize + 1;
const tables = await api.listTables(options.connectionId, options.database, options.querySchema, searchFilter, fetchLimit, searchFilter ? undefined : options.offset);
const hasMore = searchFilter ? false : tables.length > options.pageSize;
@@ -1110,6 +1114,20 @@ export const useConnectionStore = defineStore("connection", () => {
return options?.expectedSidebarSearchQuery !== undefined && (sidebarSearchQuery.value || "") !== options.expectedSidebarSearchQuery;
}
function isSidebarTableSearchQueryChanged(options?: LoadTreeOptions) {
if (!options?.sidebarTableSearchParentId || options.expectedSidebarTableSearchQuery === undefined) return false;
return (sidebarTableSearchQueries.value[options.sidebarTableSearchParentId]?.trim() || "") !== options.expectedSidebarTableSearchQuery;
}
function activeTreeLoadSearchFilter(options?: LoadTreeOptions): string {
return (options?.searchFilter ?? sidebarSearchQuery.value) || "";
}
function isTreeLoadSearchChanged(searchFilter: string, options?: LoadTreeOptions): boolean {
if (options?.sidebarTableSearchParentId) return isSidebarTableSearchQueryChanged(options);
return (sidebarSearchQuery.value || "") !== searchFilter || isSidebarSearchQueryChanged(options);
}
function isTreeNodeChildrenLoaded(nodeId: string): boolean {
return loadedTreeNodeChildrenIds.value.has(nodeId);
}
@@ -2350,7 +2368,8 @@ export const useConnectionStore = defineStore("connection", () => {
if (useCachedChildren(node, options)) return;
const simpleObjectDisplay = useSettingsStore().editorSettings.sidebarObjectDisplay === "simple";
const cacheKey = schemaCacheKey(connectionId, database, schema || "", simpleObjectDisplay ? "objects-simple-v3" : "objects-grouped-v3");
const searchFilter = sidebarSearchQuery.value || "";
const searchFilter = activeTreeLoadSearchFilter(options);
const isSidebarTableSearch = !!options?.sidebarTableSearchParentId;
if (!options?.force && !searchFilter) {
const cached = await loadPersistedTreeChildren(node, cacheKey);
if (cached.hit) {
@@ -2388,9 +2407,9 @@ export const useConnectionStore = defineStore("connection", () => {
objectTypes: supportedSidebarObjectTypes(config),
});
}
if ((sidebarSearchQuery.value || "") !== searchFilter || isSidebarSearchQueryChanged(options)) return;
if (isTreeLoadSearchChanged(searchFilter, options)) return;
setChildren(node, children);
if (!searchFilter) {
if (!searchFilter && !isSidebarTableSearch) {
await savePersistedTreeChildren(cacheKey, children);
}
node.isExpanded = true;
@@ -2416,7 +2435,8 @@ export const useConnectionStore = defineStore("connection", () => {
const querySchema = connectionObjectTreeQuerySchema(config, node.database, node.schema);
const effectiveSchema = connectionObjectTreeNodeSchema(config, node.database, node.schema);
const cacheKey = objectGroupCacheKey(node);
const searchFilter = sidebarSearchQuery.value || "";
const searchFilter = activeTreeLoadSearchFilter(options);
const isSidebarTableSearch = !!options?.sidebarTableSearchParentId;
if (!options?.force && !searchFilter) {
const cached = await loadPersistedTreeChildren(node, cacheKey);
if (cached.hit) {
@@ -2451,9 +2471,9 @@ export const useConnectionStore = defineStore("connection", () => {
});
node.objectCount = children.length;
}
if ((sidebarSearchQuery.value || "") !== searchFilter || isSidebarSearchQueryChanged(options)) return;
if (isTreeLoadSearchChanged(searchFilter, options)) return;
setChildren(node, children);
if (!searchFilter) {
if (!searchFilter && !isSidebarTableSearch) {
await savePersistedTreeChildren(cacheKey, children);
}
node.isExpanded = true;
@@ -2623,6 +2643,39 @@ export const useConnectionStore = defineStore("connection", () => {
}
}
function setSidebarTableSearchQuery(parentNodeId: string, query: string) {
const normalized = query.trim();
const next = { ...sidebarTableSearchQueries.value };
if (normalized) {
next[parentNodeId] = query;
} else {
delete next[parentNodeId];
}
sidebarTableSearchQueries.value = next;
}
async function refreshSidebarTableSearch(parentNodeId: string) {
const parent = findNode(treeNodes.value, parentNodeId);
if (!parent?.connectionId || !hasTreeNodeDatabaseContext(parent)) return;
const searchFilter = sidebarTableSearchQueries.value[parentNodeId]?.trim() || "";
const options: LoadTreeOptions = {
force: true,
searchFilter: searchFilter || undefined,
sidebarTableSearchParentId: parentNodeId,
expectedSidebarTableSearchQuery: searchFilter,
};
if (parent.type === "group-tables") {
await loadObjectGroupChildren(parent, options);
return;
}
if (parent.type === "database" || parent.type === "schema" || parent.type === "linked-server-schema") {
await loadTables(parent.connectionId, parent.database, parent.schema, options);
}
}
function normalizedObjectTreeKind(type: string): DatabaseObjectTreeKind {
return normalizeSidebarObjectKind(type);
}
@@ -4313,6 +4366,9 @@ export const useConnectionStore = defineStore("connection", () => {
databaseSearchSource,
databaseExportSource,
sidebarSearchQuery,
sidebarTableSearchQueries,
setSidebarTableSearchQuery,
refreshSidebarTableSearch,
createConnectionGroup(name: string, parentGroupId?: string | null) {
const result = createGroupOp(sidebarLayout.value, name, parentGroupId);
updateLayoutAndRebuild(result.layout);