yangdx commited on
Commit
a5f80ff
·
1 Parent(s): 88fc1cb

fix: Replace global searchCache with Zustand state management

Browse files
lightrag_webui/src/components/graph/GraphSearch.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { FC, useCallback, useEffect, useMemo } from 'react'
2
  import {
3
  EdgeById,
4
  NodeById,
@@ -11,7 +11,9 @@ import { useGraphStore } from '@/stores/graph'
11
  import MiniSearch from 'minisearch'
12
  import { useTranslation } from 'react-i18next'
13
  import { OptionItem } from './graphSearchTypes'
14
- import { messageId, searchCache } from './graphSearchUtils'
 
 
15
 
16
  const NodeOption = ({ id }: { id: string }) => {
17
  const graph = useGraphStore.use.sigmaGraph()
@@ -46,25 +48,24 @@ export const GraphSearchInput = ({
46
  }) => {
47
  const { t } = useTranslation()
48
  const graph = useGraphStore.use.sigmaGraph()
 
49
 
50
- // Force reset the cache when graph changes
51
  useEffect(() => {
52
  if (graph) {
53
- // Reset cache to ensure fresh search results with new graph data
54
- searchCache.graph = null;
55
- searchCache.searchEngine = null;
56
  }
57
  }, [graph]);
58
 
59
- const searchEngine = useMemo(() => {
60
- if (searchCache.graph == graph) {
61
- return searchCache.searchEngine
 
 
62
  }
63
- if (!graph || graph.nodes().length == 0) return
64
-
65
- searchCache.graph = graph
66
 
67
- const searchEngine = new MiniSearch({
 
68
  idField: 'id',
69
  fields: ['label'],
70
  searchOptions: {
@@ -76,16 +77,16 @@ export const GraphSearchInput = ({
76
  }
77
  })
78
 
79
- // Add documents
80
  const documents = graph.nodes().map((id: string) => ({
81
  id: id,
82
  label: graph.getNodeAttribute(id, 'label')
83
  }))
84
- searchEngine.addAll(documents)
85
 
86
- searchCache.searchEngine = searchEngine
87
- return searchEngine
88
- }, [graph])
89
 
90
  /**
91
  * Loading the options while the user is typing.
@@ -96,9 +97,6 @@ export const GraphSearchInput = ({
96
 
97
  // Safety checks to prevent crashes
98
  if (!graph || !searchEngine) {
99
- // Reset cache to ensure fresh search engine initialization on next render
100
- searchCache.graph = null
101
- searchCache.searchEngine = null
102
  return []
103
  }
104
 
@@ -107,7 +105,7 @@ export const GraphSearchInput = ({
107
  return []
108
  }
109
 
110
- // If no query, return first searchResultLimit nodes that exist
111
  if (!query) {
112
  const nodeIds = graph.nodes()
113
  .filter(id => graph.hasNode(id))
 
1
+ import { FC, useCallback, useEffect } from 'react'
2
  import {
3
  EdgeById,
4
  NodeById,
 
11
  import MiniSearch from 'minisearch'
12
  import { useTranslation } from 'react-i18next'
13
  import { OptionItem } from './graphSearchTypes'
14
+
15
+ // Message item identifier for search results
16
+ export const messageId = '__message_item'
17
 
18
  const NodeOption = ({ id }: { id: string }) => {
19
  const graph = useGraphStore.use.sigmaGraph()
 
48
  }) => {
49
  const { t } = useTranslation()
50
  const graph = useGraphStore.use.sigmaGraph()
51
+ const searchEngine = useGraphStore.use.searchEngine()
52
 
53
+ // Reset search engine when graph changes
54
  useEffect(() => {
55
  if (graph) {
56
+ useGraphStore.getState().resetSearchEngine()
 
 
57
  }
58
  }, [graph]);
59
 
60
+ // Create search engine when needed
61
+ useEffect(() => {
62
+ // Skip if no graph, empty graph, or search engine already exists
63
+ if (!graph || graph.nodes().length === 0 || searchEngine) {
64
+ return
65
  }
 
 
 
66
 
67
+ // Create new search engine
68
+ const newSearchEngine = new MiniSearch({
69
  idField: 'id',
70
  fields: ['label'],
71
  searchOptions: {
 
77
  }
78
  })
79
 
80
+ // Add nodes to search engine
81
  const documents = graph.nodes().map((id: string) => ({
82
  id: id,
83
  label: graph.getNodeAttribute(id, 'label')
84
  }))
85
+ newSearchEngine.addAll(documents)
86
 
87
+ // Update search engine in store
88
+ useGraphStore.getState().setSearchEngine(newSearchEngine)
89
+ }, [graph, searchEngine])
90
 
91
  /**
92
  * Loading the options while the user is typing.
 
97
 
98
  // Safety checks to prevent crashes
99
  if (!graph || !searchEngine) {
 
 
 
100
  return []
101
  }
102
 
 
105
  return []
106
  }
107
 
108
+ // If no query, return some nodes for user to select
109
  if (!query) {
110
  const nodeIds = graph.nodes()
111
  .filter(id => graph.hasNode(id))
lightrag_webui/src/components/graph/graphSearchUtils.ts DELETED
@@ -1,13 +0,0 @@
1
- import { DirectedGraph } from 'graphology'
2
- import MiniSearch from 'minisearch'
3
-
4
- export const messageId = '__message_item'
5
-
6
- // Reset this cache when graph changes to ensure fresh search results
7
- export const searchCache: {
8
- graph: DirectedGraph | null;
9
- searchEngine: MiniSearch | null;
10
- } = {
11
- graph: null,
12
- searchEngine: null
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lightrag_webui/src/hooks/useLightragGraph.tsx CHANGED
@@ -11,7 +11,6 @@ import { useSettingsStore } from '@/stores/settings'
11
  import { useTabVisibility } from '@/contexts/useTabVisibility'
12
 
13
  import seedrandom from 'seedrandom'
14
- import { searchCache } from '@/components/graph/graphSearchUtils'
15
 
16
  const validateGraph = (graph: RawGraph) => {
17
  if (!graph) {
@@ -599,9 +598,8 @@ const useLightrangeGraph = () => {
599
  // Update the dynamic edge map and invalidate search cache
600
  rawGraph.buildDynamicMap();
601
 
602
- // Force search engine rebuild by invalidating cache
603
- searchCache.graph = null;
604
- searchCache.searchEngine = null;
605
 
606
  // Update sizes for all nodes with discarded edges
607
  updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, range, scale);
@@ -718,9 +716,8 @@ const useLightrangeGraph = () => {
718
  // Rebuild the dynamic edge map and invalidate search cache
719
  rawGraph.buildDynamicMap();
720
 
721
- // Force search engine rebuild by invalidating cache
722
- searchCache.graph = null;
723
- searchCache.searchEngine = null;
724
 
725
  // Show notification if we deleted more than just the selected node
726
  if (nodesToDelete.size > 1) {
 
11
  import { useTabVisibility } from '@/contexts/useTabVisibility'
12
 
13
  import seedrandom from 'seedrandom'
 
14
 
15
  const validateGraph = (graph: RawGraph) => {
16
  if (!graph) {
 
598
  // Update the dynamic edge map and invalidate search cache
599
  rawGraph.buildDynamicMap();
600
 
601
+ // Reset search engine to force rebuild
602
+ useGraphStore.getState().resetSearchEngine();
 
603
 
604
  // Update sizes for all nodes with discarded edges
605
  updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, range, scale);
 
716
  // Rebuild the dynamic edge map and invalidate search cache
717
  rawGraph.buildDynamicMap();
718
 
719
+ // Reset search engine to force rebuild
720
+ useGraphStore.getState().resetSearchEngine();
 
721
 
722
  // Show notification if we deleted more than just the selected node
723
  if (nodesToDelete.size > 1) {
lightrag_webui/src/stores/graph.ts CHANGED
@@ -2,6 +2,7 @@ import { create } from 'zustand'
2
  import { createSelectors } from '@/lib/utils'
3
  import { DirectedGraph } from 'graphology'
4
  import { getGraphLabels } from '@/api/lightrag'
 
5
 
6
  export type RawNodeType = {
7
  id: string
@@ -69,6 +70,9 @@ interface GraphState {
69
  sigmaInstance: any | null
70
  allDatabaseLabels: string[]
71
 
 
 
 
72
  moveToSelectedNode: boolean
73
  isFetching: boolean
74
  shouldRender: boolean
@@ -94,6 +98,10 @@ interface GraphState {
94
  setIsFetching: (isFetching: boolean) => void
95
  setShouldRender: (shouldRender: boolean) => void
96
 
 
 
 
 
97
  // Methods to set global flags
98
  setGraphDataFetchAttempted: (attempted: boolean) => void
99
  setLabelsFetchAttempted: (attempted: boolean) => void
@@ -126,6 +134,8 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
126
  sigmaInstance: null,
127
  allDatabaseLabels: ['*'],
128
 
 
 
129
 
130
  setIsFetching: (isFetching: boolean) => set({ isFetching }),
131
  setShouldRender: (shouldRender: boolean) => set({ shouldRender }),
@@ -149,6 +159,7 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
149
  focusedEdge: null,
150
  rawGraph: null,
151
  sigmaGraph: null, // to avoid other components from acccessing graph objects
 
152
  moveToSelectedNode: false,
153
  shouldRender: false
154
  });
@@ -183,6 +194,9 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
183
 
184
  setSigmaInstance: (instance: any) => set({ sigmaInstance: instance }),
185
 
 
 
 
186
  // Methods to set global flags
187
  setGraphDataFetchAttempted: (attempted: boolean) => set({ graphDataFetchAttempted: attempted }),
188
  setLabelsFetchAttempted: (attempted: boolean) => set({ labelsFetchAttempted: attempted }),
 
2
  import { createSelectors } from '@/lib/utils'
3
  import { DirectedGraph } from 'graphology'
4
  import { getGraphLabels } from '@/api/lightrag'
5
+ import MiniSearch from 'minisearch'
6
 
7
  export type RawNodeType = {
8
  id: string
 
70
  sigmaInstance: any | null
71
  allDatabaseLabels: string[]
72
 
73
+ // 搜索引擎状态
74
+ searchEngine: MiniSearch | null
75
+
76
  moveToSelectedNode: boolean
77
  isFetching: boolean
78
  shouldRender: boolean
 
98
  setIsFetching: (isFetching: boolean) => void
99
  setShouldRender: (shouldRender: boolean) => void
100
 
101
+ // 搜索引擎方法
102
+ setSearchEngine: (engine: MiniSearch | null) => void
103
+ resetSearchEngine: () => void
104
+
105
  // Methods to set global flags
106
  setGraphDataFetchAttempted: (attempted: boolean) => void
107
  setLabelsFetchAttempted: (attempted: boolean) => void
 
134
  sigmaInstance: null,
135
  allDatabaseLabels: ['*'],
136
 
137
+ searchEngine: null,
138
+
139
 
140
  setIsFetching: (isFetching: boolean) => set({ isFetching }),
141
  setShouldRender: (shouldRender: boolean) => set({ shouldRender }),
 
159
  focusedEdge: null,
160
  rawGraph: null,
161
  sigmaGraph: null, // to avoid other components from acccessing graph objects
162
+ searchEngine: null, // 重置搜索引擎
163
  moveToSelectedNode: false,
164
  shouldRender: false
165
  });
 
194
 
195
  setSigmaInstance: (instance: any) => set({ sigmaInstance: instance }),
196
 
197
+ setSearchEngine: (engine: MiniSearch | null) => set({ searchEngine: engine }),
198
+ resetSearchEngine: () => set({ searchEngine: null }),
199
+
200
  // Methods to set global flags
201
  setGraphDataFetchAttempted: (attempted: boolean) => set({ graphDataFetchAttempted: attempted }),
202
  setLabelsFetchAttempted: (attempted: boolean) => set({ labelsFetchAttempted: attempted }),