diff --git a/src/__tests__/__snapshots__/tool.searchPatternFlyDocs.test.ts.snap b/src/__tests__/__snapshots__/tool.searchPatternFlyDocs.test.ts.snap index 02a4620..e64429e 100644 --- a/src/__tests__/__snapshots__/tool.searchPatternFlyDocs.test.ts.snap +++ b/src/__tests__/__snapshots__/tool.searchPatternFlyDocs.test.ts.snap @@ -20,7 +20,7 @@ exports[`searchPatternFlyDocsTool, callback should parse parameters, with lower exports[`searchPatternFlyDocsTool, callback should parse parameters, with made up componentName: search 1`] = `"No PatternFly documentation found matching "lorem ipsum dolor sit amet""`; -exports[`searchPatternFlyDocsTool, callback should parse parameters, with multiple words: search 1`] = `"No PatternFly documentation found matching "Button Card Table""`; +exports[`searchPatternFlyDocsTool, callback should parse parameters, with multiple words: search 1`] = `"# Search results for "Button Card Table", 3 matches found:"`; exports[`searchPatternFlyDocsTool, callback should parse parameters, with partial componentName: search 1`] = `"# Search results for "ton", 2 matches found:"`; diff --git a/src/server.search.ts b/src/server.search.ts index 154625f..32e80a3 100644 --- a/src/server.search.ts +++ b/src/server.search.ts @@ -22,7 +22,7 @@ interface ClosestSearchOptions { interface FuzzySearchResult { item: string; distance: number; - matchType: 'exact' | 'prefix' | 'suffix' | 'contains' | 'fuzzy' | 'all'; + matchType: 'exact' | 'prefix' | 'suffix' | 'contains' | 'partial' | 'fuzzy' | 'all'; } /** @@ -33,6 +33,7 @@ interface FuzzySearchResult { * - prefix = 1 * - suffix = 1 * - contains = 2 + * - partial = 2 * - fuzzy = Levenshtein edit distance * - `maxResults` - Maximum number of results to return * - `normalizeFn` - Function to normalize strings (default: `normalizeString`) @@ -47,6 +48,7 @@ interface FuzzySearchOptions { isPrefixMatch?: boolean; isSuffixMatch?: boolean; isContainsMatch?: boolean; + isPartialMatch?: boolean; isFuzzyMatch?: boolean; deduplicateByNormalized?: boolean; } @@ -59,6 +61,7 @@ interface FuzzySearchOptions { * - Function has a `memo` property to allow use as a memoized function. * * @param str + * @returns Normalized or empty string */ const normalizeString: NormalizeString = (str: string) => String(str || '') .trim() @@ -126,11 +129,12 @@ const findClosest = ( * @param {FuzzySearchOptions} options - Search configuration options * @param {number} options.maxDistance - Maximum edit distance for a match. Distance is defined as * @param {number} options.maxResults - Maximum number of results to return - * @param {NormalizeString} options.normalizeFn - Function to normalize strings (default: `normalizeString`) + * @param {NormalizeString} options.normalizeFn - Function to normalize strings. Should always return a string or empty string (default: `normalizeString`) * @param {boolean} options.isExactMatch - Include exact matches in results (default: `true`) * @param {boolean} options.isPrefixMatch - Include prefix matches in results (default: `true`) * @param {boolean} options.isSuffixMatch - Include suffix matches in results (default: `true`) * @param {boolean} options.isContainsMatch - Include contains matches in results (default: `true`) + * @param {boolean} options.isPartialMatch - Include partial matches in results (default: `true`) * @param {boolean} options.isFuzzyMatch - Allow fuzzy matches even when `maxDistance` is negative or zero. * @param {boolean} options.deduplicateByNormalized - If `true`, deduplicate results by normalized value instead of original string. * @returns {FuzzySearchResult[]} Array of matching strings with distance and match type @@ -155,6 +159,7 @@ const fuzzySearch = ( isPrefixMatch = true, isSuffixMatch = true, isContainsMatch = true, + isPartialMatch = true, isFuzzyMatch = false, deduplicateByNormalized = false }: FuzzySearchOptions = {} @@ -186,6 +191,9 @@ const fuzzySearch = ( } else if (normalizedQuery !== '' && normalizedItem.includes(normalizedQuery)) { matchType = 'contains'; editDistance = 2; + } else if (normalizedQuery !== '' && normalizedItem !== '' && normalizedQuery.includes(normalizedItem)) { + matchType = 'partial'; + editDistance = 2; } else if (isFuzzyMatch && Math.abs(normalizedItem.length - normalizedQuery.length) <= maxDistance) { matchType = 'fuzzy'; editDistance = distance(normalizedQuery, normalizedItem); @@ -199,6 +207,7 @@ const fuzzySearch = ( (matchType === 'prefix' && isPrefixMatch) || (matchType === 'suffix' && isSuffixMatch) || (matchType === 'contains' && isContainsMatch) || + (matchType === 'partial' && isPartialMatch) || (matchType === 'fuzzy' && isFuzzyMatch); if (editDistance <= maxDistance && isIncluded) {