From ea43f3537e0f6d7feb6fb63d54991c7c777cec24 Mon Sep 17 00:00:00 2001 From: choizhang Date: Sat, 12 Apr 2025 10:36:05 +0800 Subject: [PATCH] fix(graph): Fixed the issue of incorrect handling of edges and nodes during node ID updates --- .../components/graph/EditablePropertyRow.tsx | 125 ++++++++++++------ .../src/components/graph/GraphControl.tsx | 12 +- lightrag_webui/src/locales/ar.json | 26 ++++ lightrag_webui/src/locales/en.json | 9 ++ lightrag_webui/src/locales/fr.json | 26 ++++ lightrag_webui/src/locales/zh.json | 9 ++ 6 files changed, 164 insertions(+), 43 deletions(-) diff --git a/lightrag_webui/src/components/graph/EditablePropertyRow.tsx b/lightrag_webui/src/components/graph/EditablePropertyRow.tsx index dfd94e49..837f6514 100644 --- a/lightrag_webui/src/components/graph/EditablePropertyRow.tsx +++ b/lightrag_webui/src/components/graph/EditablePropertyRow.tsx @@ -119,40 +119,62 @@ const EditablePropertyRow = ({ if (sigmaInstance && sigmaGraph && rawGraph) { // Update the node in sigma graph if (sigmaGraph.hasNode(String(value))) { - // Update the node label in the sigma graph - sigmaGraph.setNodeAttribute(String(value), 'label', editValue) + try { + // Create a new node with the updated ID + const oldNodeAttributes = sigmaGraph.getNodeAttributes(String(value)) - // Also update the node in the raw graph - const nodeIndex = rawGraph.nodeIdMap[String(value)] - if (nodeIndex !== undefined) { - rawGraph.nodes[nodeIndex].id = editValue - // Update the node ID map - delete rawGraph.nodeIdMap[String(value)] - rawGraph.nodeIdMap[editValue] = nodeIndex + // Add a new node with the new ID but keep all other attributes + sigmaGraph.addNode(editValue, { + ...oldNodeAttributes, + label: editValue + }) + + // Copy all edges from the old node to the new node + sigmaGraph.forEachEdge(String(value), (edge, attributes, source, target) => { + const otherNode = source === String(value) ? target : source + const isOutgoing = source === String(value) + + // Create a new edge with the same attributes but connected to the new node ID + if (isOutgoing) { + sigmaGraph.addEdge(editValue, otherNode, attributes) + } else { + sigmaGraph.addEdge(otherNode, editValue, attributes) + } + + // Remove the old edge + sigmaGraph.dropEdge(edge) + }) + + // Remove the old node after all edges have been transferred + sigmaGraph.dropNode(String(value)) + + // Also update the node in the raw graph + const nodeIndex = rawGraph.nodeIdMap[String(value)] + if (nodeIndex !== undefined) { + rawGraph.nodes[nodeIndex].id = editValue + // Update the node ID map + delete rawGraph.nodeIdMap[String(value)] + rawGraph.nodeIdMap[editValue] = nodeIndex + } + + // Refresh the sigma instance to reflect changes + sigmaInstance.refresh() + + // Update selected node ID if it was the edited node + const selectedNode = useGraphStore.getState().selectedNode + if (selectedNode === String(value)) { + useGraphStore.getState().setSelectedNode(editValue) + } + + // Update focused node ID if it was the edited node + const focusedNode = useGraphStore.getState().focusedNode + if (focusedNode === String(value)) { + useGraphStore.getState().setFocusedNode(editValue) + } + } catch (error) { + console.error('Error updating node ID in graph:', error) + throw new Error('Failed to update node ID in graph') } - - // Refresh the sigma instance to reflect changes - sigmaInstance.refresh() - - // Update selected node ID if it was the edited node - const selectedNode = useGraphStore.getState().selectedNode - if (selectedNode === String(value)) { - useGraphStore.getState().setSelectedNode(editValue) - } - } - } else { - // Fallback to full graph reload if direct update is not possible - useGraphStore.getState().setGraphDataFetchAttempted(false) - useGraphStore.getState().setLabelsFetchAttempted(false) - - // Get current label to trigger reload - const currentLabel = useSettingsStore.getState().queryLabel - if (currentLabel) { - // Trigger data reload by temporarily clearing and resetting the label - useSettingsStore.getState().setQueryLabel('') - setTimeout(() => { - useSettingsStore.getState().setQueryLabel(currentLabel) - }, 0) } } } else if (name === 'description') { @@ -178,22 +200,45 @@ const EditablePropertyRow = ({ if (onValueChange) { onValueChange(editValue) } - } catch (error: any) { // Keep type as any to access potential response properties + } catch (error: any) { console.error('Error updating property:', error); - // Attempt to extract a more specific error message - let detailMessage = t('graphPanel.propertiesView.errors.updateFailed'); // Default message + // 尝试提取更具体的错误信息 + let detailMessage = t('graphPanel.propertiesView.errors.updateFailed'); + if (error.response?.data?.detail) { - // Use the detailed message from the backend response if available - detailMessage = error.response.data.detail; + detailMessage = error.response.data.detail; + } else if (error.response?.data?.message) { + detailMessage = error.response.data.message; } else if (error.message) { - // Use the error object's message if no backend detail - detailMessage = error.message; + detailMessage = error.message; } - toast.error(detailMessage); // Show the determined error message + // 记录详细的错误信息以便调试 + console.error('Update failed:', { + entityType, + entityId, + propertyName: name, + newValue: editValue, + error: error.response?.data || error.message + }); + + toast.error(detailMessage, { + description: t('graphPanel.propertiesView.errors.tryAgainLater') + }); } finally { + // Update the value immediately in the UI + if (onValueChange) { + onValueChange(editValue); + } + // Trigger graph data refresh + useGraphStore.getState().setGraphDataFetchAttempted(false); + useGraphStore.getState().setLabelsFetchAttempted(false); + // Re-select the node to refresh properties panel + const currentNodeId = name === 'entity_id' ? editValue : (entityId || ''); + useGraphStore.getState().setSelectedNode(null); + useGraphStore.getState().setSelectedNode(currentNodeId); setIsSubmitting(false) setIsEditing(false) } diff --git a/lightrag_webui/src/components/graph/GraphControl.tsx b/lightrag_webui/src/components/graph/GraphControl.tsx index aca8a9c4..8211178a 100644 --- a/lightrag_webui/src/components/graph/GraphControl.tsx +++ b/lightrag_webui/src/components/graph/GraphControl.tsx @@ -99,7 +99,10 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) const events: Record = { enterNode: (event: NodeEvent) => { if (!isButtonPressed(event.event.original)) { - setFocusedNode(event.node) + const graph = sigma.getGraph() + if (graph.hasNode(event.node)) { + setFocusedNode(event.node) + } } }, leaveNode: (event: NodeEvent) => { @@ -108,8 +111,11 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) } }, clickNode: (event: NodeEvent) => { - setSelectedNode(event.node) - setSelectedEdge(null) + const graph = sigma.getGraph() + if (graph.hasNode(event.node)) { + setSelectedNode(event.node) + setSelectedEdge(null) + } }, clickStage: () => clearSelection() } diff --git a/lightrag_webui/src/locales/ar.json b/lightrag_webui/src/locales/ar.json index 36f31e92..9b227d47 100644 --- a/lightrag_webui/src/locales/ar.json +++ b/lightrag_webui/src/locales/ar.json @@ -35,6 +35,32 @@ "common": { "cancel": "إلغاء" }, + "graphPanel": { + "propertiesView": { + "errors": { + "duplicateName": "اسم العقدة موجود بالفعل", + "updateFailed": "فشل تحديث العقدة", + "tryAgainLater": "يرجى المحاولة مرة أخرى لاحقاً" + }, + "success": { + "entityUpdated": "تم تحديث العقدة بنجاح", + "relationUpdated": "تم تحديث العلاقة بنجاح" + }, + "node": { + "title": "عقدة", + "id": "المعرف", + "labels": "التسميات", + "degree": "الدرجة", + "properties": "الخصائص", + "relationships": "العلاقات (ضمن الرسم البياني الفرعي)", + "expandNode": "توسيع العقدة", + "pruneNode": "تقليم العقدة", + "deleteAllNodesError": "رفض حذف جميع العقد في الرسم البياني", + "nodesRemoved": "تم حذف {{count}} عقدة، بما في ذلك العقد اليتيمة", + "noNewNodes": "لم يتم العثور على عقد قابلة للتوسيع" + } + } + }, "documentPanel": { "clearDocuments": { "button": "مسح", diff --git a/lightrag_webui/src/locales/en.json b/lightrag_webui/src/locales/en.json index 13b6e5a5..a7119cbd 100644 --- a/lightrag_webui/src/locales/en.json +++ b/lightrag_webui/src/locales/en.json @@ -235,6 +235,15 @@ "vectorStorage": "Vector Storage" }, "propertiesView": { + "errors": { + "duplicateName": "Node name already exists", + "updateFailed": "Failed to update node", + "tryAgainLater": "Please try again later" + }, + "success": { + "entityUpdated": "Node updated successfully", + "relationUpdated": "Relation updated successfully" + }, "node": { "title": "Node", "id": "ID", diff --git a/lightrag_webui/src/locales/fr.json b/lightrag_webui/src/locales/fr.json index dbba9480..f4894d86 100644 --- a/lightrag_webui/src/locales/fr.json +++ b/lightrag_webui/src/locales/fr.json @@ -35,6 +35,32 @@ "common": { "cancel": "Annuler" }, + "graphPanel": { + "propertiesView": { + "errors": { + "duplicateName": "Le nom du nœud existe déjà", + "updateFailed": "Échec de la mise à jour du nœud", + "tryAgainLater": "Veuillez réessayer plus tard" + }, + "success": { + "entityUpdated": "Nœud mis à jour avec succès", + "relationUpdated": "Relation mise à jour avec succès" + }, + "node": { + "title": "Nœud", + "id": "ID", + "labels": "Étiquettes", + "degree": "Degré", + "properties": "Propriétés", + "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", + "nodesRemoved": "{{count}} nœuds supprimés, y compris les nœuds orphelins", + "noNewNodes": "Aucun nœud extensible trouvé" + } + } + }, "documentPanel": { "clearDocuments": { "button": "Effacer", diff --git a/lightrag_webui/src/locales/zh.json b/lightrag_webui/src/locales/zh.json index df4c33c3..beeb56d4 100644 --- a/lightrag_webui/src/locales/zh.json +++ b/lightrag_webui/src/locales/zh.json @@ -236,6 +236,15 @@ "vectorStorage": "向量存储" }, "propertiesView": { + "errors": { + "duplicateName": "节点名称已存在", + "updateFailed": "更新节点失败", + "tryAgainLater": "请稍后重试" + }, + "success": { + "entityUpdated": "节点更新成功", + "relationUpdated": "关系更新成功" + }, "node": { "title": "节点", "id": "ID",