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:
@@ -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
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@@ -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];
|
||||||
|
@@ -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 })
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user