@@ -32,7 +32,7 @@ import { formatCourse } from "../course/formatter";
3232import { formatEnrollment } from "../enrollment/formatter" ;
3333import type { EnrollmentModule } from "../enrollment/generated-types/module-types" ;
3434import type { GradeDistributionModule } from "../grade-distribution/generated-types/module-types" ;
35- import { getSearchIndex } from "./catalog-cache" ;
35+ import { getCachedCatalog , getSearchIndex } from "./catalog-cache" ;
3636
3737export interface CatalogQueryParams {
3838 year : number ;
@@ -160,40 +160,39 @@ const getCatalogWithSemanticSearch = async ({
160160 limit : number ;
161161 skip : number ;
162162} ) => {
163- // Throws if the semantic service is unavailable — surfaces as GraphQL error
164163 const response = await searchSemantic ( searchTerm , year , semester ) ;
165164
166165 if ( response . results . length === 0 ) {
167166 return { results : [ ] , totalCount : 0 } ;
168167 }
169168
170- // Throws if the semantic service is unavailable — surfaces as GraphQL error
171169 // Python already returns results sorted by score descending — preserve that order.
172170 // Build a rank map: "subject-courseNumber" → position in Python's ranked list
173171 const rankMap = new Map < string , number > ( ) ;
174172 response . results . forEach ( ( { subject, courseNumber } , index ) => {
175173 rankMap . set ( `${ subject } -${ courseNumber } ` , index ) ;
176174 } ) ;
177175
178- const query = buildFilterQuery ( year , semester , filters ) ;
179- query . $or = response . results . map ( ( { subject, courseNumber } ) => ( {
180- subject,
181- courseNumber,
182- } ) ) as unknown as CatalogFilterCondition [ ] ;
176+ // Reuse the server-side catalog cache (same one fuzzy search uses) to avoid
177+ // a redundant MongoDB round-trip.
178+ const allCatalog = await getCachedCatalog ( year , semester ) ;
179+
180+ const semanticMatches = allCatalog . filter ( ( item ) =>
181+ rankMap . has ( `${ item . subject } -${ item . courseNumber } ` )
182+ ) ;
183183
184- // Fetch all matching docs — semantic result sets are small (bounded by threshold)
185- const allResults = await CatalogClassModel . find ( query ) . lean ( ) ;
184+ const filtered = applyInMemoryFilters ( semanticMatches , filters ) ;
186185
187186 // Sort by Python's relevance rank, then by section number within the same course
188- allResults . sort ( ( a , b ) => {
187+ filtered . sort ( ( a , b ) => {
189188 const rankA = rankMap . get ( `${ a . subject } -${ a . courseNumber } ` ) ?? 999 ;
190189 const rankB = rankMap . get ( `${ b . subject } -${ b . courseNumber } ` ) ?? 999 ;
191190 if ( rankA !== rankB ) return rankA - rankB ;
192191 return a . number . localeCompare ( b . number ) ;
193192 } ) ;
194193
195- const totalCount = allResults . length ;
196- const results = allResults . slice ( skip , skip + limit ) ;
194+ const totalCount = filtered . length ;
195+ const results = filtered . slice ( skip , skip + limit ) ;
197196
198197 return { results, totalCount } ;
199198} ;
0 commit comments