fix: Replace global searchCache with Zustand state management
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { FC, useCallback, useEffect, useMemo } from 'react'
|
import { FC, useCallback, useEffect } from 'react'
|
||||||
import {
|
import {
|
||||||
EdgeById,
|
EdgeById,
|
||||||
NodeById,
|
NodeById,
|
||||||
@@ -11,7 +11,9 @@ import { useGraphStore } from '@/stores/graph'
|
|||||||
import MiniSearch from 'minisearch'
|
import MiniSearch from 'minisearch'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { OptionItem } from './graphSearchTypes'
|
import { OptionItem } from './graphSearchTypes'
|
||||||
import { messageId, searchCache } from './graphSearchUtils'
|
|
||||||
|
// Message item identifier for search results
|
||||||
|
export const messageId = '__message_item'
|
||||||
|
|
||||||
const NodeOption = ({ id }: { id: string }) => {
|
const NodeOption = ({ id }: { id: string }) => {
|
||||||
const graph = useGraphStore.use.sigmaGraph()
|
const graph = useGraphStore.use.sigmaGraph()
|
||||||
@@ -46,25 +48,24 @@ export const GraphSearchInput = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const graph = useGraphStore.use.sigmaGraph()
|
const graph = useGraphStore.use.sigmaGraph()
|
||||||
|
const searchEngine = useGraphStore.use.searchEngine()
|
||||||
|
|
||||||
// Force reset the cache when graph changes
|
// Reset search engine when graph changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (graph) {
|
if (graph) {
|
||||||
// Reset cache to ensure fresh search results with new graph data
|
useGraphStore.getState().resetSearchEngine()
|
||||||
searchCache.graph = null;
|
|
||||||
searchCache.searchEngine = null;
|
|
||||||
}
|
}
|
||||||
}, [graph]);
|
}, [graph]);
|
||||||
|
|
||||||
const searchEngine = useMemo(() => {
|
// Create search engine when needed
|
||||||
if (searchCache.graph == graph) {
|
useEffect(() => {
|
||||||
return searchCache.searchEngine
|
// Skip if no graph, empty graph, or search engine already exists
|
||||||
|
if (!graph || graph.nodes().length === 0 || searchEngine) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if (!graph || graph.nodes().length == 0) return
|
|
||||||
|
|
||||||
searchCache.graph = graph
|
// Create new search engine
|
||||||
|
const newSearchEngine = new MiniSearch({
|
||||||
const searchEngine = new MiniSearch({
|
|
||||||
idField: 'id',
|
idField: 'id',
|
||||||
fields: ['label'],
|
fields: ['label'],
|
||||||
searchOptions: {
|
searchOptions: {
|
||||||
@@ -76,16 +77,16 @@ export const GraphSearchInput = ({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add documents
|
// Add nodes to search engine
|
||||||
const documents = graph.nodes().map((id: string) => ({
|
const documents = graph.nodes().map((id: string) => ({
|
||||||
id: id,
|
id: id,
|
||||||
label: graph.getNodeAttribute(id, 'label')
|
label: graph.getNodeAttribute(id, 'label')
|
||||||
}))
|
}))
|
||||||
searchEngine.addAll(documents)
|
newSearchEngine.addAll(documents)
|
||||||
|
|
||||||
searchCache.searchEngine = searchEngine
|
// Update search engine in store
|
||||||
return searchEngine
|
useGraphStore.getState().setSearchEngine(newSearchEngine)
|
||||||
}, [graph])
|
}, [graph, searchEngine])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loading the options while the user is typing.
|
* Loading the options while the user is typing.
|
||||||
@@ -96,9 +97,6 @@ export const GraphSearchInput = ({
|
|||||||
|
|
||||||
// Safety checks to prevent crashes
|
// Safety checks to prevent crashes
|
||||||
if (!graph || !searchEngine) {
|
if (!graph || !searchEngine) {
|
||||||
// Reset cache to ensure fresh search engine initialization on next render
|
|
||||||
searchCache.graph = null
|
|
||||||
searchCache.searchEngine = null
|
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +105,7 @@ export const GraphSearchInput = ({
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no query, return first searchResultLimit nodes that exist
|
// If no query, return some nodes for user to select
|
||||||
if (!query) {
|
if (!query) {
|
||||||
const nodeIds = graph.nodes()
|
const nodeIds = graph.nodes()
|
||||||
.filter(id => graph.hasNode(id))
|
.filter(id => graph.hasNode(id))
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
import { DirectedGraph } from 'graphology'
|
|
||||||
import MiniSearch from 'minisearch'
|
|
||||||
|
|
||||||
export const messageId = '__message_item'
|
|
||||||
|
|
||||||
// Reset this cache when graph changes to ensure fresh search results
|
|
||||||
export const searchCache: {
|
|
||||||
graph: DirectedGraph | null;
|
|
||||||
searchEngine: MiniSearch | null;
|
|
||||||
} = {
|
|
||||||
graph: null,
|
|
||||||
searchEngine: null
|
|
||||||
}
|
|
@@ -11,7 +11,6 @@ import { useSettingsStore } from '@/stores/settings'
|
|||||||
import { useTabVisibility } from '@/contexts/useTabVisibility'
|
import { useTabVisibility } from '@/contexts/useTabVisibility'
|
||||||
|
|
||||||
import seedrandom from 'seedrandom'
|
import seedrandom from 'seedrandom'
|
||||||
import { searchCache } from '@/components/graph/graphSearchUtils'
|
|
||||||
|
|
||||||
const validateGraph = (graph: RawGraph) => {
|
const validateGraph = (graph: RawGraph) => {
|
||||||
if (!graph) {
|
if (!graph) {
|
||||||
@@ -599,9 +598,8 @@ const useLightrangeGraph = () => {
|
|||||||
// Update the dynamic edge map and invalidate search cache
|
// Update the dynamic edge map and invalidate search cache
|
||||||
rawGraph.buildDynamicMap();
|
rawGraph.buildDynamicMap();
|
||||||
|
|
||||||
// Force search engine rebuild by invalidating cache
|
// Reset search engine to force rebuild
|
||||||
searchCache.graph = null;
|
useGraphStore.getState().resetSearchEngine();
|
||||||
searchCache.searchEngine = null;
|
|
||||||
|
|
||||||
// Update sizes for all nodes with discarded edges
|
// Update sizes for all nodes with discarded edges
|
||||||
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, range, scale);
|
updateNodeSizes(sigmaGraph, nodesWithDiscardedEdges, minDegree, range, scale);
|
||||||
@@ -718,9 +716,8 @@ const useLightrangeGraph = () => {
|
|||||||
// Rebuild the dynamic edge map and invalidate search cache
|
// Rebuild the dynamic edge map and invalidate search cache
|
||||||
rawGraph.buildDynamicMap();
|
rawGraph.buildDynamicMap();
|
||||||
|
|
||||||
// Force search engine rebuild by invalidating cache
|
// Reset search engine to force rebuild
|
||||||
searchCache.graph = null;
|
useGraphStore.getState().resetSearchEngine();
|
||||||
searchCache.searchEngine = null;
|
|
||||||
|
|
||||||
// Show notification if we deleted more than just the selected node
|
// Show notification if we deleted more than just the selected node
|
||||||
if (nodesToDelete.size > 1) {
|
if (nodesToDelete.size > 1) {
|
||||||
|
@@ -2,6 +2,7 @@ import { create } from 'zustand'
|
|||||||
import { createSelectors } from '@/lib/utils'
|
import { createSelectors } from '@/lib/utils'
|
||||||
import { DirectedGraph } from 'graphology'
|
import { DirectedGraph } from 'graphology'
|
||||||
import { getGraphLabels } from '@/api/lightrag'
|
import { getGraphLabels } from '@/api/lightrag'
|
||||||
|
import MiniSearch from 'minisearch'
|
||||||
|
|
||||||
export type RawNodeType = {
|
export type RawNodeType = {
|
||||||
id: string
|
id: string
|
||||||
@@ -69,6 +70,9 @@ interface GraphState {
|
|||||||
sigmaInstance: any | null
|
sigmaInstance: any | null
|
||||||
allDatabaseLabels: string[]
|
allDatabaseLabels: string[]
|
||||||
|
|
||||||
|
// 搜索引擎状态
|
||||||
|
searchEngine: MiniSearch | null
|
||||||
|
|
||||||
moveToSelectedNode: boolean
|
moveToSelectedNode: boolean
|
||||||
isFetching: boolean
|
isFetching: boolean
|
||||||
shouldRender: boolean
|
shouldRender: boolean
|
||||||
@@ -94,6 +98,10 @@ interface GraphState {
|
|||||||
setIsFetching: (isFetching: boolean) => void
|
setIsFetching: (isFetching: boolean) => void
|
||||||
setShouldRender: (shouldRender: boolean) => void
|
setShouldRender: (shouldRender: boolean) => void
|
||||||
|
|
||||||
|
// 搜索引擎方法
|
||||||
|
setSearchEngine: (engine: MiniSearch | null) => void
|
||||||
|
resetSearchEngine: () => void
|
||||||
|
|
||||||
// Methods to set global flags
|
// Methods to set global flags
|
||||||
setGraphDataFetchAttempted: (attempted: boolean) => void
|
setGraphDataFetchAttempted: (attempted: boolean) => void
|
||||||
setLabelsFetchAttempted: (attempted: boolean) => void
|
setLabelsFetchAttempted: (attempted: boolean) => void
|
||||||
@@ -126,6 +134,8 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
|
|||||||
sigmaInstance: null,
|
sigmaInstance: null,
|
||||||
allDatabaseLabels: ['*'],
|
allDatabaseLabels: ['*'],
|
||||||
|
|
||||||
|
searchEngine: null,
|
||||||
|
|
||||||
|
|
||||||
setIsFetching: (isFetching: boolean) => set({ isFetching }),
|
setIsFetching: (isFetching: boolean) => set({ isFetching }),
|
||||||
setShouldRender: (shouldRender: boolean) => set({ shouldRender }),
|
setShouldRender: (shouldRender: boolean) => set({ shouldRender }),
|
||||||
@@ -149,6 +159,7 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
|
|||||||
focusedEdge: null,
|
focusedEdge: null,
|
||||||
rawGraph: null,
|
rawGraph: null,
|
||||||
sigmaGraph: null, // to avoid other components from acccessing graph objects
|
sigmaGraph: null, // to avoid other components from acccessing graph objects
|
||||||
|
searchEngine: null, // 重置搜索引擎
|
||||||
moveToSelectedNode: false,
|
moveToSelectedNode: false,
|
||||||
shouldRender: false
|
shouldRender: false
|
||||||
});
|
});
|
||||||
@@ -183,6 +194,9 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
|
|||||||
|
|
||||||
setSigmaInstance: (instance: any) => set({ sigmaInstance: instance }),
|
setSigmaInstance: (instance: any) => set({ sigmaInstance: instance }),
|
||||||
|
|
||||||
|
setSearchEngine: (engine: MiniSearch | null) => set({ searchEngine: engine }),
|
||||||
|
resetSearchEngine: () => set({ searchEngine: null }),
|
||||||
|
|
||||||
// Methods to set global flags
|
// Methods to set global flags
|
||||||
setGraphDataFetchAttempted: (attempted: boolean) => set({ graphDataFetchAttempted: attempted }),
|
setGraphDataFetchAttempted: (attempted: boolean) => set({ graphDataFetchAttempted: attempted }),
|
||||||
setLabelsFetchAttempted: (attempted: boolean) => set({ labelsFetchAttempted: attempted }),
|
setLabelsFetchAttempted: (attempted: boolean) => set({ labelsFetchAttempted: attempted }),
|
||||||
|
Reference in New Issue
Block a user