fix(sql): improve completion ranking and fuzzy table lookup
This commit is contained in:
1 parent
ce411f27e9
commit
4b00bf8215
3 files changed
+205
-62
No files matched your search
@@ -759,8 +759,9 @@ export function shouldAutoOpenSqlCompletion(sql: string, cursor: number): boolea
|
||||
}
|
||||
|
||||
export function getSqlCompletionResultValidFor(sql: string, cursor: number): RegExp | undefined {
|
||||
const context = getSqlCompletionContext(sql, cursor);
|
||||
return context.suggestTables ? undefined : /^[\w$]*$/;
|
||||
void sql;
|
||||
void cursor;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getSqlFunctionSignatureHelp(sql: string, cursor: number): SqlFunctionSignatureHelp | null {
|
||||
@@ -1487,13 +1488,14 @@ function unquoteIdentifier(value: string): string {
|
||||
function buildTableItems(prefix: string, tables: SqlCompletionTable[]): SqlCompletionItem[] {
|
||||
return tables
|
||||
.filter((table) => matchesPrefix(table.name, prefix))
|
||||
.slice(0, MAX_TABLE_COMPLETION_ITEMS)
|
||||
.map((table) => ({
|
||||
label: table.name,
|
||||
type: "table" as const,
|
||||
detail: table.schema ? `${table.schema}.${table.name}` : table.type,
|
||||
boost: computeBoost(table.name, prefix) + 1000,
|
||||
}));
|
||||
}))
|
||||
.sort(compareCompletionItems)
|
||||
.slice(0, MAX_TABLE_COMPLETION_ITEMS);
|
||||
}
|
||||
|
||||
function buildSchemaItems(prefix: string, schemas: string[]): SqlCompletionItem[] {
|
||||
@@ -1773,7 +1775,8 @@ function buildColumnItems(
|
||||
detail: buildColumnDetail(column),
|
||||
boost: computeBoost(column.displayLabel, context.prefix) + keyBoost,
|
||||
};
|
||||
});
|
||||
})
|
||||
.sort(compareCompletionItems);
|
||||
}
|
||||
|
||||
function isKeyColumn(name: string): boolean {
|
||||
@@ -2126,18 +2129,18 @@ function getHistoryBoost(label: string, type: string): number {
|
||||
|
||||
function dedupeAndSort(items: SqlCompletionItem[]): SqlCompletionItem[] {
|
||||
const seen = new Set<string>();
|
||||
return items
|
||||
.sort((left, right) => {
|
||||
const leftBonus = getHistoryBoost(left.label, left.type);
|
||||
const rightBonus = getHistoryBoost(right.label, right.type);
|
||||
return right.boost + rightBonus - (left.boost + leftBonus);
|
||||
})
|
||||
.filter((item) => {
|
||||
const key = `${item.type}:${item.label}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
return items.sort(compareCompletionItems).filter((item) => {
|
||||
const key = `${item.type}:${item.label}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function compareCompletionItems(left: SqlCompletionItem, right: SqlCompletionItem): number {
|
||||
const leftBonus = getHistoryBoost(left.label, left.type);
|
||||
const rightBonus = getHistoryBoost(right.label, right.type);
|
||||
return right.boost + rightBonus - (left.boost + leftBonus);
|
||||
}
|
||||
|
||||
function findActiveFunctionOpenParen(sqlBeforeCursor: string): number | null {
|
||||
|
||||
@@ -1408,6 +1408,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
schema?: string,
|
||||
): Promise<SqlCompletionTable[]> {
|
||||
const normalizedFilter = filter.trim().toLowerCase();
|
||||
const relaxedFilter = relaxedCompletionTableFilter(normalizedFilter);
|
||||
const cacheKey = `${connectionId}:${database}:${normalizedFilter}:${limit ?? ""}:${schema ?? ""}`;
|
||||
if (completionTablesCache.value[cacheKey]) {
|
||||
return completionTablesCache.value[cacheKey];
|
||||
@@ -1440,7 +1441,37 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
results.push(...group);
|
||||
}
|
||||
}
|
||||
const limitedTables = limit ? results.slice(0, limit) : results;
|
||||
if (results.length === 0 && relaxedFilter) {
|
||||
for (let i = 0; i < schemas.length && results.length < (limit ?? Infinity); i += batchSize) {
|
||||
const batch = schemas.slice(i, i + batchSize);
|
||||
const batchResults = await Promise.all(
|
||||
batch.map(async (s) => {
|
||||
try {
|
||||
const tables = await api.listTables(
|
||||
connectionId,
|
||||
database,
|
||||
s,
|
||||
relaxedFilter,
|
||||
expandedCompletionLimit(limit),
|
||||
);
|
||||
return tables.map((table) => ({
|
||||
name: table.name,
|
||||
schema: s,
|
||||
type: table.table_type === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
})) as SqlCompletionTable[];
|
||||
} catch {
|
||||
return [] as SqlCompletionTable[];
|
||||
}
|
||||
}),
|
||||
);
|
||||
for (const group of batchResults) {
|
||||
results.push(...group);
|
||||
}
|
||||
}
|
||||
}
|
||||
const limitedTables = limit
|
||||
? dedupeCompletionTables(results).slice(0, expandedCompletionLimit(limit))
|
||||
: results;
|
||||
completionTablesCache.value[cacheKey] = limitedTables;
|
||||
evictOldestCacheEntries(completionTablesCache.value, COMPLETION_CACHE_MAX);
|
||||
return completionTablesCache.value[cacheKey];
|
||||
@@ -1465,7 +1496,10 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
return completionTablesCache.value[cacheKey];
|
||||
}
|
||||
|
||||
const tables = await api.listTables(connectionId, database, database, normalizedFilter, limit);
|
||||
let tables = await api.listTables(connectionId, database, database, normalizedFilter, limit);
|
||||
if (tables.length === 0 && relaxedFilter) {
|
||||
tables = await api.listTables(connectionId, database, database, relaxedFilter, expandedCompletionLimit(limit));
|
||||
}
|
||||
completionTablesCache.value[cacheKey] = tables.map((table) => ({
|
||||
name: table.name,
|
||||
type: table.table_type === "VIEW" ? ("view" as const) : ("table" as const),
|
||||
@@ -1474,6 +1508,28 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
return completionTablesCache.value[cacheKey];
|
||||
}
|
||||
|
||||
function relaxedCompletionTableFilter(filter: string): string | undefined {
|
||||
if (filter.length < 3) return undefined;
|
||||
return filter.slice(0, 2);
|
||||
}
|
||||
|
||||
function expandedCompletionLimit(limit?: number): number | undefined {
|
||||
if (!limit) return limit;
|
||||
return Math.min(Math.max(limit * 3, limit), 1000);
|
||||
}
|
||||
|
||||
function dedupeCompletionTables(tables: SqlCompletionTable[]): SqlCompletionTable[] {
|
||||
const seen = new Set<string>();
|
||||
const deduped: SqlCompletionTable[] = [];
|
||||
for (const table of tables) {
|
||||
const key = `${table.schema ?? ""}.${table.name}`.toLowerCase();
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
deduped.push(table);
|
||||
}
|
||||
return deduped;
|
||||
}
|
||||
|
||||
async function listCompletionColumns(
|
||||
connectionId: string,
|
||||
database: string,
|
||||
|
||||
Reference in new issue
Block a user