choizhang
commited on
Commit
·
7843329
1
Parent(s):
fa48cea
fix(graph): Fixed the issue of incorrect handling of edges and nodes during node ID updates
Browse files
lightrag_webui/src/components/graph/EditablePropertyRow.tsx
CHANGED
@@ -119,41 +119,63 @@ const EditablePropertyRow = ({
|
|
119 |
if (sigmaInstance && sigmaGraph && rawGraph) {
|
120 |
// Update the node in sigma graph
|
121 |
if (sigmaGraph.hasNode(String(value))) {
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
|
134 |
-
|
135 |
-
|
136 |
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
}
|
142 |
}
|
143 |
-
} else {
|
144 |
-
// Fallback to full graph reload if direct update is not possible
|
145 |
-
useGraphStore.getState().setGraphDataFetchAttempted(false)
|
146 |
-
useGraphStore.getState().setLabelsFetchAttempted(false)
|
147 |
-
|
148 |
-
// Get current label to trigger reload
|
149 |
-
const currentLabel = useSettingsStore.getState().queryLabel
|
150 |
-
if (currentLabel) {
|
151 |
-
// Trigger data reload by temporarily clearing and resetting the label
|
152 |
-
useSettingsStore.getState().setQueryLabel('')
|
153 |
-
setTimeout(() => {
|
154 |
-
useSettingsStore.getState().setQueryLabel(currentLabel)
|
155 |
-
}, 0)
|
156 |
-
}
|
157 |
}
|
158 |
} else if (name === 'description') {
|
159 |
// For description updates
|
@@ -178,22 +200,45 @@ const EditablePropertyRow = ({
|
|
178 |
if (onValueChange) {
|
179 |
onValueChange(editValue)
|
180 |
}
|
181 |
-
} catch (error: any) {
|
182 |
console.error('Error updating property:', error);
|
183 |
|
184 |
-
//
|
185 |
-
let detailMessage = t('graphPanel.propertiesView.errors.updateFailed');
|
|
|
186 |
if (error.response?.data?.detail) {
|
187 |
-
|
188 |
-
|
|
|
189 |
} else if (error.message) {
|
190 |
-
|
191 |
-
detailMessage = error.message;
|
192 |
}
|
193 |
|
194 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
195 |
|
196 |
} finally {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
setIsSubmitting(false)
|
198 |
setIsEditing(false)
|
199 |
}
|
|
|
119 |
if (sigmaInstance && sigmaGraph && rawGraph) {
|
120 |
// Update the node in sigma graph
|
121 |
if (sigmaGraph.hasNode(String(value))) {
|
122 |
+
try {
|
123 |
+
// Create a new node with the updated ID
|
124 |
+
const oldNodeAttributes = sigmaGraph.getNodeAttributes(String(value))
|
125 |
+
|
126 |
+
// Add a new node with the new ID but keep all other attributes
|
127 |
+
sigmaGraph.addNode(editValue, {
|
128 |
+
...oldNodeAttributes,
|
129 |
+
label: editValue
|
130 |
+
})
|
131 |
+
|
132 |
+
// Copy all edges from the old node to the new node
|
133 |
+
sigmaGraph.forEachEdge(String(value), (edge, attributes, source, target) => {
|
134 |
+
const otherNode = source === String(value) ? target : source
|
135 |
+
const isOutgoing = source === String(value)
|
136 |
+
|
137 |
+
// Create a new edge with the same attributes but connected to the new node ID
|
138 |
+
if (isOutgoing) {
|
139 |
+
sigmaGraph.addEdge(editValue, otherNode, attributes)
|
140 |
+
} else {
|
141 |
+
sigmaGraph.addEdge(otherNode, editValue, attributes)
|
142 |
+
}
|
143 |
+
|
144 |
+
// Remove the old edge
|
145 |
+
sigmaGraph.dropEdge(edge)
|
146 |
+
})
|
147 |
+
|
148 |
+
// Remove the old node after all edges have been transferred
|
149 |
+
sigmaGraph.dropNode(String(value))
|
150 |
+
|
151 |
+
// Also update the node in the raw graph
|
152 |
+
const nodeIndex = rawGraph.nodeIdMap[String(value)]
|
153 |
+
if (nodeIndex !== undefined) {
|
154 |
+
rawGraph.nodes[nodeIndex].id = editValue
|
155 |
+
// Update the node ID map
|
156 |
+
delete rawGraph.nodeIdMap[String(value)]
|
157 |
+
rawGraph.nodeIdMap[editValue] = nodeIndex
|
158 |
+
}
|
159 |
|
160 |
+
// Refresh the sigma instance to reflect changes
|
161 |
+
sigmaInstance.refresh()
|
162 |
|
163 |
+
// Update selected node ID if it was the edited node
|
164 |
+
const selectedNode = useGraphStore.getState().selectedNode
|
165 |
+
if (selectedNode === String(value)) {
|
166 |
+
useGraphStore.getState().setSelectedNode(editValue)
|
167 |
+
}
|
168 |
+
|
169 |
+
// Update focused node ID if it was the edited node
|
170 |
+
const focusedNode = useGraphStore.getState().focusedNode
|
171 |
+
if (focusedNode === String(value)) {
|
172 |
+
useGraphStore.getState().setFocusedNode(editValue)
|
173 |
+
}
|
174 |
+
} catch (error) {
|
175 |
+
console.error('Error updating node ID in graph:', error)
|
176 |
+
throw new Error('Failed to update node ID in graph')
|
177 |
}
|
178 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
}
|
180 |
} else if (name === 'description') {
|
181 |
// For description updates
|
|
|
200 |
if (onValueChange) {
|
201 |
onValueChange(editValue)
|
202 |
}
|
203 |
+
} catch (error: any) {
|
204 |
console.error('Error updating property:', error);
|
205 |
|
206 |
+
// 尝试提取更具体的错误信息
|
207 |
+
let detailMessage = t('graphPanel.propertiesView.errors.updateFailed');
|
208 |
+
|
209 |
if (error.response?.data?.detail) {
|
210 |
+
detailMessage = error.response.data.detail;
|
211 |
+
} else if (error.response?.data?.message) {
|
212 |
+
detailMessage = error.response.data.message;
|
213 |
} else if (error.message) {
|
214 |
+
detailMessage = error.message;
|
|
|
215 |
}
|
216 |
|
217 |
+
// 记录详细的错误信息以便调试
|
218 |
+
console.error('Update failed:', {
|
219 |
+
entityType,
|
220 |
+
entityId,
|
221 |
+
propertyName: name,
|
222 |
+
newValue: editValue,
|
223 |
+
error: error.response?.data || error.message
|
224 |
+
});
|
225 |
+
|
226 |
+
toast.error(detailMessage, {
|
227 |
+
description: t('graphPanel.propertiesView.errors.tryAgainLater')
|
228 |
+
});
|
229 |
|
230 |
} finally {
|
231 |
+
// Update the value immediately in the UI
|
232 |
+
if (onValueChange) {
|
233 |
+
onValueChange(editValue);
|
234 |
+
}
|
235 |
+
// Trigger graph data refresh
|
236 |
+
useGraphStore.getState().setGraphDataFetchAttempted(false);
|
237 |
+
useGraphStore.getState().setLabelsFetchAttempted(false);
|
238 |
+
// Re-select the node to refresh properties panel
|
239 |
+
const currentNodeId = name === 'entity_id' ? editValue : (entityId || '');
|
240 |
+
useGraphStore.getState().setSelectedNode(null);
|
241 |
+
useGraphStore.getState().setSelectedNode(currentNodeId);
|
242 |
setIsSubmitting(false)
|
243 |
setIsEditing(false)
|
244 |
}
|
lightrag_webui/src/components/graph/GraphControl.tsx
CHANGED
@@ -99,7 +99,10 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
|
|
99 |
const events: Record<string, any> = {
|
100 |
enterNode: (event: NodeEvent) => {
|
101 |
if (!isButtonPressed(event.event.original)) {
|
102 |
-
|
|
|
|
|
|
|
103 |
}
|
104 |
},
|
105 |
leaveNode: (event: NodeEvent) => {
|
@@ -108,8 +111,11 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
|
|
108 |
}
|
109 |
},
|
110 |
clickNode: (event: NodeEvent) => {
|
111 |
-
|
112 |
-
|
|
|
|
|
|
|
113 |
},
|
114 |
clickStage: () => clearSelection()
|
115 |
}
|
|
|
99 |
const events: Record<string, any> = {
|
100 |
enterNode: (event: NodeEvent) => {
|
101 |
if (!isButtonPressed(event.event.original)) {
|
102 |
+
const graph = sigma.getGraph()
|
103 |
+
if (graph.hasNode(event.node)) {
|
104 |
+
setFocusedNode(event.node)
|
105 |
+
}
|
106 |
}
|
107 |
},
|
108 |
leaveNode: (event: NodeEvent) => {
|
|
|
111 |
}
|
112 |
},
|
113 |
clickNode: (event: NodeEvent) => {
|
114 |
+
const graph = sigma.getGraph()
|
115 |
+
if (graph.hasNode(event.node)) {
|
116 |
+
setSelectedNode(event.node)
|
117 |
+
setSelectedEdge(null)
|
118 |
+
}
|
119 |
},
|
120 |
clickStage: () => clearSelection()
|
121 |
}
|
lightrag_webui/src/locales/ar.json
CHANGED
@@ -35,6 +35,32 @@
|
|
35 |
"common": {
|
36 |
"cancel": "إلغاء"
|
37 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
"documentPanel": {
|
39 |
"clearDocuments": {
|
40 |
"button": "مسح",
|
|
|
35 |
"common": {
|
36 |
"cancel": "إلغاء"
|
37 |
},
|
38 |
+
"graphPanel": {
|
39 |
+
"propertiesView": {
|
40 |
+
"errors": {
|
41 |
+
"duplicateName": "اسم العقدة موجود بالفعل",
|
42 |
+
"updateFailed": "فشل تحديث العقدة",
|
43 |
+
"tryAgainLater": "يرجى المحاولة مرة أخرى لاحقاً"
|
44 |
+
},
|
45 |
+
"success": {
|
46 |
+
"entityUpdated": "تم تحديث العقدة بنجاح",
|
47 |
+
"relationUpdated": "تم تحديث العلاقة بنجاح"
|
48 |
+
},
|
49 |
+
"node": {
|
50 |
+
"title": "عقدة",
|
51 |
+
"id": "المعرف",
|
52 |
+
"labels": "التسميات",
|
53 |
+
"degree": "الدرجة",
|
54 |
+
"properties": "الخصائص",
|
55 |
+
"relationships": "العلاقات (ضمن الرسم البياني الفرعي)",
|
56 |
+
"expandNode": "توسيع العقدة",
|
57 |
+
"pruneNode": "تقليم العقدة",
|
58 |
+
"deleteAllNodesError": "رفض حذف جميع العقد في الرسم البياني",
|
59 |
+
"nodesRemoved": "تم حذف {{count}} عقدة، بما في ذلك العقد اليتيمة",
|
60 |
+
"noNewNodes": "لم يتم العثور على عقد قابلة للتوسيع"
|
61 |
+
}
|
62 |
+
}
|
63 |
+
},
|
64 |
"documentPanel": {
|
65 |
"clearDocuments": {
|
66 |
"button": "مسح",
|
lightrag_webui/src/locales/en.json
CHANGED
@@ -235,6 +235,15 @@
|
|
235 |
"vectorStorage": "Vector Storage"
|
236 |
},
|
237 |
"propertiesView": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
"node": {
|
239 |
"title": "Node",
|
240 |
"id": "ID",
|
|
|
235 |
"vectorStorage": "Vector Storage"
|
236 |
},
|
237 |
"propertiesView": {
|
238 |
+
"errors": {
|
239 |
+
"duplicateName": "Node name already exists",
|
240 |
+
"updateFailed": "Failed to update node",
|
241 |
+
"tryAgainLater": "Please try again later"
|
242 |
+
},
|
243 |
+
"success": {
|
244 |
+
"entityUpdated": "Node updated successfully",
|
245 |
+
"relationUpdated": "Relation updated successfully"
|
246 |
+
},
|
247 |
"node": {
|
248 |
"title": "Node",
|
249 |
"id": "ID",
|
lightrag_webui/src/locales/fr.json
CHANGED
@@ -35,6 +35,32 @@
|
|
35 |
"common": {
|
36 |
"cancel": "Annuler"
|
37 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
"documentPanel": {
|
39 |
"clearDocuments": {
|
40 |
"button": "Effacer",
|
|
|
35 |
"common": {
|
36 |
"cancel": "Annuler"
|
37 |
},
|
38 |
+
"graphPanel": {
|
39 |
+
"propertiesView": {
|
40 |
+
"errors": {
|
41 |
+
"duplicateName": "Le nom du nœud existe déjà",
|
42 |
+
"updateFailed": "Échec de la mise à jour du nœud",
|
43 |
+
"tryAgainLater": "Veuillez réessayer plus tard"
|
44 |
+
},
|
45 |
+
"success": {
|
46 |
+
"entityUpdated": "Nœud mis à jour avec succès",
|
47 |
+
"relationUpdated": "Relation mise à jour avec succès"
|
48 |
+
},
|
49 |
+
"node": {
|
50 |
+
"title": "Nœud",
|
51 |
+
"id": "ID",
|
52 |
+
"labels": "Étiquettes",
|
53 |
+
"degree": "Degré",
|
54 |
+
"properties": "Propriétés",
|
55 |
+
"relationships": "Relations(dans le sous-graphe)",
|
56 |
+
"expandNode": "Développer le nœud",
|
57 |
+
"pruneNode": "Élaguer le nœud",
|
58 |
+
"deleteAllNodesError": "Refus de supprimer tous les nœuds du graphe",
|
59 |
+
"nodesRemoved": "{{count}} nœuds supprimés, y compris les nœuds orphelins",
|
60 |
+
"noNewNodes": "Aucun nœud extensible trouvé"
|
61 |
+
}
|
62 |
+
}
|
63 |
+
},
|
64 |
"documentPanel": {
|
65 |
"clearDocuments": {
|
66 |
"button": "Effacer",
|
lightrag_webui/src/locales/zh.json
CHANGED
@@ -236,6 +236,15 @@
|
|
236 |
"vectorStorage": "向量存储"
|
237 |
},
|
238 |
"propertiesView": {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
"node": {
|
240 |
"title": "节点",
|
241 |
"id": "ID",
|
|
|
236 |
"vectorStorage": "向量存储"
|
237 |
},
|
238 |
"propertiesView": {
|
239 |
+
"errors": {
|
240 |
+
"duplicateName": "节点名称已存在",
|
241 |
+
"updateFailed": "更新节点失败",
|
242 |
+
"tryAgainLater": "请稍后重试"
|
243 |
+
},
|
244 |
+
"success": {
|
245 |
+
"entityUpdated": "节点更新成功",
|
246 |
+
"relationUpdated": "关系更新成功"
|
247 |
+
},
|
248 |
"node": {
|
249 |
"title": "节点",
|
250 |
"id": "ID",
|