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" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lightrag</title>
<script type="module" crossorigin src="/webui/assets/index-DpQ0dh7t.js"></script>
<link rel="stylesheet" crossorigin href="/webui/assets/index-BcBS1RaQ.css">
<script type="module" crossorigin src="/webui/assets/index-DPOdOU_f.js"></script>
<link rel="stylesheet" crossorigin href="/webui/assets/index-Cq65VeVX.css">
</head>
<body>
<div id="root"></div>

View File

@@ -218,8 +218,8 @@ const LayoutsControl = () => {
maxIterations: maxIterations,
settings: {
attraction: 0.0003, // Lower attraction force to reduce oscillation
repulsion: 0.05, // Lower repulsion force to reduce oscillation
gravity: 0.01, // Increase gravity to make nodes converge to center faster
repulsion: 0.02, // Lower repulsion force to reduce oscillation
gravity: 0.02, // Increase gravity to make nodes converge to center faster
inertia: 0.4, // Lower inertia to add damping effect
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
const lastSuccessfulQueryLabel = useGraphStore.getState().lastSuccessfulQueryLabel;
if (!lastSuccessfulQueryLabel) {
console.log('Last successful query label is empty, fetching all database labels first...');
console.log('Last successful queryLabel is empty');
try {
await useGraphStore.getState().fetchAllDatabaseLabels();
} catch (e) {
@@ -89,7 +89,7 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
const queryLabel = label || '*';
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);
} catch (e) {
useBackendState.getState().setErrorMessage(errorMessage(e), 'Query Graphs Error!');
@@ -163,7 +163,7 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
if (!validateGraph(rawGraph)) {
rawGraph = null
console.error('Invalid graph data')
console.warn('Invalid graph data')
}
console.log('Graph data loaded')
}
@@ -360,8 +360,6 @@ const useLightrangeGraph = () => {
// Reset camera view
state.setMoveToSelectedNode(true);
console.log('Graph data loaded successfully');
}
// Update flags
@@ -466,7 +464,7 @@ const useLightrangeGraph = () => {
const nodesToAdd = 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;
let maxDegree = 0;
sigmaGraph.forEachNode(node => {
@@ -474,10 +472,6 @@ const useLightrangeGraph = () => {
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)
for (const node of processedNodes) {
// Skip if node already exists
@@ -498,6 +492,7 @@ const useLightrangeGraph = () => {
// Calculate node degrees and track discarded edges in one pass
const nodeDegrees = new Map<string, number>();
const existingNodeDegreeIncrements = new Map<string, number>(); // Track degree increments for existing nodes
const nodesWithDiscardedEdges = new Set<string>();
for (const edge of processedEdges) {
@@ -506,12 +501,19 @@ const useLightrangeGraph = () => {
if (sourceExists && targetExists) {
edgesToAdd.add(edge.id);
// Add degrees for valid edges
// Add degrees for both new and existing nodes
if (nodesToAdd.has(edge.source)) {
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)) {
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 {
// Track discarded edges for both new and existing nodes
@@ -535,16 +537,21 @@ const useLightrangeGraph = () => {
sigmaGraph: DirectedGraph,
nodesWithDiscardedEdges: Set<string>,
minDegree: number,
range: number,
scale: number
maxDegree: 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) {
if (sigmaGraph.hasNode(nodeId)) {
let newDegree = sigmaGraph.degree(nodeId);
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(
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');
@@ -558,16 +565,27 @@ const useLightrangeGraph = () => {
// If no new connectable nodes found, show toast and return
if (nodesToAdd.size === 0) {
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, range, scale);
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, maxDegree);
toast.info(t('graphPanel.propertiesView.node.noNewNodes'));
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()) {
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
// Calculate camera ratio and spread factor once before the loop
const cameraRatio = useGraphStore.getState().sigmaInstance?.getCamera().ratio || 1;
@@ -587,8 +605,10 @@ const useLightrangeGraph = () => {
const nodeDegree = nodeDegrees.get(nodeId) || 0;
// 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(
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
@@ -663,7 +683,18 @@ const useLightrangeGraph = () => {
useGraphStore.getState().resetSearchEngine();
// 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) {
console.error('Error expanding node:', error);

View File

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

View File

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

View File

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

View File

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