Remove unused batch methods, dynamic stmt cache, and intent field
- Remove getNodesByIds, getNodesByKinds (zero callers) - Remove getFileHashMap, getFileSyncMap (zero callers) - Remove getDynamicStmt cache (only used by removed batch methods) - Revert getStaleFiles to simple implementation (zero callers, no need to optimize) - Remove intent field from SearchOptions (never read in search logic)
This commit is contained in:
+7
-139
@@ -173,30 +173,10 @@ export class QueryBuilder {
|
|||||||
getUnresolvedByName?: Database.Statement;
|
getUnresolvedByName?: Database.Statement;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
// Cache for dynamically-built prepared statements keyed by SQL shape.
|
|
||||||
private dynamicStmtCache = new Map<string, Database.Statement>();
|
|
||||||
private readonly maxDynamicStmtCacheSize = 50;
|
|
||||||
|
|
||||||
constructor(db: Database.Database) {
|
constructor(db: Database.Database) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or create a cached prepared statement for dynamically-built SQL.
|
|
||||||
*/
|
|
||||||
private getDynamicStmt(cacheKey: string, sql: string): Database.Statement {
|
|
||||||
let stmt = this.dynamicStmtCache.get(cacheKey);
|
|
||||||
if (stmt) return stmt;
|
|
||||||
|
|
||||||
if (this.dynamicStmtCache.size >= this.maxDynamicStmtCacheSize) {
|
|
||||||
this.dynamicStmtCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt = this.db.prepare(sql);
|
|
||||||
this.dynamicStmtCache.set(cacheKey, stmt);
|
|
||||||
return stmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Node Operations
|
// Node Operations
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -416,11 +396,10 @@ export class QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the node cache and dynamic statement cache
|
* Clear the node cache
|
||||||
*/
|
*/
|
||||||
clearCache(): void {
|
clearCache(): void {
|
||||||
this.nodeCache.clear();
|
this.nodeCache.clear();
|
||||||
this.dynamicStmtCache.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -455,56 +434,6 @@ export class QueryBuilder {
|
|||||||
return rows.map(rowToNode);
|
return rows.map(rowToNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all nodes matching any of the given kinds in a single query
|
|
||||||
*/
|
|
||||||
getNodesByKinds(kinds: NodeKind[]): Node[] {
|
|
||||||
if (kinds.length === 0) return [];
|
|
||||||
const placeholders = kinds.map(() => '?').join(',');
|
|
||||||
const sql = `SELECT * FROM nodes WHERE kind IN (${placeholders})`;
|
|
||||||
const cacheKey = `nodesByKinds:${kinds.length}`;
|
|
||||||
const stmt = this.getDynamicStmt(cacheKey, sql);
|
|
||||||
const rows = stmt.all(...kinds) as NodeRow[];
|
|
||||||
return rows.map(rowToNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get multiple nodes by their IDs in a single batch query
|
|
||||||
*/
|
|
||||||
getNodesByIds(ids: string[]): Map<string, Node> {
|
|
||||||
const result = new Map<string, Node>();
|
|
||||||
if (ids.length === 0) return result;
|
|
||||||
|
|
||||||
const missing: string[] = [];
|
|
||||||
for (const id of ids) {
|
|
||||||
if (this.nodeCache.has(id)) {
|
|
||||||
const cached = this.nodeCache.get(id)!;
|
|
||||||
// LRU touch
|
|
||||||
this.nodeCache.delete(id);
|
|
||||||
this.nodeCache.set(id, cached);
|
|
||||||
result.set(id, cached);
|
|
||||||
} else {
|
|
||||||
missing.push(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Batch fetch missing nodes, chunking at 999 params (SQLite limit)
|
|
||||||
const CHUNK_SIZE = 999;
|
|
||||||
for (let i = 0; i < missing.length; i += CHUNK_SIZE) {
|
|
||||||
const chunk = missing.slice(i, i + CHUNK_SIZE);
|
|
||||||
const placeholders = chunk.map(() => '?').join(',');
|
|
||||||
const sql = `SELECT * FROM nodes WHERE id IN (${placeholders})`;
|
|
||||||
const rows = this.db.prepare(sql).all(...chunk) as NodeRow[];
|
|
||||||
for (const row of rows) {
|
|
||||||
const node = rowToNode(row);
|
|
||||||
this.cacheNode(node);
|
|
||||||
result.set(node.id, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search nodes by name using FTS with fallback to LIKE for better matching
|
* Search nodes by name using FTS with fallback to LIKE for better matching
|
||||||
*
|
*
|
||||||
@@ -854,75 +783,14 @@ export class QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get files that need re-indexing (hash changed).
|
* Get files that need re-indexing (hash changed)
|
||||||
*
|
|
||||||
* Uses a temporary table + JOIN so the filtering happens entirely in SQL
|
|
||||||
* instead of loading every FileRecord into JS and comparing in a loop.
|
|
||||||
*/
|
*/
|
||||||
getStaleFiles(currentHashes: Map<string, string>): FileRecord[] {
|
getStaleFiles(currentHashes: Map<string, string>): FileRecord[] {
|
||||||
if (currentHashes.size === 0) return [];
|
const files = this.getAllFiles();
|
||||||
|
return files.filter((f) => {
|
||||||
this.db.exec(`
|
const currentHash = currentHashes.get(f.path);
|
||||||
CREATE TEMP TABLE IF NOT EXISTS _current_hashes (
|
return currentHash && currentHash !== f.contentHash;
|
||||||
path TEXT PRIMARY KEY,
|
});
|
||||||
content_hash TEXT NOT NULL
|
|
||||||
)
|
|
||||||
`);
|
|
||||||
this.db.exec('DELETE FROM _current_hashes');
|
|
||||||
|
|
||||||
const insertHash = this.db.prepare(
|
|
||||||
'INSERT INTO _current_hashes (path, content_hash) VALUES (?, ?)'
|
|
||||||
);
|
|
||||||
this.db.transaction(() => {
|
|
||||||
for (const [filePath, hash] of currentHashes) {
|
|
||||||
insertHash.run(filePath, hash);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
const rows = this.db.prepare(`
|
|
||||||
SELECT f.*
|
|
||||||
FROM files f
|
|
||||||
INNER JOIN _current_hashes ch ON f.path = ch.path
|
|
||||||
WHERE f.content_hash != ch.content_hash
|
|
||||||
`).all() as FileRow[];
|
|
||||||
|
|
||||||
this.db.exec('DELETE FROM _current_hashes');
|
|
||||||
|
|
||||||
return rows.map(rowToFileRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a lightweight map of tracked file paths to their content hashes.
|
|
||||||
*/
|
|
||||||
getFileHashMap(): Map<string, string> {
|
|
||||||
const rows = this.db.prepare(
|
|
||||||
'SELECT path, content_hash FROM files'
|
|
||||||
).all() as Array<{ path: string; content_hash: string }>;
|
|
||||||
|
|
||||||
const map = new Map<string, string>();
|
|
||||||
for (const row of rows) {
|
|
||||||
map.set(row.path, row.content_hash);
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a lightweight map of tracked file paths to mtime + size + hash.
|
|
||||||
*/
|
|
||||||
getFileSyncMap(): Map<string, { contentHash: string; modifiedAt: number; size: number }> {
|
|
||||||
const rows = this.db.prepare(
|
|
||||||
'SELECT path, content_hash, modified_at, size FROM files'
|
|
||||||
).all() as Array<{ path: string; content_hash: string; modified_at: number; size: number }>;
|
|
||||||
|
|
||||||
const map = new Map<string, { contentHash: string; modifiedAt: number; size: number }>();
|
|
||||||
for (const row of rows) {
|
|
||||||
map.set(row.path, {
|
|
||||||
contentHash: row.content_hash,
|
|
||||||
modifiedAt: row.modified_at,
|
|
||||||
size: row.size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|||||||
@@ -336,9 +336,6 @@ export interface SearchOptions {
|
|||||||
|
|
||||||
/** Whether search is case-sensitive */
|
/** Whether search is case-sensitive */
|
||||||
caseSensitive?: boolean;
|
caseSensitive?: boolean;
|
||||||
|
|
||||||
/** Search intent for biasing results */
|
|
||||||
intent?: 'api' | 'general';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user