diff --git a/packages/core/src/graph/graph.ts b/packages/core/src/graph/graph.ts index 81be83d..420e750 100644 --- a/packages/core/src/graph/graph.ts +++ b/packages/core/src/graph/graph.ts @@ -165,6 +165,19 @@ export class Graph { }); } + /** + * Iterates over all edges efficiently without creating temporary arrays. + * Useful for performance-sensitive iterations. + */ + forEachEdge(callback: (source: string, target: string, weight: number) => void) { + for (const [edgeKey, weight] of this.edges.entries()) { + const splitIndex = edgeKey.indexOf('\x00'); + const source = edgeKey.slice(0, splitIndex); + const target = edgeKey.slice(splitIndex + 1); + callback(source, target, weight); + } + } + toJSON() { return { nodes: this.getNodes(), diff --git a/packages/core/src/graph/hits.ts b/packages/core/src/graph/hits.ts index 0319ff1..20ac0d2 100644 --- a/packages/core/src/graph/hits.ts +++ b/packages/core/src/graph/hits.ts @@ -48,19 +48,18 @@ export class HITSService { const incoming: { sourceIndex: number, weight: number }[][] = new Array(N).fill(null).map(() => []); const outgoing: { targetIndex: number, weight: number }[][] = new Array(N).fill(null).map(() => []); - const allEdges = graph.getEdges(); - for (const edge of allEdges) { - if (edge.source === edge.target) continue; + graph.forEachEdge((source, target, weight) => { + if (source === target) return; - const sourceIndex = urlToIndex.get(edge.source); - const targetIndex = urlToIndex.get(edge.target); + const sourceIndex = urlToIndex.get(source); + const targetIndex = urlToIndex.get(target); if (sourceIndex !== undefined && targetIndex !== undefined) { - const weight = edge.weight || 1.0; - incoming[targetIndex].push({ sourceIndex, weight }); - outgoing[sourceIndex].push({ targetIndex, weight }); + const w = weight || 1.0; + incoming[targetIndex].push({ sourceIndex, weight: w }); + outgoing[sourceIndex].push({ targetIndex, weight: w }); } - } + }); // Initialize Scores const authScores = new Float64Array(N).fill(1.0); diff --git a/packages/core/src/graph/metrics.ts b/packages/core/src/graph/metrics.ts index 608b163..3250145 100644 --- a/packages/core/src/graph/metrics.ts +++ b/packages/core/src/graph/metrics.ts @@ -23,24 +23,23 @@ export interface Metrics { export function calculateMetrics(graph: Graph, _maxDepth: number): Metrics { const nodes = graph.getNodes(); - const edges = graph.getEdges(); const totalPages = nodes.length; - const totalEdges = edges.length; + const totalEdges = graph.edges.size; // Identify broken nodes const brokenNodes = new Set(nodes.filter(n => n.status >= 400 || n.status === 0).map(n => n.url)); // Pre-compute outgoing edges per node for faster lookup const outgoingEdges = new Map(); - for (const edge of edges) { - let targets = outgoingEdges.get(edge.source); + graph.forEachEdge((source, target) => { + let targets = outgoingEdges.get(source); if (!targets) { targets = []; - outgoingEdges.set(edge.source, targets); + outgoingEdges.set(source, targets); } - targets.push(edge.target); - } + targets.push(target); + }); // Populate brokenLinks per node for (const node of nodes) { diff --git a/packages/core/src/graph/pagerank.ts b/packages/core/src/graph/pagerank.ts index c84a9a8..08ea569 100644 --- a/packages/core/src/graph/pagerank.ts +++ b/packages/core/src/graph/pagerank.ts @@ -33,7 +33,6 @@ export class PageRankService { const neutralScoreWhenFlat = options.neutralScoreWhenFlat ?? 50; const allNodes = graph.getNodes(); - const allEdges = graph.getEdges(); // 1. Filter Eligible Nodes const eligibleNodes = allNodes.filter(node => { @@ -71,16 +70,16 @@ export class PageRankService { const outWeights = new Float64Array(nodeCount); const incoming: { sourceIndex: number, weight: number }[][] = new Array(nodeCount).fill(null).map(() => []); - for (const edge of allEdges) { - const sourceIndex = urlToIndex.get(edge.source); - const targetIndex = urlToIndex.get(edge.target); + graph.forEachEdge((source, target, weight) => { + const sourceIndex = urlToIndex.get(source); + const targetIndex = urlToIndex.get(target); if (sourceIndex !== undefined && targetIndex !== undefined) { - const weight = edge.weight || 1.0; - incoming[targetIndex].push({ sourceIndex, weight }); - outWeights[sourceIndex] += weight; + const w = weight || 1.0; + incoming[targetIndex].push({ sourceIndex, weight: w }); + outWeights[sourceIndex] += w; } - } + }); // Identify sinks const sinks: number[] = [];