Skip to content

Commit 9c9b6b9

Browse files
add isSearchExausted flag for flagging when a search captured all results
1 parent 5ab585c commit 9c9b6b9

File tree

10 files changed

+358
-279
lines changed

10 files changed

+358
-279
lines changed

packages/web/src/app/[domain]/search/components/searchResultsPage.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ export const SearchResultsPage = ({
6262
durationMs,
6363
isStreaming,
6464
numMatches,
65+
isExhaustive,
66+
stats,
6567
} = useStreamedSearch({
6668
query: searchQuery,
6769
matches: maxMatchCount,
@@ -170,10 +172,8 @@ export const SearchResultsPage = ({
170172
repoInfo={repoInfo}
171173
searchDurationMs={durationMs}
172174
isStreaming={isStreaming}
173-
// @todo: handle search stats
174-
searchStats={undefined}
175-
// @todo: detect when more results are available
176-
isMoreResultsButtonVisible={false}
175+
searchStats={stats}
176+
isMoreResultsButtonVisible={!isExhaustive}
177177
// @todo: handle branch filtering
178178
isBranchFilteringEnabled={false}
179179
/>

packages/web/src/app/[domain]/search/useStreamedSearch.ts

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { RepositoryInfo, SearchRequest, SearchResponse, SearchResultFile } from '@/features/search/types';
3+
import { RepositoryInfo, SearchRequest, SearchResultFile, SearchStats, StreamedSearchResponse } from '@/features/search/types';
44
import { useState, useCallback, useRef, useEffect } from 'react';
55
import * as Sentry from '@sentry/nextjs';
66

@@ -10,6 +10,7 @@ interface CacheEntry {
1010
numMatches: number;
1111
durationMs: number;
1212
timestamp: number;
13+
isExhaustive: boolean;
1314
}
1415

1516
const searchCache = new Map<string, CacheEntry>();
@@ -34,18 +35,22 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
3435

3536
const [state, setState] = useState<{
3637
isStreaming: boolean,
38+
isExhaustive: boolean,
3739
error: Error | null,
3840
files: SearchResultFile[],
3941
repoInfo: Record<number, RepositoryInfo>,
4042
durationMs: number,
4143
numMatches: number,
44+
stats?: SearchStats,
4245
}>({
4346
isStreaming: false,
47+
isExhaustive: false,
4448
error: null,
4549
files: [],
4650
repoInfo: {},
4751
durationMs: 0,
4852
numMatches: 0,
53+
stats: undefined,
4954
});
5055

5156
const abortControllerRef = useRef<AbortController | null>(null);
@@ -85,6 +90,7 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
8590
console.debug('Using cached search results');
8691
setState({
8792
isStreaming: false,
93+
isExhaustive: cachedEntry.isExhaustive,
8894
error: null,
8995
files: cachedEntry.files,
9096
repoInfo: cachedEntry.repoInfo,
@@ -96,6 +102,7 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
96102

97103
setState({
98104
isStreaming: true,
105+
isExhaustive: false,
99106
error: null,
100107
files: [],
101108
repoInfo: {},
@@ -167,22 +174,33 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
167174
break;
168175
}
169176

170-
const chunk: SearchResponse = JSON.parse(data);
171-
setState(prev => ({
172-
...prev,
173-
files: [
174-
...prev.files,
175-
...chunk.files
176-
],
177-
repoInfo: {
178-
...prev.repoInfo,
179-
...chunk.repositoryInfo.reduce((acc, repo) => {
180-
acc[repo.id] = repo;
181-
return acc;
182-
}, {} as Record<number, RepositoryInfo>),
183-
},
184-
numMatches: prev.numMatches + chunk.stats.actualMatchCount,
185-
}));
177+
const response: StreamedSearchResponse = JSON.parse(data);
178+
switch (response.type) {
179+
case 'chunk':
180+
setState(prev => ({
181+
...prev,
182+
files: [
183+
...prev.files,
184+
...response.files
185+
],
186+
repoInfo: {
187+
...prev.repoInfo,
188+
...response.repositoryInfo.reduce((acc, repo) => {
189+
acc[repo.id] = repo;
190+
return acc;
191+
}, {} as Record<number, RepositoryInfo>),
192+
},
193+
numMatches: prev.numMatches + response.stats.actualMatchCount,
194+
}));
195+
break;
196+
case 'final':
197+
setState(prev => ({
198+
...prev,
199+
isExhaustive: response.isSearchExhaustive,
200+
stats: response.accumulatedStats,
201+
}));
202+
break;
203+
}
186204
}
187205
}
188206

@@ -192,6 +210,7 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
192210
searchCache.set(cacheKey, {
193211
files: prev.files,
194212
repoInfo: prev.repoInfo,
213+
isExhaustive: prev.isExhaustive,
195214
numMatches: prev.numMatches,
196215
durationMs,
197216
timestamp: Date.now(),

packages/web/src/app/api/(server)/search/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { search } from "@/features/search/searchApi";
44
import { isServiceError } from "@/lib/utils";
55
import { NextRequest } from "next/server";
66
import { schemaValidationError, serviceErrorResponse } from "@/lib/serviceError";
7-
import { searchRequestSchema } from "@/features/search/schemas";
7+
import { searchRequestSchema } from "@/features/search/types";
88

99
export const POST = async (request: NextRequest) => {
1010
const body = await request.json();

packages/web/src/app/api/(server)/source/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { getFileSource } from "@/features/search/fileSourceApi";
44
import { schemaValidationError, serviceErrorResponse } from "@/lib/serviceError";
55
import { isServiceError } from "@/lib/utils";
66
import { NextRequest } from "next/server";
7-
import { fileSourceRequestSchema } from "@/features/search/schemas";
7+
import { fileSourceRequestSchema } from "@/features/search/types";
88

99
export const POST = async (request: NextRequest) => {
1010
const body = await request.json();

0 commit comments

Comments
 (0)