@aiquants/fuzzy-search
Version:
Advanced fuzzy search library with Levenshtein distance, n-gram indexing, and Web Worker support
1 lines • 152 kB
Source Map (JSON)
{"version":3,"sources":["../src/react/index.ts","../src/types/index.ts","../src/core/FuzzySearchManager.ts"],"sourcesContent":["/**\n * React hooks and utilities for fuzzy search functionality.\n * あいまい検索機能用のReactフックとユーティリティ。\n *\n * Note: This module requires React as a peer dependency.\n * 注意:このモジュールはReactをピア依存関係として必要とします。\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\"\nimport { FuzzySearchManager } from \"../core/FuzzySearchManager\"\nimport { DEFAULT_FUZZY_SEARCH_OPTIONS, type FuzzySearchOptions, type FuzzySearchResult, type PerformanceMetrics, type SearchHistory, type SearchHistoryResult, type SearchResultItem, type UseFuzzySearchOptions } from \"../types/index\"\n\nconst LOG_PREFIX = \"[fuzzy-search]\"\n\nconst extractErrorMessage = (input: unknown, fallbackMessage: string): string => {\n if (typeof input === \"string\" && input.trim()) {\n return input\n }\n\n if (input instanceof Error && input.message.trim()) {\n return input.message\n }\n\n if (typeof input === \"object\" && input !== null) {\n if (\"error\" in input) {\n return extractErrorMessage((input as { error?: unknown }).error, fallbackMessage)\n }\n if (\"message\" in input) {\n return extractErrorMessage((input as { message?: unknown }).message, fallbackMessage)\n }\n }\n\n return fallbackMessage\n}\n\n/**\n * A simplified fuzzy search hook with stable state management.\n * This hook provides an easy-to-use interface for performing fuzzy searches on a list of items.\n * 安定した状態管理を持つ簡易あいまい検索フック。\n * このフックは、アイテムのリストに対してあいまい検索を実行するための使いやすいインターフェースを提供します。\n * @param items The array of items to search through.\n * @param searchFields The fields in the items to search against.\n * @param initialOptions Optional initial configuration for the fuzzy search.\n * @returns An object containing the search state and functions.\n */\nexport function useFuzzySearch<T>(items: T[], searchFields: Array<keyof T | string>, initialOptions?: UseFuzzySearchOptions): FuzzySearchResult<T> {\n // 基本状態の管理\n const [searchTerm, setSearchTerm] = useState(\"\")\n const [filteredItems, setFilteredItems] = useState<SearchResultItem<T>[]>([])\n const [isSearching, setIsSearching] = useState(false)\n const [isIndexBuilding, setIsIndexBuilding] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [searchHistory, setSearchHistory] = useState<SearchHistory[]>([])\n const [performanceMetrics, setPerformanceMetrics] = useState<PerformanceMetrics>({\n totalSearches: 0,\n averageSearchTime: 0,\n itemCount: items.length,\n resultsCount: 0,\n workerStats: undefined,\n })\n\n // 安定したオプションオブジェクトを作成(initialOptionsが変更された場合のみ再作成)\n const options = useMemo<FuzzySearchOptions>(() => {\n return {\n ...DEFAULT_FUZZY_SEARCH_OPTIONS,\n ...initialOptions,\n }\n }, [initialOptions])\n\n // 検索マネージャーのインスタンスをuseRefで保持\n const searchManager = useRef<FuzzySearchManager<T> | null>(null)\n\n // 検索語の最新値を保持するref(インデックス構築時の自動検索用)\n const searchTermRef = useRef(searchTerm)\n useEffect(() => {\n searchTermRef.current = searchTerm\n }, [searchTerm])\n\n // 検索フィールドを文字列配列に変換(安定化のため)\n const stringFields = useMemo(() => searchFields.map((field) => String(field)), [searchFields])\n\n // 検索マネージャーの初期化(初回レンダリング時のみ)\n useEffect(() => {\n if (!searchManager.current) {\n searchManager.current = new FuzzySearchManager(options)\n console.log(LOG_PREFIX, \"🔧 FuzzySearchManager created (initial setup)\")\n }\n\n // ワーカーの統計更新と検索完了イベントをリッスンして内部状態を更新\n const handleStatsUpdate = () => {\n if (searchManager.current) {\n // getStats() の代わりに getCurrentStats() を使用して無限ループを防ぐ\n const currentStats = searchManager.current.getCurrentStats()\n const metrics: PerformanceMetrics = {\n totalSearches: currentStats?.totalSearches ?? 0,\n averageSearchTime: currentStats?.averageSearchTime ?? 0,\n itemCount: items.length,\n resultsCount: filteredItems.length,\n workerStats: {\n index: currentStats?.workers.index.stats,\n levenshtein: currentStats?.workers.levenshtein.stats,\n },\n }\n setPerformanceMetrics(metrics)\n }\n }\n\n const handleIndexWorkerError = (payload?: unknown) => {\n const message = extractErrorMessage(payload, \"Index Worker error\")\n console.error(`${LOG_PREFIX} ❌ Index worker error event`, payload)\n setError(message)\n setIsIndexBuilding(false)\n }\n\n const handleIndexRebuildError = (payload?: unknown) => {\n const message = extractErrorMessage(payload, \"Index rebuild error\")\n console.error(`${LOG_PREFIX} ❌ Index rebuild error event`, payload)\n setError(message)\n setIsIndexBuilding(false)\n }\n\n const handleSearchError = (payload?: unknown) => {\n const message = extractErrorMessage(payload, \"Search execution failed\")\n console.error(`${LOG_PREFIX} ❌ Search error event`, payload)\n setError(message)\n }\n\n // ワーカーの統計更新と検索完了イベントをリッスン\n searchManager.current.on(\"worker:statsUpdated\", handleStatsUpdate)\n searchManager.current.on(\"search:complete\", handleStatsUpdate)\n searchManager.current.on(\"indexWorker:error\", handleIndexWorkerError)\n searchManager.current.on(\"index:rebuildError\", handleIndexRebuildError)\n searchManager.current.on(\"search:error\", handleSearchError)\n\n // クリーンアップ関数でイベントリスナーを削除\n return () => {\n if (searchManager.current) {\n searchManager.current.off(\"worker:statsUpdated\", handleStatsUpdate)\n searchManager.current.off(\"search:complete\", handleStatsUpdate)\n searchManager.current.off(\"indexWorker:error\", handleIndexWorkerError)\n searchManager.current.off(\"index:rebuildError\", handleIndexRebuildError)\n searchManager.current.off(\"search:error\", handleSearchError)\n }\n }\n })\n\n const performSearch = useCallback(\n async (query: string) => {\n if (!searchManager.current) return\n\n console.log(`${LOG_PREFIX} 🔍 performSearch called with query: \"${query}\"`)\n\n try {\n console.log(`${LOG_PREFIX} 📈 Setting isSearching to true for query: \"${query}\"`)\n setIsSearching(true)\n setError(null)\n\n // クエリが空の場合は全件表示\n if (!query.trim()) {\n setFilteredItems(\n items.map((item, index) => ({\n item,\n score: 1,\n baseScore: 1,\n matchedFields: [],\n relevanceReason: \"全件表示\",\n originalIndex: index,\n })),\n )\n console.log(`${LOG_PREFIX} 📈 Setting isSearching to false for empty query: \"${query}\"`)\n setIsSearching(false)\n return\n }\n\n console.log(`${LOG_PREFIX} 🚀 Starting search with query: \"${query}\", items count: ${items.length}`)\n const startTime = performance.now()\n const results = await searchManager.current.search(query, items, stringFields, options)\n const endTime = performance.now()\n const searchTime = endTime - startTime\n\n console.log(`${LOG_PREFIX} ✅ Search completed with ${results.length} results in ${searchTime}ms`)\n setFilteredItems(results)\n\n // 検索履歴を追加\n const historyItem: SearchHistory = {\n query,\n timestamp: Date.now(),\n resultCount: results.length,\n processingTime: searchTime,\n searchTime,\n successful: results.length > 0,\n }\n\n setSearchHistory((prev) => {\n const filtered = prev.filter((item) => item.query !== query)\n return [historyItem, ...filtered].slice(0, 50)\n })\n } catch (searchError) {\n console.log(`${LOG_PREFIX} ❌ Search error for query: \"${query}\"`, searchError)\n setError(searchError instanceof Error ? searchError.message : \"Search failed\")\n setFilteredItems([])\n } finally {\n console.log(`${LOG_PREFIX} 📈 Setting isSearching to false (finally) for query: \"${query}\"`)\n setIsSearching(false)\n }\n },\n [items, stringFields, options],\n )\n\n // データや検索オプションが変更された時にインデックスを事前構築\n useEffect(() => {\n if (!searchManager.current || items.length === 0) {\n return\n }\n\n let isCancelled = false\n const shouldAutoSearch = options.autoSearchOnIndexRebuild ?? true\n\n console.log(`${LOG_PREFIX} 🏗️ Data changed, pre-building index for ${items.length} items`)\n setIsIndexBuilding(true)\n\n // 非同期でインデックスを事前構築(エラーハンドリング付き)\n searchManager.current\n .prebuildIndex(items, stringFields, options)\n .then(() => {\n if (!isCancelled && shouldAutoSearch) {\n console.log(`${LOG_PREFIX} 🔁 Re-running search after index rebuild`)\n void performSearch(searchTermRef.current)\n }\n })\n .catch((error) => {\n console.warn(LOG_PREFIX, \"🔧 Failed to pre-build index:\", error)\n // エラーが発生してもアプリケーションの動作には影響しないようにする\n setError(extractErrorMessage(error, \"インデックスの事前構築中にエラーが発生しました\"))\n })\n .finally(() => {\n if (!isCancelled) {\n setIsIndexBuilding(false)\n }\n })\n\n return () => {\n isCancelled = true\n }\n }, [items, stringFields, options, performSearch])\n\n // コンポーネントのアンマウント時にリソースをクリーンアップ\n useEffect(() => {\n return () => {\n if (searchManager.current) {\n console.log(LOG_PREFIX, \"🔧 FuzzySearchManager disposed (component unmount)\")\n searchManager.current.dispose()\n searchManager.current = null\n }\n }\n }, [])\n\n // デバウンス検索を実行\n useEffect(() => {\n console.log(`${LOG_PREFIX} 🎯 useEffect triggered - searchTerm: \"${searchTerm}\", debounceMs: ${options.debounceMs}`)\n\n const timeoutId = setTimeout(() => {\n console.log(`${LOG_PREFIX} ⏰ Timeout executing performSearch for: \"${searchTerm}\"`)\n performSearch(searchTerm)\n }, options.debounceMs)\n\n // クリーンアップ関数でタイマーをクリア\n return () => {\n console.log(`${LOG_PREFIX} 🚫 Timeout cleared for: \"${searchTerm}\"`)\n clearTimeout(timeoutId)\n }\n }, [searchTerm, options, performSearch])\n\n // 初期表示(検索語が空の場合)\n useEffect(() => {\n const trimmedSearchTerm = searchTerm.trim()\n if (!trimmedSearchTerm) {\n setFilteredItems(\n items.map((item, index) => ({\n item,\n score: 1,\n baseScore: 1,\n matchedFields: [],\n relevanceReason: \"全件表示\",\n originalIndex: index,\n })),\n )\n }\n }, [items, searchTerm])\n\n // 選択されたアイテムを処理するユーティリティ関数\n const selectItem = useCallback((item: T, index: number) => {\n console.log(LOG_PREFIX, \"🎯 Selected item:\", item, \"at index:\", index)\n }, [])\n\n // 検索をクリアするユーティリティ関数\n const clearSearch = useCallback(() => {\n setSearchTerm(\"\")\n setError(null)\n }, [])\n\n // 検索履歴をクリアするユーティリティ関数\n const clearHistory = useCallback(() => {\n setSearchHistory([])\n }, [])\n\n // 統計情報をリセットするユーティリティ関数\n const resetStats = useCallback(async () => {\n if (searchManager.current) {\n await searchManager.current.resetStats()\n setSearchHistory([]) // 検索履歴も合わせてクリア\n setError(null) // エラー状態もクリア\n\n // 統計情報を再取得して UI を更新\n const currentStats = searchManager.current.getCurrentStats()\n const resetMetrics: PerformanceMetrics = {\n totalSearches: currentStats?.totalSearches ?? 0,\n averageSearchTime: currentStats?.averageSearchTime ?? 0,\n itemCount: items.length,\n resultsCount: filteredItems.length,\n workerStats: {\n index: currentStats?.workers.index.stats,\n levenshtein: currentStats?.workers.levenshtein.stats,\n },\n }\n setPerformanceMetrics(resetMetrics)\n\n console.log(`${LOG_PREFIX} 📊 useFuzzySearch: Statistics have been reset and refreshed`)\n }\n }, [items.length, filteredItems.length])\n\n /**\n * Gets search suggestions based on the current query and search history.\n * @param query The current search query.\n * @param limit The maximum number of suggestions to return.\n * @returns An array of suggestion strings.\n * 現在のクエリと検索履歴に基づいて検索候補を取得します。\n * @param query 現在の検索クエリ。\n * @param limit 返す候補の最大数。\n * @returns 候補文字列の配列。\n */\n const getSearchSuggestions = useCallback(\n (query: string, limit: number = 5) => {\n const suggestions = searchHistory\n .filter((history) => history.query.toLowerCase().includes(query.toLowerCase()) && history.query !== query)\n .sort((a, b) => b.timestamp - a.timestamp)\n .slice(0, limit)\n .map((history) => history.query)\n\n return Array.from(new Set(suggestions))\n },\n [searchHistory],\n )\n\n /**\n * Forces a complete rebuild of the search index for the current items and fields.\n * This will clear all existing indices and rebuild them from scratch.\n * 現在のアイテムとフィールドに対して検索インデックスの完全な再構築を強制実行します。\n * これにより既存のインデックスがすべてクリアされてゼロから再構築されます。\n *\n * @param customOptions Optional custom options for the index rebuild.\n * @returns A promise that resolves when the index rebuild is complete.\n * @param customOptions インデックス再構築用のオプション設定(オプション)。\n * @returns インデックス再構築完了時に解決するPromise。\n */\n const rebuildIndex = useCallback(\n async (customOptions?: UseFuzzySearchOptions): Promise<void> => {\n if (!searchManager.current) {\n console.warn(`${LOG_PREFIX} ⚠️ Search manager is not initialized`)\n return\n }\n\n console.log(`${LOG_PREFIX} 🔄 Rebuilding index for ${items.length} items`)\n setIsIndexBuilding(true)\n setError(null)\n\n try {\n // カスタムオプションがある場合はマージ\n const rebuildOptions = customOptions ? { ...options, ...customOptions } : options\n\n // インデックス再構築を実行\n await searchManager.current.rebuildIndex(items, stringFields, rebuildOptions)\n\n console.log(`${LOG_PREFIX} ✅ Index rebuild completed successfully`)\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"インデックス再構築中にエラーが発生しました\"\n console.error(LOG_PREFIX, \"❌ Index rebuild failed:\", errorMessage)\n setError(errorMessage)\n throw error\n } finally {\n setIsIndexBuilding(false)\n }\n },\n [items, stringFields, options],\n )\n\n // フックが返す状態と関数\n return {\n searchTerm,\n setSearchTerm,\n filteredItems,\n isSearching,\n isIndexBuilding,\n error,\n searchHistory,\n options,\n selectItem,\n clearSearch,\n clearHistory,\n resetStats,\n getSearchSuggestions,\n performanceMetrics,\n rebuildIndex,\n }\n}\n\n/**\n * A hook for managing search term history.\n * This hook provides state and functions for adding to and clearing search history.\n * 検索語履歴を管理するためのフック。\n * このフックは、検索履歴の追加とクリアのための状態と関数を提供します。\n * @param maxHistory The maximum number of history items to store.\n * @returns An object containing the search history state and functions.\n */\nexport function useSearchHistory(maxHistory: number = 10): SearchHistoryResult {\n const [searchHistory, setSearchHistory] = useState<SearchHistory[]>([])\n\n /**\n * Adds a new term to the search history.\n * @param term The search term to add.\n * 検索履歴に新しい用語を追加します。\n * @param term 追加する検索用語。\n */\n const addToHistory = useCallback(\n (term: string) => {\n if (!term.trim()) return\n\n setSearchHistory((prev) => {\n const filtered = prev.filter((item) => item.query !== term)\n const newItem: SearchHistory = {\n query: term,\n timestamp: Date.now(),\n resultCount: 0,\n processingTime: 0,\n searchTime: 0,\n successful: false,\n }\n return [newItem, ...filtered].slice(0, maxHistory)\n })\n },\n [maxHistory],\n )\n\n /**\n * Clears the entire search history.\n * 検索履歴全体をクリアします。\n */\n const clearHistory = useCallback(() => {\n setSearchHistory([])\n }, [])\n\n // 履歴の状態と管理関数を返す\n return {\n searchHistory,\n addToHistory,\n clearHistory,\n }\n}\n\n// ===== Re-exports =====\n// 型定義を types/index.ts から再エクスポート\nexport type {\n FuzzySearchResult,\n PerformanceMetrics,\n SearchHistoryResult,\n UnifiedWorkerStats,\n UseFuzzySearchOptions,\n} from \"../types/index\"\n","/**\n * @module types/index\n * @description Core types for the fuzzy search library.\n * @description あいまい検索ライブラリのコア型定義。\n */\n\n// ===== Basic Configuration Types =====\n\n/**\n * @interface FuzzySearchOptions\n * @description Configuration options for fuzzy search functionality.\n * @description あいまい検索機能の設定オプション。\n */\nexport interface FuzzySearchOptions {\n /**\n * @property {number} threshold - Similarity threshold (0.0-1.0).\n * @description 類似度の閾値。この値以上のスコアを持つアイテムが結果に含まれます。\n */\n threshold: number\n\n /**\n * @property {boolean} caseSensitive - Whether to perform case-sensitive search.\n * @description 大文字小文字を区別するかどうか。true の場合、区別して検索します。\n */\n caseSensitive: boolean\n\n /**\n * @property {number} learningWeight - Learning weight for user behavior (0.0-2.0).\n * @description ユーザーの選択行動に基づく学習による重み付け。1.0より大きいと学習効果が強まります。\n */\n learningWeight: number\n\n /**\n * @property {number} debounceMs - Debounce time in milliseconds.\n * @description 検索入力のデバウンス時間(ミリ秒)。連続入力時の不要な検索処理を防ぎます。\n */\n debounceMs: number\n\n /**\n * @property {'and' | 'or'} multiTermOperator - Logical operator applied to space-separated query terms.\n * @description スペース区切りのクエリ語に適用する論理演算子。\n */\n multiTermOperator: \"and\" | \"or\"\n\n /**\n * @property {boolean} autoSearchOnIndexRebuild - Whether to trigger a search immediately after the index is rebuilt.\n * @description インデックス再構築直後に自動的に検索を実行するかどうか。\n */\n autoSearchOnIndexRebuild: boolean\n\n /**\n * @property {Record<string, number>} customWeights - Custom field weights for scoring.\n * @description 特定のフィールドに対するカスタムの重み設定。スコアリングに影響します。\n */\n customWeights: Record<string, number>\n\n /**\n * @property {number} ngramSize - N-gram size for indexing (default: 2).\n * @description インデックス作成に使用する N-gram のサイズ。通常は2または3が使われます。\n */\n ngramSize: number\n\n /**\n * @property {number} minNgramOverlap - Minimum n-gram overlap for candidate filtering.\n * @description 候補フィルタリングのための最小 N-gram 重複数。\n */\n minNgramOverlap: number\n\n /**\n * @property {'score' | 'relevance' | 'original'} sortBy - Sort results by field.\n * @description 検索結果のソート基準。'score' (類似度スコア)、'relevance' (関連性)、'original' (元の順序) から選択します。\n */\n sortBy: \"score\" | \"relevance\" | \"original\"\n\n /**\n * @property {'asc' | 'desc'} sortOrder - Sort order (ascending or descending).\n * @description ソート順序 (昇順または降順)。\n */\n sortOrder: \"asc\" | \"desc\"\n\n /**\n * @property {boolean} enableIndexFiltering - Enable index-based filtering stage.\n * @description インデックスベースのフィルタリングステージを有効にするかどうか。\n */\n enableIndexFiltering: boolean\n\n /**\n * @property {boolean} enableLevenshtein - Enable Levenshtein distance calculation stage.\n * @description レーベンシュタイン距離計算ステージを有効にするかどうか。\n */\n enableLevenshtein: boolean\n\n /**\n * @property {'index-first' | 'levenshtein-first' | 'balanced'} parallelSearchStrategy - Parallel search strategy.\n * @description 並列検索の戦略。'index-first' (インデックス優先)、'levenshtein-first' (レーベンシュタイン優先)、'balanced' (バランス) から選択します。\n */\n parallelSearchStrategy: \"index-first\" | \"levenshtein-first\" | \"balanced\"\n\n /**\n * @property {object} [workerUrls] - Custom Worker URLs for external hosting.\n * @description 外部ホスティング用のカスタム Worker URL。\n */\n workerUrls?: {\n /**\n * @property {string} [indexWorker] - URL for the index worker.\n * @description Index Worker の URL。\n */\n indexWorker?: string\n /**\n * @property {string} [levenshteinWorker] - URL for the levenshtein worker.\n * @description Levenshtein Worker の URL。\n */\n levenshteinWorker?: string\n }\n\n /**\n * @property {object} [indexWorkerOptions] - Index Worker specific parameters.\n * @description Index Worker 固有のパラメータ。\n */\n indexWorkerOptions?: {\n /**\n * @property {IndexStrategy} [strategy] - Index Worker search strategy ('fast' | 'accurate' | 'hybrid').\n * @description Index Worker の検索戦略。\n */\n strategy?: IndexStrategy\n /**\n * @property {number} [threshold] - Similarity threshold for Index Worker (0.0-1.0).\n * @description Index Worker 用の類似度閾値。\n */\n threshold?: number\n /**\n * @property {number} [ngramOverlapThreshold] - N-gram overlap threshold ratio (0.0-1.0).\n * @description N-gram 重複閾値の比率。\n */\n ngramOverlapThreshold?: number\n /**\n * @property {number} [minCandidatesRatio] - Minimum candidates ratio for fast search (0.0-1.0).\n * @description 高速検索用の最小候補比率。\n */\n minCandidatesRatio?: number\n /**\n * @property {number} [maxCandidatesRatio] - Maximum candidates ratio to trigger filtering (0.0-1.0).\n * @description フィルタリングを発動するための最大候補比率。\n */\n maxCandidatesRatio?: number\n /**\n * @property {number} [jaroWinklerPrefix] - Jaro-Winkler prefix scaling factor (0.0-0.25).\n * @description Jaro-Winkler アルゴリズムの接頭辞スケーリング係数。\n */\n jaroWinklerPrefix?: number\n /**\n * @property {number} [maxResults] - Maximum number of results for Index Worker.\n * @description Index Worker 用の最大結果数。\n */\n maxResults?: number\n /**\n * @property {number} [relevanceFieldWeight] - Weight for matched field count in relevance score (0.0-1.0).\n * @description relevance スコア計算時のマッチフィールド数の重み。\n */\n relevanceFieldWeight?: number\n /**\n * @property {number} [relevancePerfectMatchBonus] - Bonus for perfect match in relevance score (0.0-1.0).\n * @description relevance スコア計算時の完全一致ボーナス。\n */\n relevancePerfectMatchBonus?: number\n }\n\n /**\n * @property {object} [levenshteinWorkerOptions] - Levenshtein Worker specific parameters.\n * @description Levenshtein Worker 固有のパラメータ。\n */\n levenshteinWorkerOptions?: {\n /**\n * @property {number} [threshold] - Similarity threshold for Levenshtein Worker (0.0-1.0).\n * @description Levenshtein Worker 用の類似度閾値。\n */\n threshold?: number\n /**\n * @property {number} [lengthSimilarityThreshold] - Length-based similarity threshold for early rejection.\n * @description 長さベースの類似度閾値(早期リジェクション用)。\n */\n lengthSimilarityThreshold?: number\n /**\n * @property {number} [partialMatchBonus] - Partial match bonus score.\n * @description 部分一致が検出された場合に加算されるボーナススコア。\n */\n partialMatchBonus?: number\n /**\n * @property {number} [lengthDiffPenalty] - Length difference penalty ratio.\n * @description 文字列長の差によるペナルティの比率。\n */\n lengthDiffPenalty?: number\n /**\n * @property {number} [maxResults] - Maximum number of results for Levenshtein Worker.\n * @description Levenshtein Worker 用の最大結果数。\n */\n maxResults?: number\n /**\n * @property {number} [relevanceFieldWeight] - Weight for matched field count in relevance score (0.0-1.0).\n * @description relevance スコア計算時のマッチフィールド数の重み。\n */\n relevanceFieldWeight?: number\n }\n}\n\n/**\n * @type PartialFuzzySearchOptions\n * @description Partial options for initializing fuzzy search. Allows overriding a subset of the default options.\n * @description あいまい検索初期化用の部分オプション。デフォルトオプションの一部を上書きできます。\n */\nexport type PartialFuzzySearchOptions = Partial<FuzzySearchOptions>\n\n/**\n * @const DEFAULT_FUZZY_SEARCH_OPTIONS\n * @description Default options for fuzzy search. Used when no options are provided.\n * @description あいまい検索のデフォルトオプション。オプションが指定されなかった場合に使用されます。\n */\nexport const DEFAULT_FUZZY_SEARCH_OPTIONS: FuzzySearchOptions = {\n threshold: 0.4,\n caseSensitive: false,\n learningWeight: 1.2,\n debounceMs: 300,\n multiTermOperator: \"and\",\n autoSearchOnIndexRebuild: true,\n customWeights: {},\n ngramSize: 2,\n minNgramOverlap: 1,\n sortBy: \"relevance\",\n sortOrder: \"desc\",\n enableIndexFiltering: true,\n enableLevenshtein: true,\n parallelSearchStrategy: \"balanced\",\n indexWorkerOptions: {\n strategy: \"hybrid\",\n threshold: 0.4,\n ngramOverlapThreshold: 0.3,\n minCandidatesRatio: 0.1,\n maxCandidatesRatio: 0.5,\n jaroWinklerPrefix: 0.1,\n maxResults: 1000,\n relevanceFieldWeight: 0.2,\n relevancePerfectMatchBonus: 0.1,\n },\n levenshteinWorkerOptions: {\n threshold: 0.3,\n lengthSimilarityThreshold: 0.1,\n partialMatchBonus: 0.1,\n lengthDiffPenalty: 1.0,\n maxResults: 1000,\n relevanceFieldWeight: 0.15,\n },\n}\n\n// ===== Search Result Types =====\n\n/**\n * @interface SearchResultItem\n * @description Represents an individual search result item with metadata.\n * @description メタデータを含む個別の検索結果アイテムを表します。\n * @template T - The type of the original item.\n */\nexport interface SearchResultItem<T = unknown> {\n /**\n * @property {T} item - The original item from the search dataset.\n * @description 検索データセットの元のアイテム。\n */\n item: T\n\n /**\n * @property {number} score - Final similarity score (0.0-1.0+). May exceed 1.0 in Relevance mode due to bonuses.\n * @description 最終的な類似度スコア (0.0-1.0+)。Relevance モードではボーナスにより 1.0 を超える場合があります。\n */\n score: number\n\n /**\n * @property {number} baseScore - Base similarity score before any adjustments (0.0-1.0).\n * @description 調整前の基本類似度スコア (0.0-1.0)。Relevance モードの重み付け前の値です。\n */\n baseScore: number\n\n /**\n * @property {string[]} matchedFields - Fields that matched the search query.\n * @description 検索クエリにマッチしたフィールドのリスト。\n */\n matchedFields: string[]\n\n /**\n * @property {number} [originalIndex] - Original index in the source dataset.\n * @description 元のデータセットにおけるインデックス。\n */\n originalIndex?: number\n\n /**\n * @property {Record<string, unknown>} [metadata] - Additional metadata for the result.\n * @description 結果に関連する追加のメタデータ。\n */\n metadata?: Record<string, unknown>\n}\n\n/**\n * @interface SearchResults\n * @description Represents the complete search results, including items and metadata.\n * @description アイテムとメタデータを含む完全な検索結果を表します。\n * @template T - The type of the items in the results.\n */\nexport interface SearchResults<T = unknown> {\n /**\n * @property {SearchResultItem<T>[]} results - Array of search result items.\n * @description 検索結果アイテムの配列。\n */\n results: SearchResultItem<T>[]\n\n /**\n * @property {number} totalItems - Total number of items searched.\n * @description 検索対象となった総アイテム数。\n */\n totalItems: number\n\n /**\n * @property {number} processingTime - Time taken for the search operation (ms).\n * @description 検索処理全体にかかった時間(ミリ秒)。\n */\n processingTime: number\n\n /**\n * @property {string} query - The search query that was used.\n * @description この検索で使用された検索クエリ。\n */\n query: string\n\n /**\n * @property {Partial<FuzzySearchOptions>} [options] - Search options used for this search.\n * @description この検索で使用された検索オプション。\n */\n options?: Partial<FuzzySearchOptions>\n\n /**\n * @property {SearchStats} [stats] - Additional search statistics.\n * @description 追加の検索統計情報。\n */\n stats?: SearchStats\n}\n\n// ===== Index and Strategy Types =====\n\n/**\n * @type IndexStrategy\n * @description Enumeration of available index strategies.\n * @description 利用可能なインデックス戦略の列挙。\n * - `fast`: N-gram index only for quick filtering. 高速なフィルタリングのためのN-gramインデックスのみ。\n * - `accurate`: Word-based and phonetic indexes for higher accuracy. 高精度な単語ベースおよび音韻インデックス。\n * - `hybrid`: A combination of fast and accurate strategies. 高速戦略と高精度戦略の組み合わせ。\n */\nexport type IndexStrategy = \"fast\" | \"accurate\" | \"hybrid\"\n\n/**\n * @interface IndexMetadata\n * @description Interface for metadata about the search index.\n * @description 検索インデックスに関するメタデータのインターフェース。\n */\nexport interface IndexMetadata {\n /**\n * @property {number} itemCount - Number of items indexed.\n * @description インデックス化されたアイテムの総数。\n */\n itemCount: number\n\n /**\n * @property {number} buildTime - Time taken to build the index (ms).\n * @description インデックスの構築にかかった時間(ミリ秒)。\n */\n buildTime: number\n\n /**\n * @property {number} createdAt - Index creation timestamp.\n * @description インデックスが作成されたときのタイムスタンプ。\n */\n createdAt: number\n\n /**\n * @property {string[]} indexedFields - Fields that were indexed.\n * @description インデックス化の対象となったフィールドのリスト。\n */\n indexedFields: string[]\n\n /**\n * @property {number} ngramSize - N-gram size used for indexing.\n * @description インデックス化に使用された N-gram のサイズ。\n */\n ngramSize: number\n\n /**\n * @property {IndexStrategy} [strategy] - Strategy used for indexing.\n * @description インデックス化に使用された戦略。\n */\n strategy?: IndexStrategy\n\n /**\n * @property {number} [totalIndexSize] - Total size of the index.\n * @description インデックス全体の合計サイズ。\n */\n totalIndexSize?: number\n\n /**\n * @property {object} [indexSizes] - Individual index sizes.\n * @description 各インデックスタイプ(ngram, word, phonetic)の個別サイズ。\n */\n indexSizes?: {\n ngram: number\n word: number\n phonetic: number\n }\n}\n\n// ===== Parallel Search Types =====\n\n/**\n * @type ParallelSearchStrategy\n * @description Options for parallel search strategy.\n * @description 並列検索戦略のオプション。\n *\n * @x-type-gen-skip-doc\n */\nexport type ParallelSearchStrategy = \"index-first\" | \"levenshtein-first\" | \"balanced\"\n\n/**\n * @interface ParallelSearchStageResult\n * @description Represents the result from a single stage in the parallel search pipeline.\n * @description 並列検索パイプラインの単一ステージからの結果を表します。\n * @template T - The type of the items in the results.\n */\nexport interface ParallelSearchStageResult<T = unknown> {\n /**\n * @property {'index' | 'levenshtein'} stage - Stage identifier.\n * @description ステージの識別子 ('index' または 'levenshtein')。\n */\n stage: \"index\" | \"levenshtein\"\n\n /**\n * @property {SearchResultItem<T>[]} results - Search results from this stage.\n * @description このステージからの検索結果。\n */\n results: SearchResultItem<T>[]\n\n /**\n * @property {number} processingTime - Processing time for this stage.\n * @description このステージの処理時間。\n */\n processingTime: number\n\n /**\n * @property {number} confidence - Confidence score for the results of this stage.\n * @description このステージの結果に対する信頼度スコア。\n */\n confidence: number\n\n /**\n * @property {number} candidatesProcessed - Number of candidates processed in this stage.\n * @description このステージで処理された候補数。\n */\n candidatesProcessed: number\n}\n\n/**\n * @interface MergedParallelSearchResult\n * @description Represents the merged result from the parallel search pipeline.\n * @description 並列検索パイプラインからマージされた結果を表します。\n * @template T - The type of the items in the results.\n */\nexport interface MergedParallelSearchResult<T = unknown> {\n /**\n * @property {SearchResultItem<T>[]} results - Final merged search results.\n * @description 最終的にマージされた検索結果。\n */\n results: SearchResultItem<T>[]\n\n /**\n * @property {ParallelSearchStageResult<T>} indexStage - Results from the index stage.\n * @description インデックスステージからの結果。\n */\n indexStage: ParallelSearchStageResult<T>\n\n /**\n * @property {ParallelSearchStageResult<T>} levenshteinStage - Results from the Levenshtein stage.\n * @description レーベンシュタインステージからの結果。\n */\n levenshteinStage: ParallelSearchStageResult<T>\n\n /**\n * @property {number} totalProcessingTime - Total processing time for the entire pipeline.\n * @description パイプライン全体の総処理時間。\n */\n totalProcessingTime: number\n\n /**\n * @property {ParallelSearchStrategy} mergeStrategy - Merge strategy used.\n * @description 使用されたマージ戦略。\n */\n mergeStrategy: ParallelSearchStrategy\n\n /**\n * @property {number} mergeQuality - Quality score of the merged results.\n * @description マージされた結果の品質スコア。\n */\n mergeQuality: number\n}\n\n// ===== Worker Communication Types =====\n\n/**\n * @interface IndexWorkerRequest\n * @description Defines the request structure for communication with the Indexing Worker.\n * @description インデックス作成 Worker との通信のためのリクエスト構造を定義します。\n * @template T - The type of items to be indexed or searched.\n */\nexport interface IndexWorkerRequest<T = unknown> {\n type: \"ping\" | \"buildIndex\" | \"indexSearch\" | \"getStats\" | \"resetStats\"\n id: string\n query?: string\n items?: T[]\n searchFields?: string[]\n options?: Partial<FuzzySearchOptions>\n}\n\n/**\n * @interface UnifiedWorkerResponse\n * @description Completely unified response structure for all worker types.\n * @description すべてのワーカータイプに対応する完全統一レスポンス構造。\n * @template T - The type of items in the search results.\n */\nexport interface UnifiedWorkerResponse<T = unknown> {\n type: string\n id: string\n\n // 必須フィールド\n stats: UnifiedWorkerStats\n metrics: WorkerPerformanceMetrics\n processingTime: number\n\n // 条件付きフィールド\n results?: SearchResultItem<T>[]\n error?: string\n\n // ワーカー固有情報\n workerType: \"index\" | \"levenshtein\"\n operationMeta?: Record<string, unknown>\n}\n\n/**\n * @interface LevenshteinWorkerRequest\n * @description Defines the request structure for communication with the Levenshtein Distance Worker.\n * @description Levenshtein 距離計算 Worker との通信のためのリクエスト構造を定義します。\n * @template T - The type of items to be processed.\n */\nexport interface LevenshteinWorkerRequest<T = unknown> {\n type: \"ping\" | \"setData\" | \"calculateSimilarity\" | \"batchSimilarity\" | \"getStats\" | \"levenshteinSearch\" | \"resetStats\"\n id: string\n query?: string\n items?: T[]\n searchFields?: string[]\n candidateIndices?: number[]\n targets?: string[]\n target?: string\n options?: Partial<FuzzySearchOptions>\n threshold?: number\n enablePhonetic?: boolean\n}\n\n// ===== Worker-Specific Types =====\n\n/**\n * @interface WorkerSearchStatistics\n * @description Enhanced search statistics provided by a Worker.\n * @description Worker から提供される拡張検索統計。\n */\nexport interface WorkerSearchStatistics {\n candidatesFound: number\n indexSize: number\n ngrams: number\n searchStrategy: string\n}\n\n/**\n * @interface DetailedWorkerStats\n * @description Detailed performance metrics and statistics from a Worker.\n * @description Worker からの詳細なパフォーマンス指標と統計。\n */\nexport interface DetailedWorkerStats {\n type: \"stats\"\n requestId: string\n workerStats: {\n totalSearches: number\n totalIndexBuilds: number\n currentDataSize: number\n lastIndexTime: number\n }\n memory: {\n estimatedMemoryUsage: number\n heapUsage?: number\n maxMemoryUsage?: number\n }\n performanceMetrics: {\n timings: {\n averageSearchTime: number\n averageIndexTime: number\n maxSearchTime: number\n minSearchTime: number\n }\n throughput: {\n searchesPerSecond: number\n indexOperationsPerSecond: number\n }\n }\n indexMetadata?: {\n totalTokens: number\n uniqueTokens: number\n buildTime: number\n size: number\n }\n timestamp: number\n}\n\n/**\n * @interface QueryLengthStat\n * @description Statistics related to query length.\n * @description クエリ長に関連する統計。\n */\nexport interface QueryLengthStat {\n count: number\n avgTime: number\n totalTime: number\n}\n\n/**\n * @interface FieldMatchStat\n * @description Statistics related to matches in specific fields.\n * @description 特定のフィールドでのマッチに関する統計。\n */\nexport interface FieldMatchStat {\n matches: number\n avgScore: number\n totalScore: number\n}\n\n/**\n * @interface SimilarityThresholdStats\n * @description Statistics on the distribution of similarity scores.\n * @description 類似度スコアの分布に関する統計。\n */\nexport interface SimilarityThresholdStats {\n above80: number\n above60: number\n above40: number\n below40: number\n}\n\n/**\n * @interface PerformanceInsights\n * @description Data providing insights into search performance.\n * @description 検索パフォーマンスに関する洞察を提供するデータ。\n */\nexport interface PerformanceInsights {\n recentAverageProcessingTime: number\n recentAverageResultCount: number\n throughputPerSecond: number\n totalHistoryEntries: number\n mostCommonQueryLength: number\n bestPerformingField: string\n similarityDistribution: {\n highSimilarity: number\n mediumSimilarity: number\n lowSimilarity: number\n veryLowSimilarity: number\n }\n}\n\n/**\n * @interface PerformanceHistoryEntry\n * @description Individual performance history entry for tracking operation performance.\n * @description 操作パフォーマンスを追跡するための個別のパフォーマンス履歴エントリ。\n */\nexport interface PerformanceHistoryEntry {\n timestamp: number\n queryLength: number\n itemCount: number\n processingTime: number\n resultCount: number\n operationType: string\n additionalMetrics?: Record<string, unknown>\n}\n\n/**\n * @interface FieldStatistic\n * @description Statistics for individual search fields across all operations.\n * @description 全操作における個別検索フィールドの統計。\n */\nexport interface FieldStatistic {\n matchCount: number\n averageScore: number\n totalScore: number\n bestScore: number\n worstScore: number\n}\n\n/**\n * @interface UnifiedWorkerStats\n * @description Completely unified statistics interface for all worker types.\n * @description すべてのワーカータイプに対応する完全統一統計インターフェース。\n */\nexport interface UnifiedWorkerStats {\n /**\n * @property basic Basic statistics common to all workers.\n * @description 全ワーカー共通の基本統計。\n */\n basic: {\n totalSearches: number\n totalProcessingTime: number\n averageSearchTime: number\n lastSearchTime: number\n errorCount: number\n timestamp: number\n }\n\n /**\n * @property workerSpecific Worker-type specific statistics.\n * @description ワーカータイプ固有の統計。\n */\n workerSpecific: {\n workerType: \"index\" | \"levenshtein\"\n operationCount: number\n operationDistribution: Record<string, number>\n specificMetrics: Record<string, unknown>\n }\n\n /**\n * @property analysis Advanced analysis and insights.\n * @description 高度な分析とインサイト。\n */\n analysis: {\n fieldPerformance: Record<string, FieldStatistic>\n performanceInsights: PerformanceInsights\n recentHistory: PerformanceHistoryEntry[]\n }\n}\n\n/**\n * @type WorkerStats\n * @description Unified alias for basic worker statistics.\n * @description 基本ワーカー統計の統一エイリアス。\n */\nexport type WorkerStats = UnifiedWorkerStats[\"basic\"]\n\n/**\n * @type WorkerSpecificStats\n * @description Alias for worker-specific statistics section.\n * @description ワーカー固有統計セクションのエイリアス。\n */\nexport type WorkerSpecificStats = UnifiedWorkerStats[\"workerSpecific\"]\n\n/**\n * @type WorkerAnalysis\n * @description Alias for worker analysis section.\n * @description ワーカー分析セクションのエイリアス。\n */\nexport type WorkerAnalysis = UnifiedWorkerStats[\"analysis\"]\n\n/**\n * @interface WorkerPerformanceMetrics\n * @description Performance metrics for detailed monitoring of a Worker.\n * @description Worker の詳細な監視のためのパフォーマンスメトリクス。\n */\nexport interface WorkerPerformanceMetrics {\n timings: {\n averageProcessingTime: number\n fastestProcessing: number\n slowestProcessing: number\n lastProcessingTime: number\n }\n throughput: {\n operationsPerSecond: number\n totalOperations: number\n }\n quality: {\n successRate: number\n errorRate: number\n timeoutRate: number\n }\n}\n\n// ===== Search Statistics Types =====\n\n/**\n * @interface SearchStats\n * @description Overall search statistics for performance monitoring.\n * @description パフォーマンス監視のための全体的な検索統計。\n */\nexport interface SearchStats {\n /**\n * @property {number} totalSearches - Total number of searches performed.\n * @description 実行された総検索数。\n */\n totalSearches: number\n\n /**\n * @property {number} averageSearchTime - Average search time in milliseconds.\n * @description 平均検索時間(ミリ秒)。\n */\n averageSearchTime: number\n\n /**\n * @property {Array<{ query: string; count: number }>} popularQueries - Popular search queries.\n * @description 人気のある検索クエリとその実行回数。\n */\n popularQueries: Array<{ query: string; count: number }>\n\n /**\n * @property {Record<string, { averageTime: number; sampleSize: number }>} performanceBySize - Performance statistics by dataset size.\n * @description データセットのサイズ別のパフォーマンス統計。\n */\n performanceBySize: Record<string, { averageTime: number; sampleSize: number }>\n\n /**\n * @property {object} workers - Worker statistics information.\n * @description 各ワーカーの統計情報。\n */\n workers: {\n index: {\n stats: UnifiedWorkerStats\n }\n levenshtein: {\n stats: UnifiedWorkerStats\n }\n }\n}\n\n/**\n * @interface SearchStatistics\n * @description Search statistics provided by a worker.\n * @description Worker から提供される検索統計。\n */\nexport interface SearchStatistics {\n candidatesFound: number\n indexSize: number\n ngrams: number\n words?: number\n phonetic?: number\n searchStrategy: string\n}\n\n/**\n * @interface SearchHistory\n * @description Represents a single entry in the search history for learning and analytics.\n * @description 学習と分析のための検索履歴の単一エントリを表します。\n */\nexport interface SearchHistory {\n /**\n * @property {string} query - The search query.\n * @description 検索クエリ。\n */\n query: string\n\n /**\n * @property {number} timestamp - Timestamp of the search.\n * @description 検索が実行されたときのタイムスタンプ。\n */\n timestamp: number\n\n /**\n * @property {number} resultCount - Number of results found.\n * @description 見つかった結果の数。\n */\n resultCount: number\n\n /**\n * @property {number} processingTime - Processing time in milliseconds.\n * @description 処理にかかった時間(ミリ秒)。\n */\n processingTime: number\n\n /**\n * @property {number} [searchTime] - Alias for processingTime.\n * @description processingTime のエイリアス。\n */\n searchTime?: number\n\n /**\n * @property {number} [selectedIndex] - Index of the selected result, if any.\n * @description ユーザーが選択した結果のインデックス(存在する場合)。\n */\n selectedIndex?: number\n\n /**\n * @property {string} [selectedItemId] - ID of the selected item, if any.\n * @description ユーザーが選択したアイテムのID(存在する場合)。\n */\n selectedItemId?: string\n\n /**\n * @property {number} [satisfactionScore] - User satisfaction score (1-5).\n * @description ユーザー満足度スコア(1-5)。\n */\n satisfactionScore?: number\n\n /**\n * @property {boolean} successful - Whether the search was considered successful.\n * @description 検索が成功したと見なされたかどうか。\n */\n successful: boolean\n\n /**\n * @property {string} [context] - Context or source of the search.\n * @description 検索が実行されたコンテキストまたはソース。\n */\n context?: string\n}\n\n/**\n * @interface LearningData\n * @description Data structure for storing learned information to improve search results.\n * @description 検索結果を改善するために学習した情報を格納するためのデータ構造。\n */\nexport interface LearningData {\n /**\n * @property {Record<string, number>} fieldWeights - Field-specific weights learned from user behavior.\n * @description ユーザーの行動から学習したフィールド固有の重み。\n */\n fieldWeights: Record<string, number>\n\n /**\n * @property {Record<string, number>} queryPatterns - Query patterns and their frequency count.\n * @description クエリのパターンとその出現頻度。\n */\n queryPatterns: Record<string, number>\n\n /**\n * @property {object} userPreferences - User preferences and behavior.\n * @description ユーザーの好みと行動の記録。\n */\n userPreferences: {\n preferredThreshold: number\n frequentFields: string[]\n searchStyle: \"exact\" | \"fuzzy\" | \"mixed\"\n lastUsedOptions: Partial<FuzzySearchOptions>\n }\n\n /**\n * @property {object} performanceMetrics - Performance metrics for learning optimization.\n * @description 学習の最適化に使用されるパフォーマンス指標。\n */\n performanceMetrics: {\n averageSearchTime: Record<string, number>\n indexBuildTime: number\n }\n}\n\n// ===== Utility Types =====\n\n/**\n * @type ExtractItemType\n * @description Utility type to extract the item type from a SearchResultItem.\n * @description SearchResultItem からアイテムの型を抽出するためのユーティリティ型。\n * @template T - The type of the SearchResultItem.\n */\nexport type ExtractItemType<T> = T extends SearchResultItem<infer U> ? U : never\n\n/**\n * @type SearchFields\n * @description Defines the search fields with type safety, accepting keys of T or strings.\n * @description 型安全性を備えた検索フィールドを定義します。T のキーまたは文字列を受け入れます。\n * @template T - The type of the items being searched.\n */\nexport type SearchFields<T> = Array<keyof T | string>\n\n/**\n * @type SearchResultCallback\n * @description Callback function type for handling search results.\n * @description 検索結果を処理するためのコールバック関数型。\n * @template T - The type of the items in the results.\n */\nexport type SearchResultCallback<T> = (results: SearchResults<T>) => void\n\n// ===== Type Guards =====\n\n/**\n * @function isSearchResultItem\n * @description Type guard to check if an object is a SearchResultItem.\n * @description オブジェクトが SearchResultItem かどうかをチェックする型ガード。\n * @param {unknown} obj - The object to check.\n * @returns {boolean} - True if the object is a SearchResultItem.\n * @template T - The expected type of the item within the SearchResultItem.\n */\nexport function isSearchResultItem<T>(obj: unknown): obj is SearchResultItem<T> {\n // オブジェクトであり、null でなく、必須プロパティが存在するかをチェック\n return typeof obj === \"object\" && obj !== null && \"item\" in obj && \"score\" in obj && \"matchedFields\" in obj\n}\n\n// ===== React Hook Types =====\n\n/**\n * @type UseFuzzySearchOptions\n * @description Options type for the useFuzzySearch hook, extending PartialFuzzySearchOptions.\n * @description useFuzzySearch フック用のオプション型。PartialFuzzySearchOptions を拡張します。\n */\nexport type UseFuzzySearchOptions = PartialFuzzySearchOptions\n\n/**\n * @interface SearchHistoryResult\n * @description The return type for a hook that manages search history.\n * @description 検索履歴を管理するフックの戻り値の型。\n */\nexport interface SearchHistoryResult {\n searchHistory: SearchHistory[]\n addToHistory: (term: string) => void\n clearHistory: () => void\n}\n\n/**\n * @interface PerformanceMetrics\n * @description Data type for the performance metrics callback.\n * @description パフォーマンス指標コールバック用のデータ型。\n */\nexport interface PerformanceMetrics {\n totalSearches: num