widgetdc-cortex / apps /backend /src /mcp /cognitive /GraphTraversalOptimizer.ts
Kraft102's picture
Initial deployment - WidgeTDC Cortex Backend v2.1.0
529090e
import { neo4jService } from '../../database/Neo4jService';
/**
* Advanced Graph Traversal Optimizer
* Optimizes multi-hop graph queries for better performance
*/
export class GraphTraversalOptimizer {
/**
* Optimized breadth-first search with pruning
*/
async optimizedBFS(
startNodeId: string,
targetCondition: (node: any) => boolean,
maxDepth: number = 3,
maxNodes: number = 100
): Promise<{ path: any[]; nodesVisited: number }> {
await neo4jService.connect();
try {
// Use Cypher's built-in path finding with limits
const result = await neo4jService.runQuery(
`MATCH path = (start)-[*1..${maxDepth}]-(end)
WHERE id(start) = $startId
WITH path, nodes(path) as pathNodes
LIMIT $maxNodes
RETURN path, pathNodes`,
{ startId: parseInt(startNodeId), maxNodes }
);
await neo4jService.disconnect();
if (result.length === 0) {
return { path: [], nodesVisited: 0 };
}
// Find best path based on condition
const bestPath = result.find(r => {
const nodes = r.pathNodes;
return nodes.some((node: any) => targetCondition(node));
});
return {
path: bestPath?.pathNodes || [],
nodesVisited: result.length,
};
} catch (error) {
await neo4jService.disconnect();
throw error;
}
}
/**
* Find shortest path with relationship type filtering
*/
async findShortestPath(
startNodeId: string,
endNodeId: string,
relationshipTypes?: string[],
maxDepth: number = 5
): Promise<{ path: any[]; length: number } | null> {
await neo4jService.connect();
try {
const relFilter = relationshipTypes
? `:${relationshipTypes.join('|')}`
: '';
const result = await neo4jService.runQuery(
`MATCH path = shortestPath((start)-[${relFilter}*1..${maxDepth}]-(end))
WHERE id(start) = $startId AND id(end) = $endId
RETURN path, length(path) as pathLength`,
{ startId: parseInt(startNodeId), endId: parseInt(endNodeId) }
);
await neo4jService.disconnect();
if (result.length === 0) return null;
return {
path: result[0].path,
length: result[0].pathLength,
};
} catch (error) {
await neo4jService.disconnect();
throw error;
}
}
/**
* Find all paths with cost optimization
*/
async findAllPathsWithCost(
startNodeId: string,
endNodeId: string,
maxDepth: number = 4,
costProperty: string = 'weight'
): Promise<Array<{ path: any[]; cost: number }>> {
await neo4jService.connect();
try {
const result = await neo4jService.runQuery(
`MATCH path = (start)-[*1..${maxDepth}]-(end)
WHERE id(start) = $startId AND id(end) = $endId
WITH path, relationships(path) as rels
RETURN path,
reduce(cost = 0, r in rels | cost + coalesce(r.${costProperty}, 1)) as totalCost
ORDER BY totalCost ASC
LIMIT 10`,
{ startId: parseInt(startNodeId), endId: parseInt(endNodeId) }
);
await neo4jService.disconnect();
return result.map(r => ({
path: r.path,
cost: r.totalCost,
}));
} catch (error) {
await neo4jService.disconnect();
throw error;
}
}
/**
* Community detection using label propagation
*/
async detectCommunities(minCommunitySize: number = 3): Promise<Map<string, string[]>> {
await neo4jService.connect();
try {
// Simple community detection based on connected components
const result = await neo4jService.runQuery(
`CALL gds.wcc.stream('myGraph')
YIELD nodeId, componentId
WITH componentId, collect(nodeId) as members
WHERE size(members) >= $minSize
RETURN componentId, members`,
{ minSize: minCommunitySize }
);
await neo4jService.disconnect();
const communities = new Map<string, string[]>();
result.forEach(r => {
communities.set(r.componentId.toString(), r.members.map((id: any) => id.toString()));
});
return communities;
} catch (error) {
await neo4jService.disconnect();
// Fallback to simple connected components
return this.simpleConnectedComponents(minCommunitySize);
}
}
/**
* Fallback: Simple connected components without GDS
*/
private async simpleConnectedComponents(minSize: number): Promise<Map<string, string[]>> {
const result = await neo4jService.runQuery(
`MATCH (n)
OPTIONAL MATCH path = (n)-[*]-(m)
WITH n, collect(DISTINCT m) as connected
WHERE size(connected) >= $minSize
RETURN id(n) as nodeId, [x in connected | id(x)] as members
LIMIT 100`,
{ minSize }
);
const communities = new Map<string, string[]>();
result.forEach((r, idx) => {
communities.set(`community_${idx}`, r.members.map((id: any) => id.toString()));
});
return communities;
}
/**
* PageRank-style importance scoring
*/
async computeNodeImportance(dampingFactor: number = 0.85, iterations: number = 20): Promise<Map<string, number>> {
await neo4jService.connect();
try {
// Try using GDS PageRank
const result = await neo4jService.runQuery(
`CALL gds.pageRank.stream('myGraph', {
maxIterations: $iterations,
dampingFactor: $dampingFactor
})
YIELD nodeId, score
RETURN nodeId, score
ORDER BY score DESC
LIMIT 100`,
{ iterations, dampingFactor }
);
await neo4jService.disconnect();
const scores = new Map<string, number>();
result.forEach(r => {
scores.set(r.nodeId.toString(), r.score);
});
return scores;
} catch (error) {
await neo4jService.disconnect();
// Fallback to simple degree centrality
return this.simpleDegreeCentrality();
}
}
/**
* Fallback: Simple degree centrality without GDS
*/
private async simpleDegreeCentrality(): Promise<Map<string, number>> {
const result = await neo4jService.runQuery(
`MATCH (n)-[r]-(m)
WITH n, count(r) as degree
RETURN id(n) as nodeId, degree
ORDER BY degree DESC
LIMIT 100`
);
const scores = new Map<string, number>();
result.forEach(r => {
scores.set(r.nodeId.toString(), r.degree);
});
return scores;
}
}
export const graphTraversalOptimizer = new GraphTraversalOptimizer();