@danielsimonjr/memoryjs 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +266 -0
  3. package/dist/core/EntityManager.d.ts +268 -0
  4. package/dist/core/EntityManager.d.ts.map +1 -0
  5. package/dist/core/EntityManager.js +512 -0
  6. package/dist/core/EntityManager.js.map +1 -0
  7. package/dist/core/GraphEventEmitter.d.ts +202 -0
  8. package/dist/core/GraphEventEmitter.d.ts.map +1 -0
  9. package/dist/core/GraphEventEmitter.js +347 -0
  10. package/dist/core/GraphEventEmitter.js.map +1 -0
  11. package/dist/core/GraphStorage.d.ts +395 -0
  12. package/dist/core/GraphStorage.d.ts.map +1 -0
  13. package/dist/core/GraphStorage.js +786 -0
  14. package/dist/core/GraphStorage.js.map +1 -0
  15. package/dist/core/GraphTraversal.d.ts +141 -0
  16. package/dist/core/GraphTraversal.d.ts.map +1 -0
  17. package/dist/core/GraphTraversal.js +574 -0
  18. package/dist/core/GraphTraversal.js.map +1 -0
  19. package/dist/core/HierarchyManager.d.ts +111 -0
  20. package/dist/core/HierarchyManager.d.ts.map +1 -0
  21. package/dist/core/HierarchyManager.js +225 -0
  22. package/dist/core/HierarchyManager.js.map +1 -0
  23. package/dist/core/ManagerContext.d.ts +76 -0
  24. package/dist/core/ManagerContext.d.ts.map +1 -0
  25. package/dist/core/ManagerContext.js +129 -0
  26. package/dist/core/ManagerContext.js.map +1 -0
  27. package/dist/core/ObservationManager.d.ts +85 -0
  28. package/dist/core/ObservationManager.d.ts.map +1 -0
  29. package/dist/core/ObservationManager.js +124 -0
  30. package/dist/core/ObservationManager.js.map +1 -0
  31. package/dist/core/RelationManager.d.ts +131 -0
  32. package/dist/core/RelationManager.d.ts.map +1 -0
  33. package/dist/core/RelationManager.js +212 -0
  34. package/dist/core/RelationManager.js.map +1 -0
  35. package/dist/core/SQLiteStorage.d.ts +354 -0
  36. package/dist/core/SQLiteStorage.d.ts.map +1 -0
  37. package/dist/core/SQLiteStorage.js +919 -0
  38. package/dist/core/SQLiteStorage.js.map +1 -0
  39. package/dist/core/StorageFactory.d.ts +45 -0
  40. package/dist/core/StorageFactory.d.ts.map +1 -0
  41. package/dist/core/StorageFactory.js +65 -0
  42. package/dist/core/StorageFactory.js.map +1 -0
  43. package/dist/core/TransactionManager.d.ts +464 -0
  44. package/dist/core/TransactionManager.d.ts.map +1 -0
  45. package/dist/core/TransactionManager.js +869 -0
  46. package/dist/core/TransactionManager.js.map +1 -0
  47. package/dist/core/index.d.ts +17 -0
  48. package/dist/core/index.d.ts.map +1 -0
  49. package/dist/core/index.js +20 -0
  50. package/dist/core/index.js.map +1 -0
  51. package/dist/features/AnalyticsManager.d.ts +44 -0
  52. package/dist/features/AnalyticsManager.d.ts.map +1 -0
  53. package/dist/features/AnalyticsManager.js +224 -0
  54. package/dist/features/AnalyticsManager.js.map +1 -0
  55. package/dist/features/ArchiveManager.d.ts +133 -0
  56. package/dist/features/ArchiveManager.d.ts.map +1 -0
  57. package/dist/features/ArchiveManager.js +282 -0
  58. package/dist/features/ArchiveManager.js.map +1 -0
  59. package/dist/features/CompressionManager.d.ts +119 -0
  60. package/dist/features/CompressionManager.d.ts.map +1 -0
  61. package/dist/features/CompressionManager.js +470 -0
  62. package/dist/features/CompressionManager.js.map +1 -0
  63. package/dist/features/IOManager.d.ts +225 -0
  64. package/dist/features/IOManager.d.ts.map +1 -0
  65. package/dist/features/IOManager.js +1093 -0
  66. package/dist/features/IOManager.js.map +1 -0
  67. package/dist/features/KeywordExtractor.d.ts +61 -0
  68. package/dist/features/KeywordExtractor.d.ts.map +1 -0
  69. package/dist/features/KeywordExtractor.js +127 -0
  70. package/dist/features/KeywordExtractor.js.map +1 -0
  71. package/dist/features/ObservationNormalizer.d.ts +90 -0
  72. package/dist/features/ObservationNormalizer.d.ts.map +1 -0
  73. package/dist/features/ObservationNormalizer.js +194 -0
  74. package/dist/features/ObservationNormalizer.js.map +1 -0
  75. package/dist/features/StreamingExporter.d.ts +128 -0
  76. package/dist/features/StreamingExporter.d.ts.map +1 -0
  77. package/dist/features/StreamingExporter.js +212 -0
  78. package/dist/features/StreamingExporter.js.map +1 -0
  79. package/dist/features/TagManager.d.ts +147 -0
  80. package/dist/features/TagManager.d.ts.map +1 -0
  81. package/dist/features/TagManager.js +211 -0
  82. package/dist/features/TagManager.js.map +1 -0
  83. package/dist/features/index.d.ts +14 -0
  84. package/dist/features/index.d.ts.map +1 -0
  85. package/dist/features/index.js +15 -0
  86. package/dist/features/index.js.map +1 -0
  87. package/dist/index.d.ts +15 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +20 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/search/BM25Search.d.ts +148 -0
  92. package/dist/search/BM25Search.d.ts.map +1 -0
  93. package/dist/search/BM25Search.js +340 -0
  94. package/dist/search/BM25Search.js.map +1 -0
  95. package/dist/search/BasicSearch.d.ts +51 -0
  96. package/dist/search/BasicSearch.d.ts.map +1 -0
  97. package/dist/search/BasicSearch.js +138 -0
  98. package/dist/search/BasicSearch.js.map +1 -0
  99. package/dist/search/BooleanSearch.d.ts +98 -0
  100. package/dist/search/BooleanSearch.d.ts.map +1 -0
  101. package/dist/search/BooleanSearch.js +431 -0
  102. package/dist/search/BooleanSearch.js.map +1 -0
  103. package/dist/search/EarlyTerminationManager.d.ts +140 -0
  104. package/dist/search/EarlyTerminationManager.d.ts.map +1 -0
  105. package/dist/search/EarlyTerminationManager.js +280 -0
  106. package/dist/search/EarlyTerminationManager.js.map +1 -0
  107. package/dist/search/EmbeddingCache.d.ts +175 -0
  108. package/dist/search/EmbeddingCache.d.ts.map +1 -0
  109. package/dist/search/EmbeddingCache.js +247 -0
  110. package/dist/search/EmbeddingCache.js.map +1 -0
  111. package/dist/search/EmbeddingService.d.ts +277 -0
  112. package/dist/search/EmbeddingService.d.ts.map +1 -0
  113. package/dist/search/EmbeddingService.js +531 -0
  114. package/dist/search/EmbeddingService.js.map +1 -0
  115. package/dist/search/FuzzySearch.d.ts +118 -0
  116. package/dist/search/FuzzySearch.d.ts.map +1 -0
  117. package/dist/search/FuzzySearch.js +313 -0
  118. package/dist/search/FuzzySearch.js.map +1 -0
  119. package/dist/search/HybridScorer.d.ts +181 -0
  120. package/dist/search/HybridScorer.d.ts.map +1 -0
  121. package/dist/search/HybridScorer.js +258 -0
  122. package/dist/search/HybridScorer.js.map +1 -0
  123. package/dist/search/HybridSearchManager.d.ts +80 -0
  124. package/dist/search/HybridSearchManager.d.ts.map +1 -0
  125. package/dist/search/HybridSearchManager.js +188 -0
  126. package/dist/search/HybridSearchManager.js.map +1 -0
  127. package/dist/search/IncrementalIndexer.d.ts +201 -0
  128. package/dist/search/IncrementalIndexer.d.ts.map +1 -0
  129. package/dist/search/IncrementalIndexer.js +343 -0
  130. package/dist/search/IncrementalIndexer.js.map +1 -0
  131. package/dist/search/OptimizedInvertedIndex.d.ts +163 -0
  132. package/dist/search/OptimizedInvertedIndex.d.ts.map +1 -0
  133. package/dist/search/OptimizedInvertedIndex.js +359 -0
  134. package/dist/search/OptimizedInvertedIndex.js.map +1 -0
  135. package/dist/search/ParallelSearchExecutor.d.ts +172 -0
  136. package/dist/search/ParallelSearchExecutor.d.ts.map +1 -0
  137. package/dist/search/ParallelSearchExecutor.js +310 -0
  138. package/dist/search/ParallelSearchExecutor.js.map +1 -0
  139. package/dist/search/QuantizedVectorStore.d.ts +171 -0
  140. package/dist/search/QuantizedVectorStore.d.ts.map +1 -0
  141. package/dist/search/QuantizedVectorStore.js +308 -0
  142. package/dist/search/QuantizedVectorStore.js.map +1 -0
  143. package/dist/search/QueryAnalyzer.d.ts +76 -0
  144. package/dist/search/QueryAnalyzer.d.ts.map +1 -0
  145. package/dist/search/QueryAnalyzer.js +228 -0
  146. package/dist/search/QueryAnalyzer.js.map +1 -0
  147. package/dist/search/QueryCostEstimator.d.ts +244 -0
  148. package/dist/search/QueryCostEstimator.d.ts.map +1 -0
  149. package/dist/search/QueryCostEstimator.js +653 -0
  150. package/dist/search/QueryCostEstimator.js.map +1 -0
  151. package/dist/search/QueryPlanCache.d.ts +220 -0
  152. package/dist/search/QueryPlanCache.d.ts.map +1 -0
  153. package/dist/search/QueryPlanCache.js +380 -0
  154. package/dist/search/QueryPlanCache.js.map +1 -0
  155. package/dist/search/QueryPlanner.d.ts +58 -0
  156. package/dist/search/QueryPlanner.d.ts.map +1 -0
  157. package/dist/search/QueryPlanner.js +138 -0
  158. package/dist/search/QueryPlanner.js.map +1 -0
  159. package/dist/search/RankedSearch.d.ts +71 -0
  160. package/dist/search/RankedSearch.d.ts.map +1 -0
  161. package/dist/search/RankedSearch.js +239 -0
  162. package/dist/search/RankedSearch.js.map +1 -0
  163. package/dist/search/ReflectionManager.d.ts +120 -0
  164. package/dist/search/ReflectionManager.d.ts.map +1 -0
  165. package/dist/search/ReflectionManager.js +232 -0
  166. package/dist/search/ReflectionManager.js.map +1 -0
  167. package/dist/search/SavedSearchManager.d.ts +79 -0
  168. package/dist/search/SavedSearchManager.d.ts.map +1 -0
  169. package/dist/search/SavedSearchManager.js +147 -0
  170. package/dist/search/SavedSearchManager.js.map +1 -0
  171. package/dist/search/SearchFilterChain.d.ts +120 -0
  172. package/dist/search/SearchFilterChain.d.ts.map +1 -0
  173. package/dist/search/SearchFilterChain.js +186 -0
  174. package/dist/search/SearchFilterChain.js.map +1 -0
  175. package/dist/search/SearchManager.d.ts +326 -0
  176. package/dist/search/SearchManager.d.ts.map +1 -0
  177. package/dist/search/SearchManager.js +454 -0
  178. package/dist/search/SearchManager.js.map +1 -0
  179. package/dist/search/SearchSuggestions.d.ts +27 -0
  180. package/dist/search/SearchSuggestions.d.ts.map +1 -0
  181. package/dist/search/SearchSuggestions.js +58 -0
  182. package/dist/search/SearchSuggestions.js.map +1 -0
  183. package/dist/search/SemanticSearch.d.ts +149 -0
  184. package/dist/search/SemanticSearch.d.ts.map +1 -0
  185. package/dist/search/SemanticSearch.js +324 -0
  186. package/dist/search/SemanticSearch.js.map +1 -0
  187. package/dist/search/SymbolicSearch.d.ts +61 -0
  188. package/dist/search/SymbolicSearch.d.ts.map +1 -0
  189. package/dist/search/SymbolicSearch.js +164 -0
  190. package/dist/search/SymbolicSearch.js.map +1 -0
  191. package/dist/search/TFIDFEventSync.d.ts +85 -0
  192. package/dist/search/TFIDFEventSync.d.ts.map +1 -0
  193. package/dist/search/TFIDFEventSync.js +134 -0
  194. package/dist/search/TFIDFEventSync.js.map +1 -0
  195. package/dist/search/TFIDFIndexManager.d.ts +151 -0
  196. package/dist/search/TFIDFIndexManager.d.ts.map +1 -0
  197. package/dist/search/TFIDFIndexManager.js +433 -0
  198. package/dist/search/TFIDFIndexManager.js.map +1 -0
  199. package/dist/search/VectorStore.d.ts +235 -0
  200. package/dist/search/VectorStore.d.ts.map +1 -0
  201. package/dist/search/VectorStore.js +312 -0
  202. package/dist/search/VectorStore.js.map +1 -0
  203. package/dist/search/index.d.ts +35 -0
  204. package/dist/search/index.d.ts.map +1 -0
  205. package/dist/search/index.js +53 -0
  206. package/dist/search/index.js.map +1 -0
  207. package/dist/types/index.d.ts +13 -0
  208. package/dist/types/index.d.ts.map +1 -0
  209. package/dist/types/index.js +13 -0
  210. package/dist/types/index.js.map +1 -0
  211. package/dist/types/types.d.ts +1811 -0
  212. package/dist/types/types.d.ts.map +1 -0
  213. package/dist/types/types.js +10 -0
  214. package/dist/types/types.js.map +1 -0
  215. package/dist/utils/BatchProcessor.d.ts +271 -0
  216. package/dist/utils/BatchProcessor.d.ts.map +1 -0
  217. package/dist/utils/BatchProcessor.js +377 -0
  218. package/dist/utils/BatchProcessor.js.map +1 -0
  219. package/dist/utils/MemoryMonitor.d.ts +176 -0
  220. package/dist/utils/MemoryMonitor.d.ts.map +1 -0
  221. package/dist/utils/MemoryMonitor.js +306 -0
  222. package/dist/utils/MemoryMonitor.js.map +1 -0
  223. package/dist/utils/WorkerPoolManager.d.ts +233 -0
  224. package/dist/utils/WorkerPoolManager.d.ts.map +1 -0
  225. package/dist/utils/WorkerPoolManager.js +421 -0
  226. package/dist/utils/WorkerPoolManager.js.map +1 -0
  227. package/dist/utils/compressedCache.d.ts +221 -0
  228. package/dist/utils/compressedCache.d.ts.map +1 -0
  229. package/dist/utils/compressedCache.js +349 -0
  230. package/dist/utils/compressedCache.js.map +1 -0
  231. package/dist/utils/compressionUtil.d.ts +214 -0
  232. package/dist/utils/compressionUtil.d.ts.map +1 -0
  233. package/dist/utils/compressionUtil.js +248 -0
  234. package/dist/utils/compressionUtil.js.map +1 -0
  235. package/dist/utils/constants.d.ts +245 -0
  236. package/dist/utils/constants.d.ts.map +1 -0
  237. package/dist/utils/constants.js +253 -0
  238. package/dist/utils/constants.js.map +1 -0
  239. package/dist/utils/entityUtils.d.ts +379 -0
  240. package/dist/utils/entityUtils.d.ts.map +1 -0
  241. package/dist/utils/entityUtils.js +649 -0
  242. package/dist/utils/entityUtils.js.map +1 -0
  243. package/dist/utils/errors.d.ts +95 -0
  244. package/dist/utils/errors.d.ts.map +1 -0
  245. package/dist/utils/errors.js +146 -0
  246. package/dist/utils/errors.js.map +1 -0
  247. package/dist/utils/formatters.d.ts +145 -0
  248. package/dist/utils/formatters.d.ts.map +1 -0
  249. package/dist/utils/formatters.js +133 -0
  250. package/dist/utils/formatters.js.map +1 -0
  251. package/dist/utils/index.d.ts +26 -0
  252. package/dist/utils/index.d.ts.map +1 -0
  253. package/dist/utils/index.js +88 -0
  254. package/dist/utils/index.js.map +1 -0
  255. package/dist/utils/indexes.d.ts +270 -0
  256. package/dist/utils/indexes.d.ts.map +1 -0
  257. package/dist/utils/indexes.js +527 -0
  258. package/dist/utils/indexes.js.map +1 -0
  259. package/dist/utils/logger.d.ts +31 -0
  260. package/dist/utils/logger.d.ts.map +1 -0
  261. package/dist/utils/logger.js +41 -0
  262. package/dist/utils/logger.js.map +1 -0
  263. package/dist/utils/operationUtils.d.ts +124 -0
  264. package/dist/utils/operationUtils.d.ts.map +1 -0
  265. package/dist/utils/operationUtils.js +176 -0
  266. package/dist/utils/operationUtils.js.map +1 -0
  267. package/dist/utils/parallelUtils.d.ts +76 -0
  268. package/dist/utils/parallelUtils.d.ts.map +1 -0
  269. package/dist/utils/parallelUtils.js +192 -0
  270. package/dist/utils/parallelUtils.js.map +1 -0
  271. package/dist/utils/schemas.d.ts +556 -0
  272. package/dist/utils/schemas.d.ts.map +1 -0
  273. package/dist/utils/schemas.js +485 -0
  274. package/dist/utils/schemas.js.map +1 -0
  275. package/dist/utils/searchAlgorithms.d.ts +99 -0
  276. package/dist/utils/searchAlgorithms.d.ts.map +1 -0
  277. package/dist/utils/searchAlgorithms.js +168 -0
  278. package/dist/utils/searchAlgorithms.js.map +1 -0
  279. package/dist/utils/searchCache.d.ts +108 -0
  280. package/dist/utils/searchCache.d.ts.map +1 -0
  281. package/dist/utils/searchCache.js +210 -0
  282. package/dist/utils/searchCache.js.map +1 -0
  283. package/dist/utils/taskScheduler.d.ts +294 -0
  284. package/dist/utils/taskScheduler.d.ts.map +1 -0
  285. package/dist/utils/taskScheduler.js +487 -0
  286. package/dist/utils/taskScheduler.js.map +1 -0
  287. package/dist/workers/index.d.ts +12 -0
  288. package/dist/workers/index.d.ts.map +1 -0
  289. package/dist/workers/index.js +10 -0
  290. package/dist/workers/index.js.map +1 -0
  291. package/dist/workers/levenshteinWorker.d.ts +60 -0
  292. package/dist/workers/levenshteinWorker.d.ts.map +1 -0
  293. package/dist/workers/levenshteinWorker.js +99 -0
  294. package/dist/workers/levenshteinWorker.js.map +1 -0
  295. package/package.json +69 -0
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Fuzzy Search
3
+ *
4
+ * Search with typo tolerance using Levenshtein distance similarity.
5
+ * Uses workerpool for parallel processing on large datasets.
6
+ *
7
+ * @module search/FuzzySearch
8
+ */
9
+ import { levenshteinDistance } from '../utils/index.js';
10
+ import { SEARCH_LIMITS } from '../utils/constants.js';
11
+ import { SearchFilterChain } from './SearchFilterChain.js';
12
+ import workerpool from '@danielsimonjr/workerpool';
13
+ import { fileURLToPath } from 'url';
14
+ import { dirname, join, sep } from 'path';
15
+ /**
16
+ * Default fuzzy search similarity threshold (70% match required).
17
+ * Lower values are more permissive (more typos tolerated).
18
+ * Higher values are stricter (fewer typos tolerated).
19
+ */
20
+ export const DEFAULT_FUZZY_THRESHOLD = 0.7;
21
+ /**
22
+ * Phase 4 Sprint 3: Maximum cache size to prevent memory bloat.
23
+ */
24
+ const FUZZY_CACHE_MAX_SIZE = 100;
25
+ /**
26
+ * Phase 4 Sprint 3: Cache TTL in milliseconds (5 minutes).
27
+ */
28
+ const FUZZY_CACHE_TTL_MS = 5 * 60 * 1000;
29
+ /**
30
+ * Phase 7 Sprint 3: Minimum number of entities to activate worker pool.
31
+ */
32
+ const WORKER_MIN_ENTITIES = 500;
33
+ /**
34
+ * Phase 7 Sprint 3: Maximum threshold for worker pool activation.
35
+ * Higher thresholds have fewer matches, so single-threaded is faster.
36
+ */
37
+ const WORKER_MAX_THRESHOLD = 0.8;
38
+ /**
39
+ * Performs fuzzy search with configurable similarity threshold.
40
+ */
41
+ export class FuzzySearch {
42
+ storage;
43
+ /**
44
+ * Phase 4 Sprint 3: Result cache for fuzzy search.
45
+ * Maps cache key -> cached entity names.
46
+ */
47
+ fuzzyResultCache = new Map();
48
+ /**
49
+ * Phase 8: Worker pool using workerpool library.
50
+ * Initialized lazily when needed.
51
+ */
52
+ workerPool = null;
53
+ /**
54
+ * Phase 7 Sprint 3: Path to the worker script.
55
+ */
56
+ workerPath;
57
+ /**
58
+ * Phase 8: Whether to use worker pool for parallel processing.
59
+ * Can be disabled for testing or when workers are not available.
60
+ */
61
+ useWorkerPool;
62
+ constructor(storage, options = {}) {
63
+ this.storage = storage;
64
+ this.useWorkerPool = options.useWorkerPool ?? true;
65
+ // Calculate worker path using ESM module resolution
66
+ const currentFileUrl = import.meta.url;
67
+ const currentDir = dirname(fileURLToPath(currentFileUrl));
68
+ // Check if we're running from src/ (during tests) or dist/ (production)
69
+ const isRunningFromSrc = currentDir.includes(`${sep}src${sep}`);
70
+ if (isRunningFromSrc) {
71
+ // During tests, worker is in dist/workers/ relative to project root
72
+ const projectRoot = join(currentDir, '..', '..');
73
+ this.workerPath = join(projectRoot, 'dist', 'workers', 'levenshteinWorker.js');
74
+ }
75
+ else {
76
+ // In production, worker is in dist/workers/ relative to current dist/search/
77
+ this.workerPath = join(currentDir, '..', 'workers', 'levenshteinWorker.js');
78
+ }
79
+ }
80
+ /**
81
+ * Phase 4 Sprint 3: Generate cache key for fuzzy search parameters.
82
+ */
83
+ generateCacheKey(query, threshold, tags, minImportance, maxImportance, offset, limit) {
84
+ return JSON.stringify({
85
+ q: query.toLowerCase(),
86
+ t: threshold,
87
+ tags: tags?.sort().join(',') ?? '',
88
+ min: minImportance,
89
+ max: maxImportance,
90
+ off: offset,
91
+ lim: limit,
92
+ });
93
+ }
94
+ /**
95
+ * Phase 4 Sprint 3: Clear the fuzzy search cache.
96
+ */
97
+ clearCache() {
98
+ this.fuzzyResultCache.clear();
99
+ }
100
+ /**
101
+ * Phase 4 Sprint 3: Invalidate stale cache entries.
102
+ */
103
+ cleanupCache() {
104
+ const now = Date.now();
105
+ const entries = Array.from(this.fuzzyResultCache.entries());
106
+ // Remove expired entries
107
+ for (const [key, entry] of entries) {
108
+ if (now - entry.timestamp > FUZZY_CACHE_TTL_MS) {
109
+ this.fuzzyResultCache.delete(key);
110
+ }
111
+ }
112
+ // If still over limit, remove oldest entries
113
+ if (this.fuzzyResultCache.size > FUZZY_CACHE_MAX_SIZE) {
114
+ const sortedEntries = entries
115
+ .filter(([k]) => this.fuzzyResultCache.has(k))
116
+ .sort((a, b) => a[1].timestamp - b[1].timestamp);
117
+ const toRemove = sortedEntries.slice(0, this.fuzzyResultCache.size - FUZZY_CACHE_MAX_SIZE);
118
+ for (const [key] of toRemove) {
119
+ this.fuzzyResultCache.delete(key);
120
+ }
121
+ }
122
+ }
123
+ /**
124
+ * Fuzzy search for entities with typo tolerance and pagination.
125
+ *
126
+ * Uses Levenshtein distance to calculate similarity between strings.
127
+ * Matches if similarity >= threshold (0.0 to 1.0).
128
+ *
129
+ * Phase 4 Sprint 3: Implements result caching for repeated queries.
130
+ *
131
+ * @param query - Search query
132
+ * @param threshold - Similarity threshold (0.0 to 1.0), default DEFAULT_FUZZY_THRESHOLD
133
+ * @param tags - Optional tags filter
134
+ * @param minImportance - Optional minimum importance
135
+ * @param maxImportance - Optional maximum importance
136
+ * @param offset - Number of results to skip (default: 0)
137
+ * @param limit - Maximum number of results (default: 50, max: 200)
138
+ * @returns Filtered knowledge graph with fuzzy matches and pagination applied
139
+ */
140
+ async fuzzySearch(query, threshold = DEFAULT_FUZZY_THRESHOLD, tags, minImportance, maxImportance, offset = 0, limit = SEARCH_LIMITS.DEFAULT) {
141
+ const graph = await this.storage.loadGraph();
142
+ const queryLower = query.toLowerCase();
143
+ // Phase 4 Sprint 3: Generate cache key and check cache
144
+ const cacheKey = this.generateCacheKey(query, threshold, tags, minImportance, maxImportance, offset, limit);
145
+ const cached = this.fuzzyResultCache.get(cacheKey);
146
+ // Check if cache is valid (entity count hasn't changed)
147
+ if (cached && cached.entityCount === graph.entities.length) {
148
+ const now = Date.now();
149
+ if (now - cached.timestamp < FUZZY_CACHE_TTL_MS) {
150
+ // Return cached results
151
+ const cachedNameSet = new Set(cached.entityNames);
152
+ const cachedEntities = graph.entities.filter(e => cachedNameSet.has(e.name));
153
+ const cachedEntityNames = new Set(cached.entityNames);
154
+ const cachedRelations = graph.relations.filter(r => cachedEntityNames.has(r.from) && cachedEntityNames.has(r.to));
155
+ return { entities: cachedEntities, relations: cachedRelations };
156
+ }
157
+ }
158
+ // Phase 7 Sprint 3: Use worker pool for large graphs with low thresholds
159
+ // Phase 8: Respect useWorkerPool flag for testing
160
+ const shouldUseWorkers = this.useWorkerPool &&
161
+ graph.entities.length >= WORKER_MIN_ENTITIES &&
162
+ threshold < WORKER_MAX_THRESHOLD;
163
+ let fuzzyMatched;
164
+ if (shouldUseWorkers) {
165
+ fuzzyMatched = await this.searchWithWorkers(query, threshold, graph.entities);
166
+ }
167
+ else {
168
+ // Perform single-threaded fuzzy search
169
+ fuzzyMatched = this.performFuzzyMatch(graph.entities, queryLower, threshold);
170
+ }
171
+ // Apply tag and importance filters using SearchFilterChain
172
+ const filters = { tags, minImportance, maxImportance };
173
+ const filteredEntities = SearchFilterChain.applyFilters(fuzzyMatched, filters);
174
+ // Apply pagination using SearchFilterChain
175
+ const pagination = SearchFilterChain.validatePagination(offset, limit);
176
+ const paginatedEntities = SearchFilterChain.paginate(filteredEntities, pagination);
177
+ // Phase 4 Sprint 3: Cache the results
178
+ this.fuzzyResultCache.set(cacheKey, {
179
+ entityNames: paginatedEntities.map(e => e.name),
180
+ entityCount: graph.entities.length,
181
+ timestamp: Date.now(),
182
+ });
183
+ // Cleanup old cache entries periodically
184
+ if (this.fuzzyResultCache.size > FUZZY_CACHE_MAX_SIZE / 2) {
185
+ this.cleanupCache();
186
+ }
187
+ const filteredEntityNames = new Set(paginatedEntities.map(e => e.name));
188
+ const filteredRelations = graph.relations.filter(r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to));
189
+ return {
190
+ entities: paginatedEntities,
191
+ relations: filteredRelations,
192
+ };
193
+ }
194
+ /**
195
+ * Phase 4 Sprint 3: Perform the actual fuzzy matching logic.
196
+ * Extracted from fuzzySearch for cleaner code structure.
197
+ */
198
+ performFuzzyMatch(entities, queryLower, threshold) {
199
+ return entities.filter(e => {
200
+ const lowercased = this.storage.getLowercased(e.name);
201
+ // Check name match (use pre-computed lowercase)
202
+ const nameLower = lowercased?.name ?? e.name.toLowerCase();
203
+ if (this.isFuzzyMatchLower(nameLower, queryLower, threshold))
204
+ return true;
205
+ // Check type match (use pre-computed lowercase)
206
+ const typeLower = lowercased?.entityType ?? e.entityType.toLowerCase();
207
+ if (this.isFuzzyMatchLower(typeLower, queryLower, threshold))
208
+ return true;
209
+ // Check observations (use pre-computed lowercase array)
210
+ const obsLower = lowercased?.observations ?? e.observations.map(o => o.toLowerCase());
211
+ return obsLower.some(o =>
212
+ // For observations, split into words and check each word
213
+ o
214
+ .split(/\s+/)
215
+ .some(word => this.isFuzzyMatchLower(word, queryLower, threshold)) ||
216
+ // Also check if the observation contains the query
217
+ this.isFuzzyMatchLower(o, queryLower, threshold));
218
+ });
219
+ }
220
+ /**
221
+ * Check if two already-lowercase strings match with fuzzy logic.
222
+ *
223
+ * OPTIMIZED: Skips toLowerCase() calls when strings are already lowercase.
224
+ *
225
+ * @param s1 - First string (already lowercase)
226
+ * @param s2 - Second string (already lowercase)
227
+ * @param threshold - Similarity threshold (0.0 to 1.0)
228
+ * @returns True if strings match fuzzily
229
+ */
230
+ isFuzzyMatchLower(s1, s2, threshold = 0.7) {
231
+ // Exact match
232
+ if (s1 === s2)
233
+ return true;
234
+ // One contains the other
235
+ if (s1.includes(s2) || s2.includes(s1))
236
+ return true;
237
+ // Calculate similarity using Levenshtein distance
238
+ const distance = levenshteinDistance(s1, s2);
239
+ const maxLength = Math.max(s1.length, s2.length);
240
+ const similarity = 1 - distance / maxLength;
241
+ return similarity >= threshold;
242
+ }
243
+ /**
244
+ * Phase 8: Perform fuzzy search using workerpool for parallel processing.
245
+ *
246
+ * Splits entities into chunks and processes them in parallel using worker threads.
247
+ * Falls back to single-threaded search if worker execution fails.
248
+ *
249
+ * @param query - Search query
250
+ * @param threshold - Similarity threshold
251
+ * @param entities - Entities to search
252
+ * @returns Array of matched entities
253
+ */
254
+ async searchWithWorkers(query, threshold, entities) {
255
+ try {
256
+ // Initialize worker pool lazily using workerpool
257
+ if (!this.workerPool) {
258
+ // Enable ESM module support for Node.js 20+
259
+ // The 'type: module' option is needed for ESM workers but may not be in @types/node
260
+ const workerThreadOpts = { type: 'module' };
261
+ this.workerPool = workerpool.pool(this.workerPath, {
262
+ maxWorkers: Math.max(1, workerpool.cpus - 1),
263
+ workerType: 'thread',
264
+ workerThreadOpts,
265
+ });
266
+ }
267
+ // Split entities into chunks based on CPU count
268
+ const numWorkers = Math.max(1, workerpool.cpus - 1);
269
+ const chunkSize = Math.ceil(entities.length / numWorkers);
270
+ const chunks = [];
271
+ for (let i = 0; i < entities.length; i += chunkSize) {
272
+ chunks.push(entities.slice(i, i + chunkSize));
273
+ }
274
+ // Prepare worker inputs with lowercased data
275
+ const workerInputs = chunks.map(chunk => ({
276
+ query,
277
+ threshold,
278
+ entities: chunk.map(e => ({
279
+ name: e.name,
280
+ nameLower: e.name.toLowerCase(),
281
+ observations: e.observations.map(o => o.toLowerCase()),
282
+ })),
283
+ }));
284
+ // Execute all chunks in parallel using workerpool with timeout
285
+ const WORKER_TIMEOUT_MS = 30000; // 30 seconds
286
+ const results = await Promise.all(workerInputs.map(input => this.workerPool.exec('searchEntities', [input])
287
+ .timeout(WORKER_TIMEOUT_MS)));
288
+ // Flatten results and extract matched entity names
289
+ const matchedNames = new Set(results.flat().map(r => r.name));
290
+ // Return entities that matched
291
+ return entities.filter(e => matchedNames.has(e.name));
292
+ }
293
+ catch (error) {
294
+ // Worker execution failed - fall back to single-threaded mode
295
+ console.warn(`Worker pool execution failed, falling back to single-threaded fuzzy search: ${error instanceof Error ? error.message : String(error)}`);
296
+ // Use the existing single-threaded implementation
297
+ const queryLower = query.toLowerCase();
298
+ return this.performFuzzyMatch(entities, queryLower, threshold);
299
+ }
300
+ }
301
+ /**
302
+ * Phase 8: Shutdown the worker pool and clean up resources.
303
+ *
304
+ * Should be called when FuzzySearch is no longer needed.
305
+ */
306
+ async shutdown() {
307
+ if (this.workerPool) {
308
+ await this.workerPool.terminate();
309
+ this.workerPool = null;
310
+ }
311
+ }
312
+ }
313
+ //# sourceMappingURL=FuzzySearch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FuzzySearch.js","sourceRoot":"","sources":["../../src/search/FuzzySearch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Hybrid Scorer
3
+ *
4
+ * Combines semantic, lexical, and symbolic search scores with
5
+ * min-max normalization and configurable weights.
6
+ *
7
+ * Phase 12 Sprint 3: Search Algorithm Optimization
8
+ *
9
+ * @module search/HybridScorer
10
+ */
11
+ import type { Entity } from '../types/index.js';
12
+ /**
13
+ * Result from semantic search layer.
14
+ */
15
+ export interface SemanticLayerResult {
16
+ /** Entity name */
17
+ entityName: string;
18
+ /** Similarity score (typically 0-1 for cosine similarity) */
19
+ similarity: number;
20
+ /** The matched entity (if resolved) */
21
+ entity?: Entity;
22
+ }
23
+ /**
24
+ * Result from lexical search layer (TF-IDF or BM25).
25
+ */
26
+ export interface LexicalSearchResult {
27
+ /** Entity name */
28
+ entityName: string;
29
+ /** Relevance score (unbounded, higher is better) */
30
+ score: number;
31
+ /** The matched entity (if resolved) */
32
+ entity?: Entity;
33
+ }
34
+ /**
35
+ * Result from symbolic search layer.
36
+ */
37
+ export interface SymbolicSearchResult {
38
+ /** Entity name */
39
+ entityName: string;
40
+ /** Match score (typically 0-1) */
41
+ score: number;
42
+ /** The matched entity (if resolved) */
43
+ entity?: Entity;
44
+ }
45
+ /**
46
+ * Combined result with scores from all layers.
47
+ */
48
+ export interface ScoredResult {
49
+ /** Entity name */
50
+ entityName: string;
51
+ /** The matched entity */
52
+ entity: Entity;
53
+ /** Individual layer scores (normalized 0-1) */
54
+ scores: {
55
+ semantic: number;
56
+ lexical: number;
57
+ symbolic: number;
58
+ combined: number;
59
+ };
60
+ /** Which layers contributed to this result */
61
+ matchedLayers: ('semantic' | 'lexical' | 'symbolic')[];
62
+ /** Original raw scores before normalization */
63
+ rawScores: {
64
+ semantic?: number;
65
+ lexical?: number;
66
+ symbolic?: number;
67
+ };
68
+ }
69
+ /**
70
+ * Configurable weights for hybrid scoring.
71
+ */
72
+ export interface HybridWeights {
73
+ /** Weight for semantic layer (default: 0.4) */
74
+ semantic: number;
75
+ /** Weight for lexical layer (default: 0.4) */
76
+ lexical: number;
77
+ /** Weight for symbolic layer (default: 0.2) */
78
+ symbolic: number;
79
+ }
80
+ /**
81
+ * Default weights for hybrid search.
82
+ */
83
+ export declare const DEFAULT_SCORER_WEIGHTS: HybridWeights;
84
+ /**
85
+ * Options for the HybridScorer.
86
+ */
87
+ export interface HybridScorerOptions {
88
+ /** Weights for each layer */
89
+ weights?: Partial<HybridWeights>;
90
+ /** Minimum score to include in results (default: 0) */
91
+ minScore?: number;
92
+ /** Whether to normalize weights to sum to 1 (default: true) */
93
+ normalizeWeights?: boolean;
94
+ }
95
+ /**
96
+ * HybridScorer combines multiple search signals using min-max normalization.
97
+ *
98
+ * Features:
99
+ * 1. Min-max normalization brings all scores to 0-1 range
100
+ * 2. Configurable weights for each layer
101
+ * 3. Handles missing layers gracefully (redistributes weights)
102
+ * 4. Tracks which layers contributed to each result
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const scorer = new HybridScorer({
107
+ * weights: { semantic: 0.5, lexical: 0.3, symbolic: 0.2 }
108
+ * });
109
+ *
110
+ * const results = scorer.combine(
111
+ * semanticResults,
112
+ * lexicalResults,
113
+ * symbolicResults,
114
+ * entityMap
115
+ * );
116
+ * ```
117
+ */
118
+ export declare class HybridScorer {
119
+ private weights;
120
+ private minScore;
121
+ private normalizeWeights;
122
+ constructor(options?: HybridScorerOptions);
123
+ /**
124
+ * Get current weights configuration.
125
+ */
126
+ getWeights(): HybridWeights;
127
+ /**
128
+ * Update weights configuration.
129
+ */
130
+ setWeights(weights: Partial<HybridWeights>): void;
131
+ /**
132
+ * Perform min-max normalization on scores.
133
+ *
134
+ * Formula: normalized = (x - min) / (max - min)
135
+ *
136
+ * @param scores - Map of entity name to raw score
137
+ * @returns Map of entity name to normalized score (0-1)
138
+ */
139
+ minMaxNormalize(scores: Map<string, number>): Map<string, number>;
140
+ /**
141
+ * Combine results from all three search layers.
142
+ *
143
+ * @param semanticResults - Results from semantic search
144
+ * @param lexicalResults - Results from lexical search
145
+ * @param symbolicResults - Results from symbolic search
146
+ * @param entityMap - Map of entity names to Entity objects
147
+ * @returns Array of combined results sorted by score
148
+ */
149
+ combine(semanticResults: SemanticLayerResult[], lexicalResults: LexicalSearchResult[], symbolicResults: SymbolicSearchResult[], entityMap: Map<string, Entity>): ScoredResult[];
150
+ /**
151
+ * Get weights normalized to sum to 1, redistributing for missing layers.
152
+ *
153
+ * @param hasSemantic - Whether semantic results are available
154
+ * @param hasLexical - Whether lexical results are available
155
+ * @param hasSymbolic - Whether symbolic results are available
156
+ * @returns Normalized weights
157
+ */
158
+ getNormalizedWeights(hasSemantic: boolean, hasLexical: boolean, hasSymbolic: boolean): HybridWeights;
159
+ /**
160
+ * Combine scores from maps directly (alternative interface).
161
+ *
162
+ * @param semanticScores - Map of entity name to semantic score
163
+ * @param lexicalScores - Map of entity name to lexical score
164
+ * @param symbolicScores - Map of entity name to symbolic score
165
+ * @param entityMap - Map of entity names to Entity objects
166
+ * @returns Array of combined results sorted by score
167
+ */
168
+ combineFromMaps(semanticScores: Map<string, number>, lexicalScores: Map<string, number>, symbolicScores: Map<string, number>, entityMap: Map<string, Entity>): ScoredResult[];
169
+ /**
170
+ * Calculate combined score for a single entity.
171
+ *
172
+ * Useful for scoring individual results without full normalization.
173
+ *
174
+ * @param semanticScore - Normalized semantic score (0-1)
175
+ * @param lexicalScore - Normalized lexical score (0-1)
176
+ * @param symbolicScore - Normalized symbolic score (0-1)
177
+ * @returns Combined weighted score
178
+ */
179
+ calculateScore(semanticScore: number, lexicalScore: number, symbolicScore: number): number;
180
+ }
181
+ //# sourceMappingURL=HybridScorer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HybridScorer.d.ts","sourceRoot":"","sources":["../../src/search/HybridScorer.ts"],"names":[],"mappings":"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"}