Merge pull request #1156 from danielaskdd/main
Browse filesImprove node size calculation logic for node expansion to prevent oversize
- lightrag/api/webui/assets/index-BcBS1RaQ.css +0 -0
- lightrag/api/webui/assets/index-Cq65VeVX.css +0 -0
- lightrag/api/webui/assets/{index-DpQ0dh7t.js → index-DPOdOU_f.js} +0 -0
- lightrag/api/webui/index.html +0 -0
- lightrag_webui/src/components/graph/LayoutsControl.tsx +2 -2
- lightrag_webui/src/hooks/useLightragGraph.tsx +49 -18
- lightrag_webui/src/locales/ar.json +1 -1
- lightrag_webui/src/locales/en.json +1 -1
- lightrag_webui/src/locales/fr.json +1 -1
- lightrag_webui/src/locales/zh.json +1 -1
lightrag/api/webui/assets/index-BcBS1RaQ.css
DELETED
Binary file (53 kB)
|
|
lightrag/api/webui/assets/index-Cq65VeVX.css
ADDED
Binary file (53.1 kB). View file
|
|
lightrag/api/webui/assets/{index-DpQ0dh7t.js → index-DPOdOU_f.js}
RENAMED
Binary files a/lightrag/api/webui/assets/index-DpQ0dh7t.js and b/lightrag/api/webui/assets/index-DPOdOU_f.js differ
|
|
lightrag/api/webui/index.html
CHANGED
Binary files a/lightrag/api/webui/index.html and b/lightrag/api/webui/index.html differ
|
|
lightrag_webui/src/components/graph/LayoutsControl.tsx
CHANGED
@@ -218,8 +218,8 @@ const LayoutsControl = () => {
|
|
218 |
maxIterations: maxIterations,
|
219 |
settings: {
|
220 |
attraction: 0.0003, // Lower attraction force to reduce oscillation
|
221 |
-
repulsion: 0.
|
222 |
-
gravity: 0.
|
223 |
inertia: 0.4, // Lower inertia to add damping effect
|
224 |
maxMove: 100 // Limit maximum movement per step to prevent large jumps
|
225 |
}
|
|
|
218 |
maxIterations: maxIterations,
|
219 |
settings: {
|
220 |
attraction: 0.0003, // Lower attraction force to reduce oscillation
|
221 |
+
repulsion: 0.02, // Lower repulsion force to reduce oscillation
|
222 |
+
gravity: 0.02, // Increase gravity to make nodes converge to center faster
|
223 |
inertia: 0.4, // Lower inertia to add damping effect
|
224 |
maxMove: 100 // Limit maximum movement per step to prevent large jumps
|
225 |
}
|
lightrag_webui/src/hooks/useLightragGraph.tsx
CHANGED
@@ -76,7 +76,7 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
|
|
76 |
// Check if we need to fetch all database labels first
|
77 |
const lastSuccessfulQueryLabel = useGraphStore.getState().lastSuccessfulQueryLabel;
|
78 |
if (!lastSuccessfulQueryLabel) {
|
79 |
-
console.log('Last successful
|
80 |
try {
|
81 |
await useGraphStore.getState().fetchAllDatabaseLabels();
|
82 |
} catch (e) {
|
@@ -89,7 +89,7 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
|
|
89 |
const queryLabel = label || '*';
|
90 |
|
91 |
try {
|
92 |
-
console.log(`Fetching graph
|
93 |
rawData = await queryGraphs(queryLabel, maxDepth, minDegree);
|
94 |
} catch (e) {
|
95 |
useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!');
|
@@ -163,7 +163,7 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
|
|
163 |
|
164 |
if (!validateGraph(rawGraph)) {
|
165 |
rawGraph = null
|
166 |
-
console.
|
167 |
}
|
168 |
console.log('Graph data loaded')
|
169 |
}
|
@@ -360,8 +360,6 @@ const useLightrangeGraph = () => {
|
|
360 |
|
361 |
// Reset camera view
|
362 |
state.setMoveToSelectedNode(true);
|
363 |
-
|
364 |
-
console.log('Graph data loaded successfully');
|
365 |
}
|
366 |
|
367 |
// Update flags
|
@@ -466,7 +464,7 @@ const useLightrangeGraph = () => {
|
|
466 |
const nodesToAdd = new Set<string>();
|
467 |
const edgesToAdd = new Set<string>();
|
468 |
|
469 |
-
// Get degree
|
470 |
const minDegree = 1;
|
471 |
let maxDegree = 0;
|
472 |
sigmaGraph.forEachNode(node => {
|
@@ -474,10 +472,6 @@ const useLightrangeGraph = () => {
|
|
474 |
maxDegree = Math.max(maxDegree, degree);
|
475 |
});
|
476 |
|
477 |
-
// Calculate size formula parameters
|
478 |
-
const range = maxDegree - minDegree || 1; // Avoid division by zero
|
479 |
-
const scale = Constants.maxNodeSize - Constants.minNodeSize;
|
480 |
-
|
481 |
// First identify connectable nodes (nodes connected to the expanded node)
|
482 |
for (const node of processedNodes) {
|
483 |
// Skip if node already exists
|
@@ -498,6 +492,7 @@ const useLightrangeGraph = () => {
|
|
498 |
|
499 |
// Calculate node degrees and track discarded edges in one pass
|
500 |
const nodeDegrees = new Map<string, number>();
|
|
|
501 |
const nodesWithDiscardedEdges = new Set<string>();
|
502 |
|
503 |
for (const edge of processedEdges) {
|
@@ -506,12 +501,19 @@ const useLightrangeGraph = () => {
|
|
506 |
|
507 |
if (sourceExists && targetExists) {
|
508 |
edgesToAdd.add(edge.id);
|
509 |
-
// Add degrees for
|
510 |
if (nodesToAdd.has(edge.source)) {
|
511 |
nodeDegrees.set(edge.source, (nodeDegrees.get(edge.source) || 0) + 1);
|
|
|
|
|
|
|
512 |
}
|
|
|
513 |
if (nodesToAdd.has(edge.target)) {
|
514 |
nodeDegrees.set(edge.target, (nodeDegrees.get(edge.target) || 0) + 1);
|
|
|
|
|
|
|
515 |
}
|
516 |
} else {
|
517 |
// Track discarded edges for both new and existing nodes
|
@@ -535,16 +537,21 @@ const useLightrangeGraph = () => {
|
|
535 |
sigmaGraph: DirectedGraph,
|
536 |
nodesWithDiscardedEdges: Set<string>,
|
537 |
minDegree: number,
|
538 |
-
|
539 |
-
scale: number
|
540 |
) => {
|
|
|
|
|
|
|
|
|
541 |
for (const nodeId of nodesWithDiscardedEdges) {
|
542 |
if (sigmaGraph.hasNode(nodeId)) {
|
543 |
let newDegree = sigmaGraph.degree(nodeId);
|
544 |
newDegree += 1; // Add +1 for discarded edges
|
|
|
|
|
545 |
|
546 |
const newSize = Math.round(
|
547 |
-
Constants.minNodeSize + scale * Math.pow((
|
548 |
);
|
549 |
|
550 |
const currentSize = sigmaGraph.getNodeAttribute(nodeId, 'size');
|
@@ -558,16 +565,27 @@ const useLightrangeGraph = () => {
|
|
558 |
|
559 |
// If no new connectable nodes found, show toast and return
|
560 |
if (nodesToAdd.size === 0) {
|
561 |
-
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree,
|
562 |
toast.info(t('graphPanel.propertiesView.node.noNewNodes'));
|
563 |
return;
|
564 |
}
|
565 |
|
566 |
-
// Update maxDegree
|
|
|
567 |
for (const [, degree] of nodeDegrees.entries()) {
|
568 |
maxDegree = Math.max(maxDegree, degree);
|
569 |
}
|
570 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
571 |
// SAdd nodes and edges to the graph
|
572 |
// Calculate camera ratio and spread factor once before the loop
|
573 |
const cameraRatio = useGraphStore.getState().sigmaInstance?.getCamera().ratio || 1;
|
@@ -587,8 +605,10 @@ const useLightrangeGraph = () => {
|
|
587 |
const nodeDegree = nodeDegrees.get(nodeId) || 0;
|
588 |
|
589 |
// Calculate node size
|
|
|
|
|
590 |
const nodeSize = Math.round(
|
591 |
-
Constants.minNodeSize + scale * Math.pow((
|
592 |
);
|
593 |
|
594 |
// Calculate angle for polar coordinates
|
@@ -663,7 +683,18 @@ const useLightrangeGraph = () => {
|
|
663 |
useGraphStore.getState().resetSearchEngine();
|
664 |
|
665 |
// Update sizes for all nodes with discarded edges
|
666 |
-
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
667 |
|
668 |
} catch (error) {
|
669 |
console.error('Error expanding node:', error);
|
|
|
76 |
// Check if we need to fetch all database labels first
|
77 |
const lastSuccessfulQueryLabel = useGraphStore.getState().lastSuccessfulQueryLabel;
|
78 |
if (!lastSuccessfulQueryLabel) {
|
79 |
+
console.log('Last successful queryLabel is empty');
|
80 |
try {
|
81 |
await useGraphStore.getState().fetchAllDatabaseLabels();
|
82 |
} catch (e) {
|
|
|
89 |
const queryLabel = label || '*';
|
90 |
|
91 |
try {
|
92 |
+
console.log(`Fetching graph label: ${queryLabel}, depth: ${maxDepth}, deg: ${minDegree}`);
|
93 |
rawData = await queryGraphs(queryLabel, maxDepth, minDegree);
|
94 |
} catch (e) {
|
95 |
useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!');
|
|
|
163 |
|
164 |
if (!validateGraph(rawGraph)) {
|
165 |
rawGraph = null
|
166 |
+
console.warn('Invalid graph data')
|
167 |
}
|
168 |
console.log('Graph data loaded')
|
169 |
}
|
|
|
360 |
|
361 |
// Reset camera view
|
362 |
state.setMoveToSelectedNode(true);
|
|
|
|
|
363 |
}
|
364 |
|
365 |
// Update flags
|
|
|
464 |
const nodesToAdd = new Set<string>();
|
465 |
const edgesToAdd = new Set<string>();
|
466 |
|
467 |
+
// Get degree maxDegree from existing graph for size calculations
|
468 |
const minDegree = 1;
|
469 |
let maxDegree = 0;
|
470 |
sigmaGraph.forEachNode(node => {
|
|
|
472 |
maxDegree = Math.max(maxDegree, degree);
|
473 |
});
|
474 |
|
|
|
|
|
|
|
|
|
475 |
// First identify connectable nodes (nodes connected to the expanded node)
|
476 |
for (const node of processedNodes) {
|
477 |
// Skip if node already exists
|
|
|
492 |
|
493 |
// Calculate node degrees and track discarded edges in one pass
|
494 |
const nodeDegrees = new Map<string, number>();
|
495 |
+
const existingNodeDegreeIncrements = new Map<string, number>(); // Track degree increments for existing nodes
|
496 |
const nodesWithDiscardedEdges = new Set<string>();
|
497 |
|
498 |
for (const edge of processedEdges) {
|
|
|
501 |
|
502 |
if (sourceExists && targetExists) {
|
503 |
edgesToAdd.add(edge.id);
|
504 |
+
// Add degrees for both new and existing nodes
|
505 |
if (nodesToAdd.has(edge.source)) {
|
506 |
nodeDegrees.set(edge.source, (nodeDegrees.get(edge.source) || 0) + 1);
|
507 |
+
} else if (existingNodeIds.has(edge.source)) {
|
508 |
+
// Track degree increments for existing nodes
|
509 |
+
existingNodeDegreeIncrements.set(edge.source, (existingNodeDegreeIncrements.get(edge.source) || 0) + 1);
|
510 |
}
|
511 |
+
|
512 |
if (nodesToAdd.has(edge.target)) {
|
513 |
nodeDegrees.set(edge.target, (nodeDegrees.get(edge.target) || 0) + 1);
|
514 |
+
} else if (existingNodeIds.has(edge.target)) {
|
515 |
+
// Track degree increments for existing nodes
|
516 |
+
existingNodeDegreeIncrements.set(edge.target, (existingNodeDegreeIncrements.get(edge.target) || 0) + 1);
|
517 |
}
|
518 |
} else {
|
519 |
// Track discarded edges for both new and existing nodes
|
|
|
537 |
sigmaGraph: DirectedGraph,
|
538 |
nodesWithDiscardedEdges: Set<string>,
|
539 |
minDegree: number,
|
540 |
+
maxDegree: number
|
|
|
541 |
) => {
|
542 |
+
// Calculate derived values inside the function
|
543 |
+
const range = maxDegree - minDegree || 1; // Avoid division by zero
|
544 |
+
const scale = Constants.maxNodeSize - Constants.minNodeSize;
|
545 |
+
|
546 |
for (const nodeId of nodesWithDiscardedEdges) {
|
547 |
if (sigmaGraph.hasNode(nodeId)) {
|
548 |
let newDegree = sigmaGraph.degree(nodeId);
|
549 |
newDegree += 1; // Add +1 for discarded edges
|
550 |
+
// Limit newDegree to maxDegree + 1 to prevent nodes from being too large
|
551 |
+
const limitedDegree = Math.min(newDegree, maxDegree + 1);
|
552 |
|
553 |
const newSize = Math.round(
|
554 |
+
Constants.minNodeSize + scale * Math.pow((limitedDegree - minDegree) / range, 0.5)
|
555 |
);
|
556 |
|
557 |
const currentSize = sigmaGraph.getNodeAttribute(nodeId, 'size');
|
|
|
565 |
|
566 |
// If no new connectable nodes found, show toast and return
|
567 |
if (nodesToAdd.size === 0) {
|
568 |
+
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree);
|
569 |
toast.info(t('graphPanel.propertiesView.node.noNewNodes'));
|
570 |
return;
|
571 |
}
|
572 |
|
573 |
+
// Update maxDegree considering all nodes (both new and existing)
|
574 |
+
// 1. Consider degrees of new nodes
|
575 |
for (const [, degree] of nodeDegrees.entries()) {
|
576 |
maxDegree = Math.max(maxDegree, degree);
|
577 |
}
|
578 |
|
579 |
+
// 2. Consider degree increments for existing nodes
|
580 |
+
for (const [nodeId, increment] of existingNodeDegreeIncrements.entries()) {
|
581 |
+
const currentDegree = sigmaGraph.degree(nodeId);
|
582 |
+
const projectedDegree = currentDegree + increment;
|
583 |
+
maxDegree = Math.max(maxDegree, projectedDegree);
|
584 |
+
}
|
585 |
+
|
586 |
+
const range = maxDegree - minDegree || 1; // Avoid division by zero
|
587 |
+
const scale = Constants.maxNodeSize - Constants.minNodeSize;
|
588 |
+
|
589 |
// SAdd nodes and edges to the graph
|
590 |
// Calculate camera ratio and spread factor once before the loop
|
591 |
const cameraRatio = useGraphStore.getState().sigmaInstance?.getCamera().ratio || 1;
|
|
|
605 |
const nodeDegree = nodeDegrees.get(nodeId) || 0;
|
606 |
|
607 |
// Calculate node size
|
608 |
+
// Limit nodeDegree to maxDegree + 1 to prevent new nodes from being too large
|
609 |
+
const limitedDegree = Math.min(nodeDegree, maxDegree + 1);
|
610 |
const nodeSize = Math.round(
|
611 |
+
Constants.minNodeSize + scale * Math.pow((limitedDegree - minDegree) / range, 0.5)
|
612 |
);
|
613 |
|
614 |
// Calculate angle for polar coordinates
|
|
|
683 |
useGraphStore.getState().resetSearchEngine();
|
684 |
|
685 |
// Update sizes for all nodes with discarded edges
|
686 |
+
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree);
|
687 |
+
|
688 |
+
if (sigmaGraph.hasNode(nodeId)) {
|
689 |
+
const finalDegree = sigmaGraph.degree(nodeId);
|
690 |
+
const limitedDegree = Math.min(finalDegree, maxDegree + 1);
|
691 |
+
const newSize = Math.round(
|
692 |
+
Constants.minNodeSize + scale * Math.pow((limitedDegree - minDegree) / range, 0.5)
|
693 |
+
);
|
694 |
+
sigmaGraph.setNodeAttribute(nodeId, 'size', newSize);
|
695 |
+
nodeToExpand.size = newSize;
|
696 |
+
nodeToExpand.degree = finalDegree;
|
697 |
+
}
|
698 |
|
699 |
} catch (error) {
|
700 |
console.error('Error expanding node:', error);
|
lightrag_webui/src/locales/ar.json
CHANGED
@@ -164,7 +164,7 @@
|
|
164 |
"labels": "التسميات",
|
165 |
"degree": "الدرجة",
|
166 |
"properties": "الخصائص",
|
167 |
-
"relationships": "العلاقات",
|
168 |
"expandNode": "توسيع العقدة",
|
169 |
"pruneNode": "تقليم العقدة",
|
170 |
"deleteAllNodesError": "رفض حذف جميع العقد في الرسم البياني",
|
|
|
164 |
"labels": "التسميات",
|
165 |
"degree": "الدرجة",
|
166 |
"properties": "الخصائص",
|
167 |
+
"relationships": "العلاقات (داخل الرسم الفرعي)",
|
168 |
"expandNode": "توسيع العقدة",
|
169 |
"pruneNode": "تقليم العقدة",
|
170 |
"deleteAllNodesError": "رفض حذف جميع العقد في الرسم البياني",
|
lightrag_webui/src/locales/en.json
CHANGED
@@ -167,7 +167,7 @@
|
|
167 |
"labels": "Labels",
|
168 |
"degree": "Degree",
|
169 |
"properties": "Properties",
|
170 |
-
"relationships": "
|
171 |
"expandNode": "Expand Node",
|
172 |
"pruneNode": "Prune Node",
|
173 |
"deleteAllNodesError": "Refuse to delete all nodes in the graph",
|
|
|
167 |
"labels": "Labels",
|
168 |
"degree": "Degree",
|
169 |
"properties": "Properties",
|
170 |
+
"relationships": "Relations(within subgraph)",
|
171 |
"expandNode": "Expand Node",
|
172 |
"pruneNode": "Prune Node",
|
173 |
"deleteAllNodesError": "Refuse to delete all nodes in the graph",
|
lightrag_webui/src/locales/fr.json
CHANGED
@@ -164,7 +164,7 @@
|
|
164 |
"labels": "Étiquettes",
|
165 |
"degree": "Degré",
|
166 |
"properties": "Propriétés",
|
167 |
-
"relationships": "Relations",
|
168 |
"expandNode": "Développer le nœud",
|
169 |
"pruneNode": "Élaguer le nœud",
|
170 |
"deleteAllNodesError": "Refus de supprimer tous les nœuds du graphe",
|
|
|
164 |
"labels": "Étiquettes",
|
165 |
"degree": "Degré",
|
166 |
"properties": "Propriétés",
|
167 |
+
"relationships": "Relations(dans le sous-graphe)",
|
168 |
"expandNode": "Développer le nœud",
|
169 |
"pruneNode": "Élaguer le nœud",
|
170 |
"deleteAllNodesError": "Refus de supprimer tous les nœuds du graphe",
|
lightrag_webui/src/locales/zh.json
CHANGED
@@ -164,7 +164,7 @@
|
|
164 |
"labels": "标签",
|
165 |
"degree": "度数",
|
166 |
"properties": "属性",
|
167 |
-
"relationships": "关系",
|
168 |
"expandNode": "扩展节点",
|
169 |
"pruneNode": "修剪节点",
|
170 |
"deleteAllNodesError": "拒绝删除图中的所有节点",
|
|
|
164 |
"labels": "标签",
|
165 |
"degree": "度数",
|
166 |
"properties": "属性",
|
167 |
+
"relationships": "关系(子图内)",
|
168 |
"expandNode": "扩展节点",
|
169 |
"pruneNode": "修剪节点",
|
170 |
"deleteAllNodesError": "拒绝删除图中的所有节点",
|