Skip to content

Commit 2f15f6f

Browse files
committed
feat(bun-seearch): include bun workspace fixes and search view docs rendering
1 parent 4b1d8fc commit 2f15f6f

File tree

5 files changed

+97
-67
lines changed

5 files changed

+97
-67
lines changed

apps/website/src/components/DocPage/Search/SearchView.tsx

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,43 +35,64 @@ const fuseOptions: IFuseOptions<DocMetadata> = {
3535
const FUSE_WEIGHT = 0.3;
3636
const BACKEND_WEIGHT = 0.7;
3737

38-
type BackendDocResult = { fileKey: string; similarityScore: number };
38+
type BackendDocumentResult = {
39+
fileKey: string;
40+
similarityScore: number;
41+
};
3942

40-
function mergeHybridResults(
43+
/**
44+
* Merges Fuse.js (local fuzzy search) and backend (semantic search) results
45+
* into a single ranked list.
46+
*/
47+
const mergeHybridResults = (
4148
fuseResults: Fuse.FuseResult<DocMetadata>[],
42-
backendResults: BackendDocResult[],
43-
allDocs: DocMetadata[]
44-
): DocMetadata[] {
49+
backendResults: BackendDocumentResult[],
50+
allDocuments: DocMetadata[]
51+
): DocMetadata[] => {
4552
const normalizeFuse = (score?: number) => 1 - Math.min((score ?? 1) / 0.5, 1);
4653
const normalizeBackend = (score: number) => Math.min(score, 1);
4754

4855
const backendMap = new Map(
49-
backendResults.map((r) => [r.fileKey, normalizeBackend(r.similarityScore)])
56+
backendResults.map((result) => [
57+
result.fileKey,
58+
normalizeBackend(result.similarityScore),
59+
])
5060
);
51-
const combined = new Map<string, { doc: DocMetadata; score: number }>();
5261

62+
const combined = new Map<string, { document: DocMetadata; score: number }>();
63+
64+
// Combine both Fuse and backend scores where available
5365
for (const fuseItem of fuseResults) {
54-
const doc = fuseItem.item;
66+
const document = fuseItem.item;
5567
const fuseScore = normalizeFuse(fuseItem.score);
56-
const backendScore = backendMap.get(doc.docKey);
68+
const backendScore = backendMap.get(document.docKey);
5769
const combinedScore = backendScore
5870
? BACKEND_WEIGHT * backendScore + FUSE_WEIGHT * fuseScore
5971
: fuseScore;
60-
combined.set(doc.docKey, { doc, score: combinedScore });
72+
combined.set(document.docKey, { document, score: combinedScore });
6173
}
6274

75+
// Include backend-only results not found by Fuse
6376
for (const [fileKey, backendScore] of backendMap) {
6477
if (!combined.has(fileKey)) {
65-
const doc = allDocs.find((d) => d.docKey === fileKey);
66-
if (doc)
67-
combined.set(fileKey, { doc, score: BACKEND_WEIGHT * backendScore });
78+
const document = allDocuments.find(
79+
(doc) =>
80+
doc.docKey === fileKey ||
81+
doc.url.endsWith(fileKey) ||
82+
doc.url.includes(fileKey)
83+
);
84+
if (document)
85+
combined.set(fileKey, {
86+
document,
87+
score: BACKEND_WEIGHT * backendScore,
88+
});
6889
}
6990
}
7091

7192
return Array.from(combined.values())
7293
.sort((a, b) => b.score - a.score)
73-
.map((v) => v.doc);
74-
}
94+
.map((value) => value.document);
95+
};
7596

7697
const SearchResultItem: FC<{ doc: DocMetadata; onClickLink: () => void }> = ({
7798
doc,
@@ -105,16 +126,17 @@ const SearchResultItem: FC<{ doc: DocMetadata; onClickLink: () => void }> = ({
105126
);
106127
};
107128

129+
/**
130+
* SearchView — combines Fuse.js fuzzy search (client-side)
131+
* with backend semantic vector results for robust doc discovery.
132+
*/
108133
export const SearchView: FC<{
109134
onClickLink?: () => void;
110135
isOpen?: boolean;
111136
}> = ({ onClickLink = () => {}, isOpen = false }) => {
112137
const inputRef = useRef<HTMLInputElement>(null);
113138
const searchQueryParam = useSearchParams().get('search');
114139
const [results, setResults] = useState<DocMetadata[]>([]);
115-
const [currentQuery, setCurrentQuery] = useState<string | null>(
116-
searchQueryParam
117-
);
118140

119141
const { search, setSearch } = useSearch({
120142
defaultValue: searchQueryParam,
@@ -127,38 +149,52 @@ export const SearchView: FC<{
127149

128150
const docs = getIntlayer('doc-metadata', locale) as DocMetadata[];
129151
const blogs = getIntlayer('blog-metadata', locale) as BlogMetadata[];
130-
const allDocs = [...docs, ...blogs];
131-
const fuse = new Fuse(allDocs, fuseOptions);
152+
const allDocuments = [...docs, ...blogs];
153+
const fuse = new Fuse(allDocuments, fuseOptions);
132154

133155
useEffect(() => {
134-
if (backendData?.data && currentQuery) {
135-
const backendResults: BackendDocResult[] = backendData.data.map(
136-
(doc) => ({
137-
fileKey: doc.fileKey,
138-
similarityScore: doc.similarityScore ?? 0.5,
139-
})
156+
if (backendData?.data && search) {
157+
// Normalize backend response (string paths → structured results)
158+
const backendResults: BackendDocumentResult[] = backendData.data.map(
159+
(item: any) => {
160+
if (typeof item === 'string') {
161+
return {
162+
fileKey: item.replace(/^\.\//, ''),
163+
similarityScore: 0.5,
164+
};
165+
}
166+
return {
167+
fileKey: item.fileKey,
168+
similarityScore: item.similarityScore ?? 0.5,
169+
};
170+
}
140171
);
141172

142-
const fuseResults = fuse.search(currentQuery);
173+
const fuseResults = fuse.search(search);
143174
let mergedResults: DocMetadata[];
144175

145176
if (fuseResults.length > 0) {
146-
// Hybrid mode: combine Fuse.js and backend semantic results
147177
mergedResults = mergeHybridResults(
148178
fuseResults,
149179
backendResults,
150-
filesData
180+
allDocuments
151181
);
152182
} else {
153-
// Fallback: show backend-only results when Fuse finds none
154183
mergedResults = backendResults
155-
.map((r) => filesData.find((d) => d.docKey === r.fileKey))
156-
.filter((d): d is DocMetadata => Boolean(d));
184+
.map((result) =>
185+
allDocuments.find(
186+
(doc) =>
187+
doc.docKey === result.fileKey ||
188+
doc.url.endsWith(result.fileKey) ||
189+
doc.url.includes(result.fileKey)
190+
)
191+
)
192+
.filter((doc): doc is DocMetadata => Boolean(doc));
157193
}
158194

159195
setResults(mergedResults);
160196
}
161-
}, [backendData, currentQuery, allDocs, fuse]);
197+
}, [backendData, search, allDocuments, fuse]);
162198

163199
const isNoResult =
164200
!isFetching &&
@@ -187,9 +223,9 @@ export const SearchView: FC<{
187223
)}
188224
{results.length > 0 && (
189225
<ul className="flex flex-col gap-10">
190-
{results.map((r) => (
191-
<li key={r.url}>
192-
<SearchResultItem doc={r} onClickLink={onClickLink} />
226+
{results.map((result) => (
227+
<li key={result.url}>
228+
<SearchResultItem doc={result} onClickLink={onClickLink} />
193229
</li>
194230
))}
195231
</ul>

0 commit comments

Comments
 (0)