choizhang commited on
Commit
4d5e0aa
·
1 Parent(s): 7843329

refactor(graph): Refactoring node attribute update logic to improve code maintainability

Browse files
lightrag_webui/src/components/graph/EditablePropertyRow.tsx CHANGED
@@ -5,7 +5,6 @@ import Input from '@/components/ui/Input'
5
  import { toast } from 'sonner'
6
  import { updateEntity, updateRelation, checkEntityNameExists } from '@/api/lightrag'
7
  import { useGraphStore } from '@/stores/graph'
8
- import { useSettingsStore } from '@/stores/settings'
9
 
10
  interface EditablePropertyRowProps {
11
  name: string
@@ -76,169 +75,142 @@ const EditablePropertyRow = ({
76
  }
77
  }
78
 
79
- const handleSave = async () => {
80
- if (isSubmitting) return
 
 
81
 
82
- // Don't save if value hasn't changed
83
- if (editValue === String(value)) {
84
- setIsEditing(false)
85
  return
86
  }
87
 
88
- setIsSubmitting(true)
89
-
90
  try {
91
- // Special handling for entity_id (name) field to check for duplicates
92
- if (name === 'entity_id' && entityType === 'node') {
93
- // Ensure we are not checking the original name against itself if it's protected
94
- if (editValue !== String(value)) {
95
- const exists = await checkEntityNameExists(editValue);
96
- if (exists) {
97
- toast.error(t('graphPanel.propertiesView.errors.duplicateName'));
98
- setIsSubmitting(false);
99
- return;
100
- }
 
 
 
 
 
 
 
 
 
 
101
  }
102
- }
103
-
104
- // Update the entity or relation in the database
105
- if (entityType === 'node' && entityId) {
106
- // For nodes, we need to determine if we're updating the name or description
107
- const updatedData: Record<string, any> = {}
108
-
109
- if (name === 'entity_id') {
110
- // For entity name updates
111
- updatedData['entity_name'] = editValue
112
- await updateEntity(String(value), updatedData, true) // Pass original name (value) as identifier
113
-
114
- // Update node label in the graph directly instead of reloading the entire graph
115
- const sigmaInstance = useGraphStore.getState().sigmaInstance
116
- const sigmaGraph = useGraphStore.getState().sigmaGraph
117
- const rawGraph = useGraphStore.getState().rawGraph
118
-
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
182
- updatedData['description'] = editValue
183
- await updateEntity(entityId, updatedData) // Pass entityId as identifier
184
  } else {
185
- // For other property updates
186
  updatedData[name] = editValue
187
- await updateEntity(entityId, updatedData) // Pass entityId as identifier
 
 
 
188
  }
189
-
190
  toast.success(t('graphPanel.propertiesView.success.entityUpdated'))
191
  } else if (entityType === 'edge' && sourceId && targetId) {
192
- // For edges, update the relation
193
- const updatedData: Record<string, any> = {}
194
  updatedData[name] = editValue
195
  await updateRelation(sourceId, targetId, updatedData)
196
  toast.success(t('graphPanel.propertiesView.success.relationUpdated'))
197
  }
198
 
199
- // Notify parent component about the value change
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
  }
 
5
  import { toast } from 'sonner'
6
  import { updateEntity, updateRelation, checkEntityNameExists } from '@/api/lightrag'
7
  import { useGraphStore } from '@/stores/graph'
 
8
 
9
  interface EditablePropertyRowProps {
10
  name: string
 
75
  }
76
  }
77
 
78
+ const updateGraphNode = async (nodeId: string, propertyName: string, newValue: string) => {
79
+ const sigmaInstance = useGraphStore.getState().sigmaInstance
80
+ const sigmaGraph = useGraphStore.getState().sigmaGraph
81
+ const rawGraph = useGraphStore.getState().rawGraph
82
 
83
+ if (!sigmaInstance || !sigmaGraph || !rawGraph || !sigmaGraph.hasNode(String(nodeId))) {
 
 
84
  return
85
  }
86
 
 
 
87
  try {
88
+ const nodeAttributes = sigmaGraph.getNodeAttributes(String(nodeId))
89
+
90
+ if (propertyName === 'entity_id') {
91
+ sigmaGraph.addNode(newValue, { ...nodeAttributes, label: newValue })
92
+
93
+ sigmaGraph.forEachEdge(String(nodeId), (edge, attributes, source, target) => {
94
+ const otherNode = source === String(nodeId) ? target : source
95
+ const isOutgoing = source === String(nodeId)
96
+ sigmaGraph.addEdge(isOutgoing ? newValue : otherNode, isOutgoing ? otherNode : newValue, attributes)
97
+ sigmaGraph.dropEdge(edge)
98
+ })
99
+
100
+ sigmaGraph.dropNode(String(nodeId))
101
+
102
+ const nodeIndex = rawGraph.nodeIdMap[String(nodeId)]
103
+ if (nodeIndex !== undefined) {
104
+ rawGraph.nodes[nodeIndex].id = newValue
105
+ rawGraph.nodes[nodeIndex].properties.entity_id = newValue
106
+ delete rawGraph.nodeIdMap[String(nodeId)]
107
+ rawGraph.nodeIdMap[newValue] = nodeIndex
108
  }
109
+ } else {
110
+ const updatedAttributes = { ...nodeAttributes }
111
+ if (propertyName === 'description') {
112
+ updatedAttributes.description = newValue
113
+ }
114
+ Object.entries(updatedAttributes).forEach(([key, value]) => {
115
+ sigmaGraph.setNodeAttribute(String(nodeId), key, value)
116
+ })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
+ const nodeIndex = rawGraph.nodeIdMap[String(nodeId)]
119
+ if (nodeIndex !== undefined) {
120
+ rawGraph.nodes[nodeIndex].properties[propertyName] = newValue
121
+ }
122
+ }
123
 
124
+ const selectedNode = useGraphStore.getState().selectedNode
125
+ if (selectedNode === String(nodeId)) {
126
+ useGraphStore.getState().setSelectedNode(newValue)
127
+ }
128
 
129
+ const focusedNode = useGraphStore.getState().focusedNode
130
+ if (focusedNode === String(nodeId)) {
131
+ useGraphStore.getState().setFocusedNode(newValue)
132
+ }
 
 
133
 
134
+ sigmaInstance.refresh()
135
+ } catch (error) {
136
+ console.error('Error updating node in graph:', error)
137
+ throw new Error('Failed to update node in graph')
138
+ }
139
+ }
140
 
141
+ const handleSave = async () => {
142
+ if (isSubmitting) return
143
 
144
+ if (editValue === String(value)) {
145
+ setIsEditing(false)
146
+ return
147
+ }
 
 
 
 
148
 
149
+ setIsSubmitting(true)
 
150
 
151
+ try {
152
+ const updatedData: Record<string, any> = {}
 
 
 
153
 
154
+ if (entityType === 'node' && entityId) {
155
+ if (name === 'entity_id') {
156
+ if (editValue !== String(value)) {
157
+ const exists = await checkEntityNameExists(editValue)
158
+ if (exists) {
159
+ toast.error(t('graphPanel.propertiesView.errors.duplicateName'))
160
+ return
 
 
161
  }
162
  }
163
+ updatedData['entity_name'] = editValue
164
+ await updateEntity(String(value), updatedData, true)
165
+ await updateGraphNode(String(value), 'entity_id', editValue)
 
166
  } else {
 
167
  updatedData[name] = editValue
168
+ await updateEntity(entityId, updatedData)
169
+ if (name === 'description') {
170
+ await updateGraphNode(entityId, name, editValue)
171
+ }
172
  }
 
173
  toast.success(t('graphPanel.propertiesView.success.entityUpdated'))
174
  } else if (entityType === 'edge' && sourceId && targetId) {
 
 
175
  updatedData[name] = editValue
176
  await updateRelation(sourceId, targetId, updatedData)
177
  toast.success(t('graphPanel.propertiesView.success.relationUpdated'))
178
  }
179
 
 
180
  if (onValueChange) {
181
  onValueChange(editValue)
182
  }
 
 
183
 
184
+ useGraphStore.getState().setGraphDataFetchAttempted(false)
185
+ useGraphStore.getState().setLabelsFetchAttempted(false)
186
+
187
+ const currentNodeId = name === 'entity_id' ? editValue : (entityId || '')
188
+ useGraphStore.getState().setSelectedNode(null)
189
+ useGraphStore.getState().setSelectedNode(currentNodeId)
190
+ } catch (error: any) {
191
+ console.error('Error updating property:', error)
192
+ let detailMessage = t('graphPanel.propertiesView.errors.updateFailed')
193
 
194
  if (error.response?.data?.detail) {
195
+ detailMessage = error.response.data.detail
196
  } else if (error.response?.data?.message) {
197
+ detailMessage = error.response.data.message
198
  } else if (error.message) {
199
+ detailMessage = error.message
200
  }
201
 
 
202
  console.error('Update failed:', {
203
  entityType,
204
  entityId,
205
  propertyName: name,
206
  newValue: editValue,
207
  error: error.response?.data || error.message
208
+ })
209
 
210
  toast.error(detailMessage, {
211
  description: t('graphPanel.propertiesView.errors.tryAgainLater')
212
+ })
 
213
  } finally {
 
 
 
 
 
 
 
 
 
 
 
214
  setIsSubmitting(false)
215
  setIsEditing(false)
216
  }