diff --git a/crates/codegraph-core/src/structure.rs b/crates/codegraph-core/src/structure.rs index 936c0882f..ce5609640 100644 --- a/crates/codegraph-core/src/structure.rs +++ b/crates/codegraph-core/src/structure.rs @@ -589,6 +589,25 @@ fn compute_file_metrics( } } + // Batch-load import counts per file from DB (distinct imported files, + // matching the fast-path semantics in update_changed_file_metrics) + let mut import_counts: HashMap = HashMap::new(); + if let Ok(mut stmt) = tx.prepare( + "SELECT n1.file, COUNT(DISTINCT n2.file) FROM edges e \ + JOIN nodes n1 ON e.source_id = n1.id \ + JOIN nodes n2 ON e.target_id = n2.id \ + WHERE e.kind = 'imports' \ + GROUP BY n1.file", + ) { + if let Ok(rows) = stmt.query_map([], |row| { + Ok((row.get::<_, String>(0)?, row.get::<_, i64>(1)?)) + }) { + for row in rows.flatten() { + import_counts.insert(row.0, row.1); + } + } + } + { let mut upsert = match tx.prepare( "INSERT OR REPLACE INTO node_metrics \ @@ -607,7 +626,7 @@ fn compute_file_metrics( let line_count = line_count_map.get(rel_path).copied().unwrap_or(0); let symbol_count = symbol_counts.get(rel_path).copied().unwrap_or(0); - let import_count = symbols.imports.len() as i64; + let import_count = import_counts.get(rel_path).copied().unwrap_or(0); let export_count = symbols.exports.len() as i64; let fan_in = fan_in_map.get(rel_path).copied().unwrap_or(0); let fan_out = fan_out_map.get(rel_path).copied().unwrap_or(0); diff --git a/src/features/structure.ts b/src/features/structure.ts index 111bcec4c..8fe6b5a9b 100644 --- a/src/features/structure.ts +++ b/src/features/structure.ts @@ -166,6 +166,22 @@ function computeFileMetrics( fanOutMap: Map, ): void { db.transaction(() => { + // Batch-load import counts per file (distinct imported files, + // matching the fast-path semantics in updateChangedFileMetrics). + // Runs inside the transaction for parity with the Rust path. + const importCountMap = new Map(); + for (const row of db + .prepare( + `SELECT n1.file AS src, COUNT(DISTINCT n2.file) AS cnt FROM edges e + JOIN nodes n1 ON e.source_id = n1.id + JOIN nodes n2 ON e.target_id = n2.id + WHERE e.kind = 'imports' + GROUP BY n1.file`, + ) + .all() as { src: string; cnt: number }[]) { + importCountMap.set(row.src, row.cnt); + } + for (const [relPath, symbols] of fileSymbols) { const fileRow = getNodeIdStmt.get(relPath, 'file', relPath, 0); if (!fileRow) continue; @@ -180,7 +196,7 @@ function computeFileMetrics( symbolCount++; } } - const importCount = symbols.imports.length; + const importCount = importCountMap.get(relPath) || 0; const exportCount = symbols.exports.length; const fanIn = fanInMap.get(relPath) || 0; const fanOut = fanOutMap.get(relPath) || 0;