Skip to content

Commit b25c8cc

Browse files
fix: skip exhausted search pages
1 parent 5b96dcb commit b25c8cc

3 files changed

Lines changed: 87 additions & 4 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@hyperdx/app': patch
3+
---
4+
5+
Skip redundant offset queries after a search returns fewer rows than the
6+
requested page size.

‎packages/app/src/hooks/__tests__/useOffsetPaginatedQuery.test.tsx‎

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,84 @@ describe('useOffsetPaginatedQuery', () => {
388388
expect(result.current.data).toBeDefined();
389389
expect(result.current.data?.window.windowIndex).toBe(0);
390390

391-
// Should have more pages available due to large time range
392-
expect(result.current.hasNextPage).toBe(true);
391+
// The only window has fewer rows than the requested limit.
392+
expect(result.current.hasNextPage).toBe(false);
393393
});
394394
});
395395

396396
describe('Pagination Within Time Windows', () => {
397+
it.each(['Timestamp DESC', 'Timestamp ASC'])(
398+
'moves straight to the next window after a partial page (%s)',
399+
async orderBy => {
400+
const config = createMockChartConfig({
401+
orderBy,
402+
limit: { limit: 100, offset: 0 },
403+
});
404+
mockReader.read
405+
.mockResolvedValueOnce({
406+
done: false,
407+
value: [
408+
{ json: () => ['message'] },
409+
{ json: () => ['String'] },
410+
{ json: () => ['first window'] },
411+
],
412+
})
413+
.mockResolvedValueOnce({ done: true });
414+
415+
const { result } = renderHook(() => useOffsetPaginatedQuery(config), {
416+
wrapper,
417+
});
418+
await waitFor(() => expect(result.current.isLoading).toBe(false));
419+
expect(result.current.data?.data).toHaveLength(1);
420+
expect(result.current.hasNextPage).toBe(true);
421+
422+
mockReader.read.mockResolvedValueOnce({ done: true });
423+
await act(async () => {
424+
await result.current.fetchNextPage();
425+
});
426+
427+
await waitFor(() => {
428+
expect(result.current.data?.window.windowIndex).toBe(1);
429+
});
430+
expect(jest.mocked(renderChartConfig).mock.calls[1][0]).toMatchObject({
431+
limit: { limit: 100, offset: 0 },
432+
});
433+
},
434+
);
435+
436+
it.each([
437+
['a search without windowing', { orderBy: 'message DESC' }],
438+
['a query without LIMIT', { orderBy: 'message DESC', limit: undefined }],
439+
[
440+
'the final time window',
441+
{
442+
dateRange: [
443+
new Date('2024-01-01T00:00:00Z'),
444+
new Date('2024-01-01T00:05:00Z'),
445+
] as [Date, Date],
446+
},
447+
],
448+
])('stops after a partial page for %s', async (_name, overrides) => {
449+
const config = createMockChartConfig(overrides);
450+
mockReader.read
451+
.mockResolvedValueOnce({
452+
done: false,
453+
value: [
454+
{ json: () => ['message'] },
455+
{ json: () => ['String'] },
456+
{ json: () => ['only result'] },
457+
],
458+
})
459+
.mockResolvedValueOnce({ done: true });
460+
461+
const { result } = renderHook(() => useOffsetPaginatedQuery(config), {
462+
wrapper,
463+
});
464+
await waitFor(() => expect(result.current.isLoading).toBe(false));
465+
expect(result.current.data?.data).toHaveLength(1);
466+
expect(result.current.hasNextPage).toBe(false);
467+
});
468+
397469
it('should paginate within the same time window', async () => {
398470
const config = createMockChartConfig({
399471
limit: { limit: 2, offset: 0 },

‎packages/app/src/hooks/useOffsetPaginatedQuery.tsx‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,13 @@ function getNextPageParam(
159159
0,
160160
);
161161

162-
// If we have results in the current window, continue paginating within it
163-
if (lastPage.data.length > 0) {
162+
// A short page, or a query without LIMIT, has exhausted this window.
163+
const requestedLimit = config.limit?.limit;
164+
if (
165+
lastPage.data.length > 0 &&
166+
requestedLimit != null &&
167+
lastPage.data.length >= requestedLimit
168+
) {
164169
return {
165170
windowIndex: currentWindow.windowIndex,
166171
offset: currentWindowResults,

0 commit comments

Comments
 (0)