fix: Replace global searchCache with Zustand state management

This commit is contained in:
yangdx
2025-03-16 02:40:48 +08:00
parent 6b2e4186ac
commit eb17a7782d
4 changed files with 38 additions and 42 deletions

View File

@@ -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))

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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 }),