perf(website): cache normalized search fields (#4245)

Precompute lowercased search fields once per index identity so each query avoids repeating normalization across every indexed item.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Co-authored-by: Waseem <wmohammed@pros.com>
This commit is contained in:
Waseem Mohammed
2026-10-01 11:20:14 +10:00
committed by GitHub
co-authored by Waseem
parent 0c54bcee82
commit cc8c1ac4d8
+26 -7
View File
@@ -29,6 +29,12 @@ const MAX_PER_GROUP = 4;
const MAX_PAGEFIND_HITS = 12; const MAX_PAGEFIND_HITS = 12;
type ResultGroup = { category: SearchCategory; items: SearchItem[] }; type ResultGroup = { category: SearchCategory; items: SearchItem[] };
type NormalizedSearchItem = {
item: SearchItem;
title: string;
description: string;
category: string;
};
/** /**
* Shared top-navigation search. A collapsed magnifier button expands into a * Shared top-navigation search. A collapsed magnifier button expands into a
@@ -120,15 +126,28 @@ export function TopNavSearch({
}; };
}, [trimmed]); }, [trimmed]);
const normalizedIndex = useMemo<NormalizedSearchItem[]>(
() =>
index.map((item) => ({
item,
title: item.title.toLowerCase(),
description: item.description.toLowerCase(),
category: item.category.toLowerCase(),
})),
[index],
);
const groups = useMemo<ResultGroup[]>(() => { const groups = useMemo<ResultGroup[]>(() => {
const query = trimmed.toLowerCase(); const query = trimmed.toLowerCase();
if (query.length === 0) return []; if (query.length === 0) return [];
const staticMatches = index.filter( const staticMatches = normalizedIndex
(item) => .filter(
item.title.toLowerCase().includes(query) || ({ title, description, category }) =>
item.description.toLowerCase().includes(query) || title.includes(query) ||
item.category.toLowerCase().includes(query), description.includes(query) ||
); category.includes(query),
)
.map(({ item }) => item);
// Static hits win on ties: they carry curated titles and descriptions. // Static hits win on ties: they carry curated titles and descriptions.
const seen = new Set(staticMatches.map((item) => hrefKey(item.href))); const seen = new Set(staticMatches.map((item) => hrefKey(item.href)));
const matches = [...staticMatches]; const matches = [...staticMatches];
@@ -150,7 +169,7 @@ export function TopNavSearch({
grouped.push({ category, items }); grouped.push({ category, items });
} }
return grouped; return grouped;
}, [trimmed, index, pagefindHits]); }, [trimmed, normalizedIndex, pagefindHits]);
const flatResults = useMemo( const flatResults = useMemo(
() => groups.flatMap((group) => group.items), () => groups.flatMap((group) => group.items),