Merge pull request #1156 from danielaskdd/main

Improve node size calculation logic for node expansion to prevent oversize
This commit is contained in:
Daniel.y
2025-03-22 15:20:15 +08:00
committed by GitHub
10 changed files with 196 additions and 173 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -8,8 +8,8 @@
<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-DpQ0dh7t.js"></script> <script type="module" crossorigin src="/webui/assets/index-DPOdOU_f.js"></script>
<link rel="stylesheet" crossorigin href="/webui/assets/index-BcBS1RaQ.css"> <link rel="stylesheet" crossorigin href="/webui/assets/index-Cq65VeVX.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -218,8 +218,8 @@ const LayoutsControl = () => {
maxIterations: maxIterations, maxIterations: maxIterations,
settings: { settings: {
attraction: 0.0003, // Lower attraction force to reduce oscillation attraction: 0.0003, // Lower attraction force to reduce oscillation
repulsion: 0.05, // Lower repulsion force to reduce oscillation repulsion: 0.02, // Lower repulsion force to reduce oscillation
gravity: 0.01, // Increase gravity to make nodes converge to center faster gravity: 0.02, // Increase gravity to make nodes converge to center faster
inertia: 0.4, // Lower inertia to add damping effect inertia: 0.4, // Lower inertia to add damping effect
maxMove: 100 // Limit maximum movement per step to prevent large jumps maxMove: 100 // Limit maximum movement per step to prevent large jumps
} }

View File

@@ -76,7 +76,7 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
// Check if we need to fetch all database labels first // Check if we need to fetch all database labels first
const lastSuccessfulQueryLabel = useGraphStore.getState().lastSuccessfulQueryLabel; const lastSuccessfulQueryLabel = useGraphStore.getState().lastSuccessfulQueryLabel;
if (!lastSuccessfulQueryLabel) { if (!lastSuccessfulQueryLabel) {
console.log('Last successful query label is empty, fetching all database labels first...'); console.log('Last successful queryLabel is empty');
try { try {
await useGraphStore.getState().fetchAllDatabaseLabels(); await useGraphStore.getState().fetchAllDatabaseLabels();
} catch (e) { } catch (e) {
@@ -89,7 +89,7 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
const queryLabel = label || '*'; const queryLabel = label || '*';
try { try {
console.log(`Fetching graph data with label: ${queryLabel}, maxDepth: ${maxDepth}, minDegree: ${minDegree}`); console.log(`Fetching graph label: ${queryLabel}, depth: ${maxDepth}, deg: ${minDegree}`);
rawData = await queryGraphs(queryLabel, maxDepth, minDegree); rawData = await queryGraphs(queryLabel, maxDepth, minDegree);
} catch (e) { } catch (e) {
useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!'); useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!');
@@ -163,7 +163,7 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
if (!validateGraph(rawGraph)) { if (!validateGraph(rawGraph)) {
rawGraph = null rawGraph = null
console.error('Invalid graph data') console.warn('Invalid graph data')
} }
console.log('Graph data loaded') console.log('Graph data loaded')
} }
@@ -360,8 +360,6 @@ const useLightrangeGraph = () => {
// Reset camera view // Reset camera view
state.setMoveToSelectedNode(true); state.setMoveToSelectedNode(true);
console.log('Graph data loaded successfully');
} }
// Update flags // Update flags
@@ -466,7 +464,7 @@ const useLightrangeGraph = () => {
const nodesToAdd = new Set<string>(); const nodesToAdd = new Set<string>();
const edgesToAdd = new Set<string>(); const edgesToAdd = new Set<string>();
// Get degree range 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;
sigmaGraph.forEachNode(node => { sigmaGraph.forEachNode(node => {
@@ -474,10 +472,6 @@ const useLightrangeGraph = () => {
maxDegree = Math.max(maxDegree, degree); maxDegree = Math.max(maxDegree, degree);
}); });
// Calculate size formula parameters
const range = maxDegree - minDegree || 1; // Avoid division by zero
const scale = Constants.maxNodeSize - Constants.minNodeSize;
// 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
@@ -498,6 +492,7 @@ const useLightrangeGraph = () => {
// Calculate node degrees and track discarded edges in one pass // Calculate node degrees and track discarded edges in one pass
const nodeDegrees = new Map<string, number>(); const nodeDegrees = new Map<string, number>();
const existingNodeDegreeIncrements = new Map<string, number>(); // Track degree increments for existing nodes
const nodesWithDiscardedEdges = new Set<string>(); const nodesWithDiscardedEdges = new Set<string>();
for (const edge of processedEdges) { for (const edge of processedEdges) {
@@ -506,12 +501,19 @@ const useLightrangeGraph = () => {
if (sourceExists && targetExists) { if (sourceExists && targetExists) {
edgesToAdd.add(edge.id); edgesToAdd.add(edge.id);
// Add degrees for valid edges // Add degrees for both new and existing nodes
if (nodesToAdd.has(edge.source)) { if (nodesToAdd.has(edge.source)) {
nodeDegrees.set(edge.source, (nodeDegrees.get(edge.source) || 0) + 1); nodeDegrees.set(edge.source, (nodeDegrees.get(edge.source) || 0) + 1);
} else if (existingNodeIds.has(edge.source)) {
// Track degree increments for existing nodes
existingNodeDegreeIncrements.set(edge.source, (existingNodeDegreeIncrements.get(edge.source) || 0) + 1);
} }
if (nodesToAdd.has(edge.target)) { if (nodesToAdd.has(edge.target)) {
nodeDegrees.set(edge.target, (nodeDegrees.get(edge.target) || 0) + 1); nodeDegrees.set(edge.target, (nodeDegrees.get(edge.target) || 0) + 1);
} else if (existingNodeIds.has(edge.target)) {
// Track degree increments for existing nodes
existingNodeDegreeIncrements.set(edge.target, (existingNodeDegreeIncrements.get(edge.target) || 0) + 1);
} }
} else { } else {
// Track discarded edges for both new and existing nodes // Track discarded edges for both new and existing nodes
@@ -535,16 +537,21 @@ const useLightrangeGraph = () => {
sigmaGraph: DirectedGraph, sigmaGraph: DirectedGraph,
nodesWithDiscardedEdges: Set<string>, nodesWithDiscardedEdges: Set<string>,
minDegree: number, minDegree: number,
range: number, maxDegree: number
scale: number
) => { ) => {
// Calculate derived values inside the function
const range = maxDegree - minDegree || 1; // Avoid division by zero
const scale = Constants.maxNodeSize - Constants.minNodeSize;
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);
newDegree += 1; // Add +1 for discarded edges newDegree += 1; // Add +1 for discarded edges
// Limit newDegree to maxDegree + 1 to prevent nodes from being too large
const limitedDegree = Math.min(newDegree, maxDegree + 1);
const newSize = Math.round( const newSize = Math.round(
Constants.minNodeSize + scale * Math.pow((newDegree - minDegree) / range, 0.5) Constants.minNodeSize + scale * Math.pow((limitedDegree - minDegree) / range, 0.5)
); );
const currentSize = sigmaGraph.getNodeAttribute(nodeId, 'size'); const currentSize = sigmaGraph.getNodeAttribute(nodeId, 'size');
@@ -558,16 +565,27 @@ const useLightrangeGraph = () => {
// 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, range, scale); updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree);
toast.info(t('graphPanel.propertiesView.node.noNewNodes')); toast.info(t('graphPanel.propertiesView.node.noNewNodes'));
return; return;
} }
// Update maxDegree with new node degrees // Update maxDegree considering all nodes (both new and existing)
// 1. Consider degrees of new nodes
for (const [, degree] of nodeDegrees.entries()) { for (const [, degree] of nodeDegrees.entries()) {
maxDegree = Math.max(maxDegree, degree); maxDegree = Math.max(maxDegree, degree);
} }
// 2. Consider degree increments for existing nodes
for (const [nodeId, increment] of existingNodeDegreeIncrements.entries()) {
const currentDegree = sigmaGraph.degree(nodeId);
const projectedDegree = currentDegree + increment;
maxDegree = Math.max(maxDegree, projectedDegree);
}
const range = maxDegree - minDegree || 1; // Avoid division by zero
const scale = Constants.maxNodeSize - Constants.minNodeSize;
// SAdd nodes and edges to the graph // SAdd nodes and edges to the graph
// Calculate camera ratio and spread factor once before the loop // Calculate camera ratio and spread factor once before the loop
const cameraRatio = useGraphStore.getState().sigmaInstance?.getCamera().ratio || 1; const cameraRatio = useGraphStore.getState().sigmaInstance?.getCamera().ratio || 1;
@@ -587,8 +605,10 @@ const useLightrangeGraph = () => {
const nodeDegree = nodeDegrees.get(nodeId) || 0; const nodeDegree = nodeDegrees.get(nodeId) || 0;
// Calculate node size // Calculate node size
// Limit nodeDegree to maxDegree + 1 to prevent new nodes from being too large
const limitedDegree = Math.min(nodeDegree, maxDegree + 1);
const nodeSize = Math.round( const nodeSize = Math.round(
Constants.minNodeSize + scale * Math.pow((nodeDegree - minDegree) / range, 0.5) Constants.minNodeSize + scale * Math.pow((limitedDegree - minDegree) / range, 0.5)
); );
// Calculate angle for polar coordinates // Calculate angle for polar coordinates
@@ -663,7 +683,18 @@ const useLightrangeGraph = () => {
useGraphStore.getState().resetSearchEngine(); useGraphStore.getState().resetSearchEngine();
// Update sizes for all nodes with discarded edges // Update sizes for all nodes with discarded edges
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, range, scale); updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree);
if (sigmaGraph.hasNode(nodeId)) {
const finalDegree = sigmaGraph.degree(nodeId);
const limitedDegree = Math.min(finalDegree, maxDegree + 1);
const newSize = Math.round(
Constants.minNodeSize + scale * Math.pow((limitedDegree - minDegree) / range, 0.5)
);
sigmaGraph.setNodeAttribute(nodeId, 'size', newSize);
nodeToExpand.size = newSize;
nodeToExpand.degree = finalDegree;
}
} catch (error) { } catch (error) {
console.error('Error expanding node:', error); console.error('Error expanding node:', error);

View File

@@ -164,7 +164,7 @@
"labels": "التسميات", "labels": "التسميات",
"degree": "الدرجة", "degree": "الدرجة",
"properties": "الخصائص", "properties": "الخصائص",
"relationships": "العلاقات", "relationships": "العلاقات (داخل الرسم الفرعي)",
"expandNode": "توسيع العقدة", "expandNode": "توسيع العقدة",
"pruneNode": "تقليم العقدة", "pruneNode": "تقليم العقدة",
"deleteAllNodesError": "رفض حذف جميع العقد في الرسم البياني", "deleteAllNodesError": "رفض حذف جميع العقد في الرسم البياني",

View File

@@ -167,7 +167,7 @@
"labels": "Labels", "labels": "Labels",
"degree": "Degree", "degree": "Degree",
"properties": "Properties", "properties": "Properties",
"relationships": "Relationships", "relationships": "Relations(within subgraph)",
"expandNode": "Expand Node", "expandNode": "Expand Node",
"pruneNode": "Prune Node", "pruneNode": "Prune Node",
"deleteAllNodesError": "Refuse to delete all nodes in the graph", "deleteAllNodesError": "Refuse to delete all nodes in the graph",

View File

@@ -164,7 +164,7 @@
"labels": "Étiquettes", "labels": "Étiquettes",
"degree": "Degré", "degree": "Degré",
"properties": "Propriétés", "properties": "Propriétés",
"relationships": "Relations", "relationships": "Relations(dans le sous-graphe)",
"expandNode": "Développer le nœud", "expandNode": "Développer le nœud",
"pruneNode": "Élaguer le nœud", "pruneNode": "Élaguer le nœud",
"deleteAllNodesError": "Refus de supprimer tous les nœuds du graphe", "deleteAllNodesError": "Refus de supprimer tous les nœuds du graphe",

View File

@@ -164,7 +164,7 @@
"labels": "标签", "labels": "标签",
"degree": "度数", "degree": "度数",
"properties": "属性", "properties": "属性",
"relationships": "关系", "relationships": "关系(子图内)",
"expandNode": "扩展节点", "expandNode": "扩展节点",
"pruneNode": "修剪节点", "pruneNode": "修剪节点",
"deleteAllNodesError": "拒绝删除图中的所有节点", "deleteAllNodesError": "拒绝删除图中的所有节点",