11import {
22 fuzzySearch ,
33 type FuzzySearch ,
4+ type FuzzySearchOptions ,
45 type FuzzySearchResult
56} from './server.search' ;
67import { memo } from './server.caching' ;
@@ -11,7 +12,6 @@ import {
1112 type PatternFlyMcpDocsMeta ,
1213 type PatternFlyMcpResourceMetadata
1314} from './patternFly.getResources' ;
14- import { parsePatternFlyUri } from './patternFly.helpers' ;
1515import { type PatternFlyMcpDocsCatalogDoc } from './docs.embedded' ;
1616
1717/**
@@ -31,13 +31,15 @@ type PatternFlyMcpResourceFilteredMetadata = Omit<PatternFlyMcpResourceMetadata,
3131 * @property [version] - PatternFly version to filter search results. Defaults to undefined for all versions.
3232 * @property [category] - Category to filter search results. Defaults to undefined for all categories.
3333 * @property [section] - Section to filter search results. Defaults to undefined for all sections.
34- * @property [name] - Name to filter search results. Defaults to undefined for all names.
34+ * @property [name] - Name, or hash id, to filter search results. Defaults to undefined for all names and IDs.
35+ * @property [path] - Document path, or URI, to filter search results. Defaults to undefined for all paths and URIs.
3536 */
3637interface FilterPatternFlyFilters {
3738 version ?: string | undefined ;
3839 category ?: string | undefined ;
3940 section ?: string | undefined ;
4041 name ?: string | undefined ;
42+ path ?: string | undefined ;
4143}
4244
4345/**
@@ -110,6 +112,17 @@ interface SearchPatternFlyOptions {
110112 maxResults ?: number ;
111113}
112114
115+ /**
116+ * A list of prioritized filters used for matching and processing
117+ * filter patterns. Each filter corresponds to a specific key in the
118+ * FilterPatternFlyFilters type.
119+ *
120+ * @note **Sequence matters** - This array defines the order of priority for
121+ * filter keys, which may be used in operations such as sorting or
122+ * applying specific filtering logic. See {@link FilterPatternFlyFilters}
123+ */
124+ const PRIORITY_FILTERS : ( keyof FilterPatternFlyFilters ) [ ] = [ 'name' , 'section' , 'category' , 'version' , 'path' ] ;
125+
113126/**
114127 * Apply sequenced priority filters for predictable filtering, filter PatternFly data.
115128 *
@@ -169,13 +182,16 @@ const filterPatternFly = async (
169182 const matchesVersion = ! updatedFilters . version || String ( entry . version ) . toLowerCase ( ) === updatedFilters . version ;
170183 const matchesCategory = ! updatedFilters . category || filterMatch ( entry . category , updatedFilters . category ) ;
171184 const matchesSection = ! updatedFilters . section || filterMatch ( entry . section , updatedFilters . section ) ;
185+ const matchesPath = ! updatedFilters . path || filterMatch ( entry . path , updatedFilters . path ) ||
186+ filterMatch ( entry . uriId , updatedFilters . path ) || filterMatch ( entry . uriSchemas , updatedFilters . path ) ||
187+ filterMatch ( entry . uriSchemasId , updatedFilters . path ) || filterMatch ( entry . uri , updatedFilters . path ) ;
172188
173189 // Filter order matters specific id -> group id -> group name
174190 const matchesName = ! updatedFilters . name || filterMatch ( entry . id , updatedFilters . name ) ||
175191 filterMatch ( entry . groupId , updatedFilters . name ) || filterMatch ( entry . name , updatedFilters . name ) ;
176192
177193 // Any missing filter registers as true. Only filters that are active run their check.
178- return matchesVersion && matchesCategory && matchesSection && matchesName ;
194+ return matchesVersion && matchesCategory && matchesSection && matchesPath && matchesName ;
179195 } ) ;
180196
181197 if ( matchedEntries . length > 0 ) {
@@ -221,7 +237,7 @@ filterPatternFly.memo = memo(filterPatternFly, DEFAULT_OPTIONS.resourceMemoOptio
221237 * @param filters
222238 * @param mcpResources
223239 * @param [options] - Optional settings object
224- * @param [options.prioritizedFilters] - Array of filters to prioritize. Defaults to `['name', 'section', 'category', 'version']` .
240+ * @param [options.prioritizedFilters] - Array of filters to prioritize. Defaults to { @link PRIORITY_FILTERS} .
225241 * @param [options.maxResultsLimit] - Max number of results internal conditions need to match before they return the original result. Defaults to `1`.
226242 * @param [options.useExistingFilters] - Use the existing filters or bypass them. Defaults to `true`.
227243 * @returns {Promise<FilterPatternFlyResults> } - A Promise resolving to the filtering results.
@@ -230,7 +246,7 @@ const dynamicFilterPatternFly = async (
230246 searchQuery : string , filters : FilterPatternFlyFilters | undefined ,
231247 mcpResources ?: Promise < PatternFlyMcpAvailableResources > | Map < string , PatternFlyMcpResourceFilteredMetadata > ,
232248 {
233- prioritizedFilters = [ 'name' , 'section' , 'category' , 'version' ] ,
249+ prioritizedFilters = PRIORITY_FILTERS ,
234250 maxResultsLimit = 1 ,
235251 useExistingFilters = true
236252 } : { prioritizedFilters ?: ( keyof FilterPatternFlyFilters ) [ ] ; maxResultsLimit ?: number ; useExistingFilters ?: boolean } = { }
@@ -311,26 +327,31 @@ const searchPatternFly = async (searchQuery: unknown, filters?: FilterPatternFly
311327 const updatedFilters = filters || { } ;
312328 const isWildCardAll = coercedSearchQuery === '*' || coercedSearchQuery . toLowerCase ( ) === 'all' || coercedSearchQuery === '' ;
313329 const isSearchWildCardAll = allowWildCardAll && isWildCardAll ;
330+ const pathMatchName = updatedResources . pathIndex ?. get ( coercedSearchQuery ) ;
331+ const uriMatchName = updatedResources . uriIndex ?. get ( coercedSearchQuery ) ;
332+ const hashMatchName = updatedResources . hashIndex ?. get ( coercedSearchQuery ) ;
314333 let search : FuzzySearch | undefined ;
315334 let searchResults : FuzzySearchResult [ ] = [ ] ;
316335
317336 // Perform wildcard all search or fuzzy search
318337 if ( isSearchWildCardAll ) {
319338 searchResults = updatedResources . keywordsIndex . map ( name => ( { matchType : 'all' , distance : 0 , item : name } as FuzzySearchResult ) ) ;
339+ } else if ( pathMatchName || uriMatchName || hashMatchName ) {
340+ searchResults = [
341+ {
342+ matchType : 'exact' ,
343+ distance : 0 ,
344+ item : pathMatchName || uriMatchName || hashMatchName
345+ } as FuzzySearchResult
346+ ] ;
320347 } else {
321- const patternflyUri = parsePatternFlyUri . memo ( coercedSearchQuery ) ;
322- const fuzzySearchSettings = {
348+ const fuzzySearchSettings : FuzzySearchOptions = {
323349 maxDistance,
324350 maxResults,
325351 isFuzzyMatch : true ,
326352 deduplicateByNormalized : true
327353 } ;
328354
329- if ( patternflyUri ) {
330- fuzzySearchSettings . maxDistance = 1 ;
331- fuzzySearchSettings . isFuzzyMatch = false ;
332- }
333-
334355 // Pass the original searchQuery, fuzzySearch has its own normalization.
335356 search = fuzzySearch ( searchQuery , updatedResources . keywordsIndex , fuzzySearchSettings ) ;
336357 searchResults = search . results ;
0 commit comments