Merge pull request #1284 from danielaskdd/main

Add edge size handling for node expansion and change graph to an undirected one
This commit is contained in:
Daniel.y
2025-04-07 01:24:08 +08:00
committed by GitHub
4 changed files with 97 additions and 54 deletions

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
<link rel="icon" type="image/svg+xml" href="logo.png" /> <link rel="icon" type="image/svg+xml" href="logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lightrag</title> <title>Lightrag</title>
<script type="module" crossorigin src="/webui/assets/index-CAYmcaJK.js"></script> <script type="module" crossorigin src="/webui/assets/index-C_A4jxV8.js"></script>
<link rel="stylesheet" crossorigin href="/webui/assets/index-QU59h9JG.css"> <link rel="stylesheet" crossorigin href="/webui/assets/index-QU59h9JG.css">
</head> </head>
<body> <body>

View File

@@ -31,7 +31,7 @@ import '@react-sigma/graph-search/lib/style.css'
const defaultSigmaSettings: Partial<SigmaSettings> = { const defaultSigmaSettings: Partial<SigmaSettings> = {
allowInvalidContainer: true, allowInvalidContainer: true,
defaultNodeType: 'default', defaultNodeType: 'default',
defaultEdgeType: 'curvedArrow', defaultEdgeType: 'curvedNoArrow',
renderEdgeLabels: false, renderEdgeLabels: false,
edgeProgramClasses: { edgeProgramClasses: {
arrow: EdgeArrowProgram, arrow: EdgeArrowProgram,

View File

@@ -1,4 +1,4 @@
import Graph, { DirectedGraph } from 'graphology' import Graph, { UndirectedGraph } from 'graphology'
import { useCallback, useEffect, useRef } from 'react' import { useCallback, useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { errorMessage } from '@/lib/utils' import { errorMessage } from '@/lib/utils'
@@ -20,7 +20,7 @@ const NODE_COLORS = [
'#a64dff', // Purple - technology '#a64dff', // Purple - technology
'#f46a9b', // Magenta '#f46a9b', // Magenta
'#00bfa0', // Turquoise '#00bfa0', // Turquoise
'#fdcce5', // Light Pink '#fdcce5', // Pale Pink
'#0f558a', // Blue - location '#0f558a', // Blue - location
'#b2e061', // Yellow Green '#b2e061', // Yellow Green
'#bd7ebe', // Light Violet - event '#bd7ebe', // Light Violet - event
@@ -40,7 +40,7 @@ const EXTENDED_COLORS = [
'#996600', // Yellow Brown '#996600', // Yellow Brown
'#4421af', // Deep Purple '#4421af', // Deep Purple
'#E67E22', // Carrot '#E67E22', // Carrot
'#e61919', // Light Red '#ff1a1a', // Pure Red
]; ];
// All available colors combined // All available colors combined
@@ -303,7 +303,7 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
} }
// Create new graph instance // Create new graph instance
const graph = new DirectedGraph() const graph = new UndirectedGraph()
// Add nodes from raw graph data // Add nodes from raw graph data
for (const rawNode of rawGraph?.nodes ?? []) { for (const rawNode of rawGraph?.nodes ?? []) {
@@ -329,10 +329,11 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
// Get weight from edge properties or default to 1 // Get weight from edge properties or default to 1
const weight = rawEdge.properties?.weight !== undefined ? Number(rawEdge.properties.weight) : 1 const weight = rawEdge.properties?.weight !== undefined ? Number(rawEdge.properties.weight) : 1
rawEdge.dynamicId = graph.addDirectedEdge(rawEdge.source, rawEdge.target, { rawEdge.dynamicId = graph.addEdge(rawEdge.source, rawEdge.target, {
label: rawEdge.properties?.keywords || undefined, label: rawEdge.properties?.keywords || undefined,
size: weight, // Set initial size based on weight size: weight, // Set initial size based on weight
originalWeight: weight, // Store original weight for recalculation originalWeight: weight, // Store original weight for recalculation
type: 'curvedNoArrow' // Explicitly set edge type to no arrow
}) })
} }
@@ -486,7 +487,7 @@ const useLightrangeGraph = () => {
// Check if data is empty or invalid // Check if data is empty or invalid
if (!data || !data.nodes || data.nodes.length === 0) { if (!data || !data.nodes || data.nodes.length === 0) {
// Create a graph with a single "Graph Is Empty" node // Create a graph with a single "Graph Is Empty" node
const emptyGraph = new DirectedGraph(); const emptyGraph = new UndirectedGraph();
// Add a single node with "Graph Is Empty" label // Add a single node with "Graph Is Empty" label
emptyGraph.addNode('empty-graph-node', { emptyGraph.addNode('empty-graph-node', {
@@ -646,11 +647,24 @@ const useLightrangeGraph = () => {
// Get degree maxDegree from existing graph for size calculations // Get degree maxDegree from existing graph for size calculations
const minDegree = 1; const minDegree = 1;
let maxDegree = 0; let maxDegree = 0;
// Initialize edge weight min and max values
let minWeight = Number.MAX_SAFE_INTEGER;
let maxWeight = 0;
// Calculate node degrees and edge weights from existing graph
sigmaGraph.forEachNode(node => { sigmaGraph.forEachNode(node => {
const degree = sigmaGraph.degree(node); const degree = sigmaGraph.degree(node);
maxDegree = Math.max(maxDegree, degree); maxDegree = Math.max(maxDegree, degree);
}); });
// Calculate edge weights from existing graph
sigmaGraph.forEachEdge(edge => {
const weight = sigmaGraph.getEdgeAttribute(edge, 'originalWeight') || 1;
minWeight = Math.min(minWeight, weight);
maxWeight = Math.max(maxWeight, weight);
});
// First identify connectable nodes (nodes connected to the expanded node) // First identify connectable nodes (nodes connected to the expanded node)
for (const node of processedNodes) { for (const node of processedNodes) {
// Skip if node already exists // Skip if node already exists
@@ -713,7 +727,7 @@ const useLightrangeGraph = () => {
// Helper function to update node sizes // Helper function to update node sizes
const updateNodeSizes = ( const updateNodeSizes = (
sigmaGraph: DirectedGraph, sigmaGraph: UndirectedGraph,
nodesWithDiscardedEdges: Set<string>, nodesWithDiscardedEdges: Set<string>,
minDegree: number, minDegree: number,
maxDegree: number maxDegree: number
@@ -722,6 +736,7 @@ const useLightrangeGraph = () => {
const range = maxDegree - minDegree || 1; // Avoid division by zero const range = maxDegree - minDegree || 1; // Avoid division by zero
const scale = Constants.maxNodeSize - Constants.minNodeSize; const scale = Constants.maxNodeSize - Constants.minNodeSize;
// Update node sizes
for (const nodeId of nodesWithDiscardedEdges) { for (const nodeId of nodesWithDiscardedEdges) {
if (sigmaGraph.hasNode(nodeId)) { if (sigmaGraph.hasNode(nodeId)) {
let newDegree = sigmaGraph.degree(nodeId); let newDegree = sigmaGraph.degree(nodeId);
@@ -742,6 +757,25 @@ const useLightrangeGraph = () => {
} }
}; };
// Helper function to update edge sizes
const updateEdgeSizes = (
sigmaGraph: UndirectedGraph,
minWeight: number,
maxWeight: number
) => {
// Update edge sizes
const minEdgeSize = useSettingsStore.getState().minEdgeSize;
const maxEdgeSize = useSettingsStore.getState().maxEdgeSize;
const weightRange = maxWeight - minWeight || 1; // Avoid division by zero
const sizeScale = maxEdgeSize - minEdgeSize;
sigmaGraph.forEachEdge(edge => {
const weight = sigmaGraph.getEdgeAttribute(edge, 'originalWeight') || 1;
const scaledSize = minEdgeSize + sizeScale * Math.pow((weight - minWeight) / weightRange, 0.5);
sigmaGraph.setEdgeAttribute(edge, 'size', scaledSize);
});
};
// If no new connectable nodes found, show toast and return // If no new connectable nodes found, show toast and return
if (nodesToAdd.size === 0) { if (nodesToAdd.size === 0) {
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree); updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree);
@@ -833,13 +867,20 @@ const useLightrangeGraph = () => {
if (sigmaGraph.hasEdge(newEdge.source, newEdge.target)) { if (sigmaGraph.hasEdge(newEdge.source, newEdge.target)) {
continue; continue;
} }
if (sigmaGraph.hasEdge(newEdge.target, newEdge.source)) {
continue; // Get weight from edge properties or default to 1
} const weight = newEdge.properties?.weight !== undefined ? Number(newEdge.properties.weight) : 1;
// Update min and max weight values
minWeight = Math.min(minWeight, weight);
maxWeight = Math.max(maxWeight, weight);
// Add the edge to the sigma graph // Add the edge to the sigma graph
newEdge.dynamicId = sigmaGraph.addDirectedEdge(newEdge.source, newEdge.target, { newEdge.dynamicId = sigmaGraph.addEdge(newEdge.source, newEdge.target, {
label: newEdge.properties?.keywords || undefined label: newEdge.properties?.keywords || undefined,
size: weight, // Set initial size based on weight
originalWeight: weight, // Store original weight for recalculation
type: 'curvedNoArrow' // Explicitly set edge type to no arrow
}); });
// Add the edge to the raw graph // Add the edge to the raw graph
@@ -861,9 +902,11 @@ const useLightrangeGraph = () => {
// Reset search engine to force rebuild // Reset search engine to force rebuild
useGraphStore.getState().resetSearchEngine(); useGraphStore.getState().resetSearchEngine();
// Update sizes for all nodes with discarded edges // Update sizes for all nodes and edges
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree); updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree);
updateEdgeSizes(sigmaGraph, minWeight, maxWeight);
// Final update for the expanded node
if (sigmaGraph.hasNode(nodeId)) { if (sigmaGraph.hasNode(nodeId)) {
const finalDegree = sigmaGraph.degree(nodeId); const finalDegree = sigmaGraph.degree(nodeId);
const limitedDegree = Math.min(finalDegree, maxDegree + 1); const limitedDegree = Math.min(finalDegree, maxDegree + 1);
@@ -891,7 +934,7 @@ const useLightrangeGraph = () => {
}, [nodeToExpand, sigmaGraph, rawGraph, t]); }, [nodeToExpand, sigmaGraph, rawGraph, t]);
// Helper function to get all nodes that will be deleted // Helper function to get all nodes that will be deleted
const getNodesThatWillBeDeleted = useCallback((nodeId: string, graph: DirectedGraph) => { const getNodesThatWillBeDeleted = useCallback((nodeId: string, graph: UndirectedGraph) => {
const nodesToDelete = new Set<string>([nodeId]); const nodesToDelete = new Set<string>([nodeId]);
// Find all nodes that would become isolated after deletion // Find all nodes that would become isolated after deletion
@@ -1019,7 +1062,7 @@ const useLightrangeGraph = () => {
// If no graph exists yet, create a new one and store it // If no graph exists yet, create a new one and store it
console.log('Creating new Sigma graph instance') console.log('Creating new Sigma graph instance')
const graph = new DirectedGraph() const graph = new UndirectedGraph()
useGraphStore.getState().setSigmaGraph(graph) useGraphStore.getState().setSigmaGraph(graph)
return graph as Graph<NodeType, EdgeType> return graph as Graph<NodeType, EdgeType>
}, [sigmaGraph]) }, [sigmaGraph])