Limit the search scope to labels in the current subgraph

- Decouple datasource label selection from the search input field
- Improve label selection handling logic
This commit is contained in:
yangdx
2025-03-13 00:20:53 +08:00
parent fa1e7b13a1
commit 727b137506
3 changed files with 45 additions and 24 deletions

View File

@@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next'
const GraphLabels = () => { const GraphLabels = () => {
const { t } = useTranslation() const { t } = useTranslation()
const label = useSettingsStore.use.queryLabel() const label = useSettingsStore.use.queryLabel()
const graphLabels = useGraphStore.use.graphLabels() const allDatabaseLabels = useGraphStore.use.allDatabaseLabels()
const getSearchEngine = useCallback(() => { const getSearchEngine = useCallback(() => {
// Create search engine // Create search engine
@@ -26,14 +26,14 @@ const GraphLabels = () => {
}) })
// Add documents // Add documents
const documents = graphLabels.map((str, index) => ({ id: index, value: str })) const documents = allDatabaseLabels.map((str, index) => ({ id: index, value: str }))
searchEngine.addAll(documents) searchEngine.addAll(documents)
return { return {
labels: graphLabels, labels: allDatabaseLabels,
searchEngine searchEngine
} }
}, [graphLabels]) }, [allDatabaseLabels])
const fetchData = useCallback( const fetchData = useCallback(
async (query?: string): Promise<string[]> => { async (query?: string): Promise<string[]> => {
@@ -47,27 +47,11 @@ const GraphLabels = () => {
return result.length <= labelListLimit return result.length <= labelListLimit
? result ? result
: [...result.slice(0, labelListLimit), t('graphLabels.andOthers', { count: result.length - labelListLimit })] : [...result.slice(0, labelListLimit), '...']
}, },
[getSearchEngine, t] [getSearchEngine, t]
) )
const setQueryLabel = useCallback((newLabel: string) => {
if (newLabel.startsWith('And ') && newLabel.endsWith(' others')) return
const currentLabel = useSettingsStore.getState().queryLabel
if (newLabel === '*' && currentLabel === '*') {
// When reselecting '*', just set it again to trigger a new fetch
useSettingsStore.getState().setQueryLabel('*')
} else if (newLabel === currentLabel && newLabel !== '*') {
// When selecting the same label (except '*'), switch to '*'
useSettingsStore.getState().setQueryLabel('*')
} else {
useSettingsStore.getState().setQueryLabel(newLabel)
}
}, [])
return ( return (
<AsyncSelect<string> <AsyncSelect<string>
className="ml-2" className="ml-2"
@@ -81,8 +65,20 @@ const GraphLabels = () => {
notFound={<div className="py-6 text-center text-sm">No labels found</div>} notFound={<div className="py-6 text-center text-sm">No labels found</div>}
label={t('graphPanel.graphLabels.label')} label={t('graphPanel.graphLabels.label')}
placeholder={t('graphPanel.graphLabels.placeholder')} placeholder={t('graphPanel.graphLabels.placeholder')}
value={label !== null ? label : ''} value={label !== null ? label : '*'}
onChange={setQueryLabel} onChange={(newLabel) => {
const currentLabel = useSettingsStore.getState().queryLabel
if (newLabel === '...') {
newLabel = '*'
}
if (newLabel === currentLabel && newLabel !== '*') {
// 选择相同标签时切换到'*'
useSettingsStore.getState().setQueryLabel('*')
} else {
useSettingsStore.getState().setQueryLabel(newLabel)
}
}}
clearable={false} // Prevent clearing value on reselect clearable={false} // Prevent clearing value on reselect
/> />
) )

View File

@@ -169,6 +169,11 @@ const useLightrangeGraph = () => {
const minDegree = useSettingsStore.use.graphMinDegree() const minDegree = useSettingsStore.use.graphMinDegree()
const isFetching = useGraphStore.use.isFetching() const isFetching = useGraphStore.use.isFetching()
// Fetch all database labels on mount
useEffect(() => {
useGraphStore.getState().fetchAllDatabaseLabels()
}, [])
// Use ref to track fetch status // Use ref to track fetch status
const fetchStatusRef = useRef<Record<string, boolean>>({}); const fetchStatusRef = useRef<Record<string, boolean>>({});
@@ -222,7 +227,7 @@ const useLightrangeGraph = () => {
state.setSigmaGraph(newSigmaGraph) state.setSigmaGraph(newSigmaGraph)
state.setRawGraph(data) state.setRawGraph(data)
// Extract labels from graph data // Extract labels from current graph data
if (data) { if (data) {
const labelSet = new Set<string>(); const labelSet = new Set<string>();
for (const node of data.nodes) { for (const node of data.nodes) {
@@ -241,6 +246,9 @@ const useLightrangeGraph = () => {
// Ensure * is there eventhough there is no graph data // Ensure * is there eventhough there is no graph data
state.setGraphLabels(['*']); state.setGraphLabels(['*']);
} }
// Fetch all database labels after graph update
state.fetchAllDatabaseLabels();
if (!data) { if (!data) {
// If data is invalid, remove the fetch flag to allow retry // If data is invalid, remove the fetch flag to allow retry
delete fetchStatusRef.current[fetchKey]; delete fetchStatusRef.current[fetchKey];

View File

@@ -1,6 +1,7 @@
import { create } from 'zustand' 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'
export type RawNodeType = { export type RawNodeType = {
id: string id: string
@@ -66,6 +67,7 @@ interface GraphState {
rawGraph: RawGraph | null rawGraph: RawGraph | null
sigmaGraph: DirectedGraph | null sigmaGraph: DirectedGraph | null
graphLabels: string[] graphLabels: string[]
allDatabaseLabels: string[]
moveToSelectedNode: boolean moveToSelectedNode: boolean
isFetching: boolean isFetching: boolean
@@ -82,6 +84,8 @@ interface GraphState {
setRawGraph: (rawGraph: RawGraph | null) => void setRawGraph: (rawGraph: RawGraph | null) => void
setSigmaGraph: (sigmaGraph: DirectedGraph | null) => void setSigmaGraph: (sigmaGraph: DirectedGraph | null) => void
setGraphLabels: (labels: string[]) => void setGraphLabels: (labels: string[]) => void
setAllDatabaseLabels: (labels: string[]) => void
fetchAllDatabaseLabels: () => Promise<void>
setIsFetching: (isFetching: boolean) => void setIsFetching: (isFetching: boolean) => void
} }
@@ -97,6 +101,7 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
rawGraph: null, rawGraph: null,
sigmaGraph: null, sigmaGraph: null,
graphLabels: ['*'], graphLabels: ['*'],
allDatabaseLabels: ['*'],
setIsFetching: (isFetching: boolean) => set({ isFetching }), setIsFetching: (isFetching: boolean) => set({ isFetching }),
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
@@ -132,6 +137,18 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
setGraphLabels: (labels: string[]) => set({ graphLabels: labels }), setGraphLabels: (labels: string[]) => set({ graphLabels: labels }),
setAllDatabaseLabels: (labels: string[]) => set({ allDatabaseLabels: labels }),
fetchAllDatabaseLabels: async () => {
try {
const labels = await getGraphLabels();
set({ allDatabaseLabels: ['*', ...labels] });
} catch (error) {
console.error('Failed to fetch all database labels:', error);
set({ allDatabaseLabels: ['*'] });
}
},
setMoveToSelectedNode: (moveToSelectedNode?: boolean) => set({ moveToSelectedNode }) setMoveToSelectedNode: (moveToSelectedNode?: boolean) => set({ moveToSelectedNode })
})) }))