Fix duplicate api requuests for graph fetching

- Optimize graph data fetching conditions
- Add isFetching state to prevent duplicate requests
- Improve label selection handling
This commit is contained in:
yangdx
2025-03-12 18:53:28 +08:00
parent 0f61d368f8
commit 215dd76a46
6 changed files with 165 additions and 133 deletions

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
<link rel="icon" type="image/svg+xml" href="./logo.png" /> <link rel="icon" type="image/svg+xml" href="./logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lightrag</title> <title>Lightrag</title>
<script type="module" crossorigin src="./assets/index-B9TRs-Wk.js"></script> <script type="module" crossorigin src="./assets/index-fJflQM9b.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-DRGuXfZw.css"> <link rel="stylesheet" crossorigin href="./assets/index-DRGuXfZw.css">
</head> </head>
<body> <body>

View File

@@ -1,10 +1,11 @@
import { useLoadGraph, useRegisterEvents, useSetSettings, useSigma } from '@react-sigma/core' import { useLoadGraph, useRegisterEvents, useSetSettings, useSigma } from '@react-sigma/core'
import Graph from 'graphology'
// import { useLayoutCircular } from '@react-sigma/layout-circular' // import { useLayoutCircular } from '@react-sigma/layout-circular'
import { useLayoutForceAtlas2 } from '@react-sigma/layout-forceatlas2' import { useLayoutForceAtlas2 } from '@react-sigma/layout-forceatlas2'
import { useEffect } from 'react' import { useEffect } from 'react'
// import useRandomGraph, { EdgeType, NodeType } from '@/hooks/useRandomGraph' // import useRandomGraph, { EdgeType, NodeType } from '@/hooks/useRandomGraph'
import useLightragGraph, { EdgeType, NodeType } from '@/hooks/useLightragGraph' import { EdgeType, NodeType } from '@/hooks/useLightragGraph'
import useTheme from '@/hooks/useTheme' import useTheme from '@/hooks/useTheme'
import * as Constants from '@/lib/constants' import * as Constants from '@/lib/constants'
@@ -21,7 +22,6 @@ const isButtonPressed = (ev: MouseEvent | TouchEvent) => {
} }
const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) => { const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean }) => {
const { lightrageGraph } = useLightragGraph()
const sigma = useSigma<NodeType, EdgeType>() const sigma = useSigma<NodeType, EdgeType>()
const registerEvents = useRegisterEvents<NodeType, EdgeType>() const registerEvents = useRegisterEvents<NodeType, EdgeType>()
const setSettings = useSetSettings<NodeType, EdgeType>() const setSettings = useSetSettings<NodeType, EdgeType>()
@@ -38,17 +38,18 @@ const GraphControl = ({ disableHoverEffect }: { disableHoverEffect?: boolean })
const focusedNode = useGraphStore.use.focusedNode() const focusedNode = useGraphStore.use.focusedNode()
const selectedEdge = useGraphStore.use.selectedEdge() const selectedEdge = useGraphStore.use.selectedEdge()
const focusedEdge = useGraphStore.use.focusedEdge() const focusedEdge = useGraphStore.use.focusedEdge()
const sigmaGraph = useGraphStore.use.sigmaGraph()
/** /**
* When component mount or maxIterations changes * When component mount or maxIterations changes
* => load the graph and apply layout * => load the graph and apply layout
*/ */
useEffect(() => { useEffect(() => {
// Create & load the graph if (sigmaGraph) {
const graph = lightrageGraph() loadGraph(sigmaGraph as unknown as Graph<NodeType, EdgeType>)
loadGraph(graph)
assignLayout() assignLayout()
}, [assignLayout, loadGraph, lightrageGraph, maxIterations]) }
}, [assignLayout, loadGraph, sigmaGraph, maxIterations])
/** /**
* When component mount * When component mount

View File

@@ -57,8 +57,11 @@ const GraphLabels = () => {
const currentLabel = useSettingsStore.getState().queryLabel 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 '*' // When selecting the same label (except '*'), switch to '*'
if (newLabel === currentLabel && newLabel !== '*') {
useSettingsStore.getState().setQueryLabel('*') useSettingsStore.getState().setQueryLabel('*')
} else { } else {
useSettingsStore.getState().setQueryLabel(newLabel) useSettingsStore.getState().setQueryLabel(newLabel)

View File

@@ -162,13 +162,32 @@ const createSigmaGraph = (rawGraph: RawGraph | null) => {
} }
const useLightrangeGraph = () => { const useLightrangeGraph = () => {
// Use useRef to maintain lastQueryLabel state between renders
const lastQueryLabelRef = useRef({ label: '', maxQueryDepth: 0, minDegree: 0 })
const queryLabel = useSettingsStore.use.queryLabel() const queryLabel = useSettingsStore.use.queryLabel()
const rawGraph = useGraphStore.use.rawGraph() const rawGraph = useGraphStore.use.rawGraph()
const sigmaGraph = useGraphStore.use.sigmaGraph() const sigmaGraph = useGraphStore.use.sigmaGraph()
const maxQueryDepth = useSettingsStore.use.graphQueryMaxDepth() const maxQueryDepth = useSettingsStore.use.graphQueryMaxDepth()
const minDegree = useSettingsStore.use.graphMinDegree() const minDegree = useSettingsStore.use.graphMinDegree()
const isFetching = useGraphStore.use.isFetching()
// Use ref to track fetch status
const fetchStatusRef = useRef<Record<string, boolean>>({});
// Track previous parameters to detect actual changes
const prevParamsRef = useRef({ queryLabel, maxQueryDepth, minDegree });
// Reset fetch status only when parameters actually change
useEffect(() => {
const prevParams = prevParamsRef.current;
if (prevParams.queryLabel !== queryLabel ||
prevParams.maxQueryDepth !== maxQueryDepth ||
prevParams.minDegree !== minDegree) {
useGraphStore.getState().setIsFetching(false);
// Reset fetch status for new parameters
fetchStatusRef.current = {};
// Update previous parameters
prevParamsRef.current = { queryLabel, maxQueryDepth, minDegree };
}
}, [queryLabel, maxQueryDepth, minDegree])
const getNode = useCallback( const getNode = useCallback(
(nodeId: string) => { (nodeId: string) => {
@@ -186,17 +205,12 @@ const useLightrangeGraph = () => {
useEffect(() => { useEffect(() => {
if (queryLabel) { if (queryLabel) {
// Always fetch data for "*" label const fetchKey = `${queryLabel}-${maxQueryDepth}-${minDegree}`;
// For other labels, only fetch when parameters change
const shouldUpdate = true;
if (shouldUpdate) {
lastQueryLabelRef.current = {
label: queryLabel,
maxQueryDepth,
minDegree
}
// Only fetch if we haven't fetched this combination in the current component lifecycle
if (!isFetching && !fetchStatusRef.current[fetchKey]) {
useGraphStore.getState().setIsFetching(true);
fetchStatusRef.current[fetchKey] = true;
fetchGraph(queryLabel, maxQueryDepth, minDegree).then((data) => { fetchGraph(queryLabel, maxQueryDepth, minDegree).then((data) => {
const state = useGraphStore.getState() const state = useGraphStore.getState()
const newSigmaGraph = createSigmaGraph(data) const newSigmaGraph = createSigmaGraph(data)
@@ -227,6 +241,16 @@ const useLightrangeGraph = () => {
// Ensure * is there eventhough there is no graph data // Ensure * is there eventhough there is no graph data
state.setGraphLabels(['*']); state.setGraphLabels(['*']);
} }
if (!data) {
// If data is invalid, remove the fetch flag to allow retry
delete fetchStatusRef.current[fetchKey];
}
// Reset fetching state after all updates are complete
state.setIsFetching(false);
}).catch(() => {
// Reset fetching state and remove flag in case of error
useGraphStore.getState().setIsFetching(false);
delete fetchStatusRef.current[fetchKey];
}) })
} }
} else { } else {

View File

@@ -68,6 +68,7 @@ interface GraphState {
graphLabels: string[] graphLabels: string[]
moveToSelectedNode: boolean moveToSelectedNode: boolean
isFetching: boolean
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => void setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => void
setFocusedNode: (nodeId: string | null) => void setFocusedNode: (nodeId: string | null) => void
@@ -81,6 +82,7 @@ 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
setIsFetching: (isFetching: boolean) => void
} }
const useGraphStoreBase = create<GraphState>()((set) => ({ const useGraphStoreBase = create<GraphState>()((set) => ({
@@ -90,11 +92,13 @@ const useGraphStoreBase = create<GraphState>()((set) => ({
focusedEdge: null, focusedEdge: null,
moveToSelectedNode: false, moveToSelectedNode: false,
isFetching: false,
rawGraph: null, rawGraph: null,
sigmaGraph: null, sigmaGraph: null,
graphLabels: ['*'], graphLabels: ['*'],
setIsFetching: (isFetching: boolean) => set({ isFetching }),
setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) => setSelectedNode: (nodeId: string | null, moveToSelectedNode?: boolean) =>
set({ selectedNode: nodeId, moveToSelectedNode }), set({ selectedNode: nodeId, moveToSelectedNode }),
setFocusedNode: (nodeId: string | null) => set({ focusedNode: nodeId }), setFocusedNode: (nodeId: string | null) => set({ focusedNode: nodeId }),