@soulcraft/brainy 3.45.0 → 3.47.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.
- package/CHANGELOG.md +191 -1
- package/README.md +23 -0
- package/dist/brainy.d.ts +13 -1
- package/dist/brainy.js +71 -12
- package/dist/hnsw/typeAwareHNSWIndex.d.ts +231 -0
- package/dist/hnsw/typeAwareHNSWIndex.js +439 -0
- package/dist/triple/TripleIntelligenceSystem.d.ts +3 -1
- package/dist/utils/metadataIndex.d.ts +59 -1
- package/dist/utils/metadataIndex.js +223 -2
- package/package.json +1 -1
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-Aware HNSW Index - Phase 2 Billion-Scale Optimization
|
|
3
|
+
*
|
|
4
|
+
* Maintains separate HNSW graphs per entity type for massive memory savings:
|
|
5
|
+
* - Memory @ 1B scale: 384GB → 50GB (-87%)
|
|
6
|
+
* - Query speed: 10x faster for single-type queries
|
|
7
|
+
* - Storage: Already type-first from Phase 1a
|
|
8
|
+
*
|
|
9
|
+
* Architecture:
|
|
10
|
+
* - One HNSWIndex per NounType (31 total)
|
|
11
|
+
* - Lazy initialization (indexes created on first use)
|
|
12
|
+
* - Type routing for optimal performance
|
|
13
|
+
* - Falls back to multi-type search when type unknown
|
|
14
|
+
*/
|
|
15
|
+
import { HNSWIndex } from './hnswIndex.js';
|
|
16
|
+
import { NOUN_TYPE_COUNT, TypeUtils } from '../types/graphTypes.js';
|
|
17
|
+
import { euclideanDistance } from '../utils/index.js';
|
|
18
|
+
import { prodLog } from '../utils/logger.js';
|
|
19
|
+
// Default HNSW parameters (same as HNSWIndex)
|
|
20
|
+
const DEFAULT_CONFIG = {
|
|
21
|
+
M: 16,
|
|
22
|
+
efConstruction: 200,
|
|
23
|
+
efSearch: 50,
|
|
24
|
+
ml: 16
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* TypeAwareHNSWIndex - Separate HNSW graphs per entity type
|
|
28
|
+
*
|
|
29
|
+
* Phase 2 of billion-scale optimization roadmap.
|
|
30
|
+
* Reduces HNSW memory by 87% @ billion scale.
|
|
31
|
+
*/
|
|
32
|
+
export class TypeAwareHNSWIndex {
|
|
33
|
+
/**
|
|
34
|
+
* Create a new TypeAwareHNSWIndex
|
|
35
|
+
*
|
|
36
|
+
* @param config HNSW configuration (M, efConstruction, efSearch, ml)
|
|
37
|
+
* @param distanceFunction Distance function (default: euclidean)
|
|
38
|
+
* @param options Additional options (storage, parallelization)
|
|
39
|
+
*/
|
|
40
|
+
constructor(config = {}, distanceFunction = euclideanDistance, options = {}) {
|
|
41
|
+
// One HNSW index per noun type (lazy initialization)
|
|
42
|
+
this.indexes = new Map();
|
|
43
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
44
|
+
this.distanceFunction = distanceFunction;
|
|
45
|
+
this.storage = options.storage || null;
|
|
46
|
+
this.useParallelization =
|
|
47
|
+
options.useParallelization !== undefined
|
|
48
|
+
? options.useParallelization
|
|
49
|
+
: true;
|
|
50
|
+
prodLog.info('TypeAwareHNSWIndex initialized (Phase 2: Type-Aware HNSW)');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get or create HNSW index for a specific type (lazy initialization)
|
|
54
|
+
*
|
|
55
|
+
* Indexes are created on-demand to save memory.
|
|
56
|
+
* Only types with entities get an index.
|
|
57
|
+
*
|
|
58
|
+
* @param type The noun type
|
|
59
|
+
* @returns HNSWIndex for this type
|
|
60
|
+
*/
|
|
61
|
+
getIndexForType(type) {
|
|
62
|
+
// Validate type is a valid NounType
|
|
63
|
+
const typeIndex = TypeUtils.getNounIndex(type);
|
|
64
|
+
if (typeIndex === undefined || typeIndex === null || typeIndex < 0) {
|
|
65
|
+
throw new Error(`Invalid NounType: ${type}. Must be one of the 31 defined types.`);
|
|
66
|
+
}
|
|
67
|
+
if (!this.indexes.has(type)) {
|
|
68
|
+
prodLog.info(`Creating HNSW index for type: ${type}`);
|
|
69
|
+
const index = new HNSWIndex(this.config, this.distanceFunction, {
|
|
70
|
+
useParallelization: this.useParallelization,
|
|
71
|
+
storage: this.storage || undefined
|
|
72
|
+
});
|
|
73
|
+
this.indexes.set(type, index);
|
|
74
|
+
}
|
|
75
|
+
const index = this.indexes.get(type);
|
|
76
|
+
if (!index) {
|
|
77
|
+
throw new Error(`Unexpected: Index for type ${type} not found after creation`);
|
|
78
|
+
}
|
|
79
|
+
return index;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Add a vector to the type-aware index
|
|
83
|
+
*
|
|
84
|
+
* Routes to the correct type's HNSW graph.
|
|
85
|
+
*
|
|
86
|
+
* @param item Vector document to add
|
|
87
|
+
* @param type The noun type (required for routing)
|
|
88
|
+
* @returns The item ID
|
|
89
|
+
*/
|
|
90
|
+
async addItem(item, type) {
|
|
91
|
+
if (!item || !item.vector) {
|
|
92
|
+
throw new Error('Invalid VectorDocument: item or vector is null/undefined');
|
|
93
|
+
}
|
|
94
|
+
if (!type) {
|
|
95
|
+
throw new Error('Type is required for type-aware indexing');
|
|
96
|
+
}
|
|
97
|
+
const index = this.getIndexForType(type);
|
|
98
|
+
return await index.addItem(item);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Search for nearest neighbors (type-aware)
|
|
102
|
+
*
|
|
103
|
+
* **Single-type search** (fast path):
|
|
104
|
+
* ```typescript
|
|
105
|
+
* await index.search(queryVector, 10, 'person')
|
|
106
|
+
* // Searches only person graph (100M nodes instead of 1B)
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* **Multi-type search**:
|
|
110
|
+
* ```typescript
|
|
111
|
+
* await index.search(queryVector, 10, ['person', 'organization'])
|
|
112
|
+
* // Searches person + organization, merges results
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* **All-types search** (fallback):
|
|
116
|
+
* ```typescript
|
|
117
|
+
* await index.search(queryVector, 10)
|
|
118
|
+
* // Searches all 31 graphs (slower but comprehensive)
|
|
119
|
+
* ```
|
|
120
|
+
*
|
|
121
|
+
* @param queryVector Query vector
|
|
122
|
+
* @param k Number of results
|
|
123
|
+
* @param type Type or types to search (undefined = all types)
|
|
124
|
+
* @param filter Optional filter function
|
|
125
|
+
* @returns Array of [id, distance] tuples sorted by distance
|
|
126
|
+
*/
|
|
127
|
+
async search(queryVector, k = 10, type, filter) {
|
|
128
|
+
// Single-type search (fast path)
|
|
129
|
+
if (type && typeof type === 'string') {
|
|
130
|
+
const index = this.getIndexForType(type);
|
|
131
|
+
return await index.search(queryVector, k, filter);
|
|
132
|
+
}
|
|
133
|
+
// Multi-type search (handle empty array edge case)
|
|
134
|
+
if (type && Array.isArray(type) && type.length > 0) {
|
|
135
|
+
return await this.searchMultipleTypes(queryVector, k, type, filter);
|
|
136
|
+
}
|
|
137
|
+
// All-types search (slowest path + empty array fallback)
|
|
138
|
+
return await this.searchAllTypes(queryVector, k, filter);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Search across multiple specific types
|
|
142
|
+
*
|
|
143
|
+
* @param queryVector Query vector
|
|
144
|
+
* @param k Number of results
|
|
145
|
+
* @param types Array of types to search
|
|
146
|
+
* @param filter Optional filter function
|
|
147
|
+
* @returns Merged and sorted results
|
|
148
|
+
*/
|
|
149
|
+
async searchMultipleTypes(queryVector, k, types, filter) {
|
|
150
|
+
const allResults = [];
|
|
151
|
+
// Search each specified type
|
|
152
|
+
for (const type of types) {
|
|
153
|
+
if (this.indexes.has(type)) {
|
|
154
|
+
const index = this.indexes.get(type);
|
|
155
|
+
const results = await index.search(queryVector, k, filter);
|
|
156
|
+
allResults.push(...results);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Merge and sort by distance
|
|
160
|
+
allResults.sort((a, b) => a[1] - b[1]);
|
|
161
|
+
// Return top k
|
|
162
|
+
return allResults.slice(0, k);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Search across all types (fallback for type-agnostic queries)
|
|
166
|
+
*
|
|
167
|
+
* This is the slowest path, but provides comprehensive results.
|
|
168
|
+
* Used when type cannot be inferred from query.
|
|
169
|
+
*
|
|
170
|
+
* @param queryVector Query vector
|
|
171
|
+
* @param k Number of results
|
|
172
|
+
* @param filter Optional filter function
|
|
173
|
+
* @returns Merged and sorted results from all types
|
|
174
|
+
*/
|
|
175
|
+
async searchAllTypes(queryVector, k, filter) {
|
|
176
|
+
const allResults = [];
|
|
177
|
+
// Search each type's graph
|
|
178
|
+
for (const [type, index] of this.indexes.entries()) {
|
|
179
|
+
const results = await index.search(queryVector, k, filter);
|
|
180
|
+
allResults.push(...results);
|
|
181
|
+
}
|
|
182
|
+
// Merge and sort by distance
|
|
183
|
+
allResults.sort((a, b) => a[1] - b[1]);
|
|
184
|
+
// Return top k
|
|
185
|
+
return allResults.slice(0, k);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Remove an item from the index
|
|
189
|
+
*
|
|
190
|
+
* @param id Item ID to remove
|
|
191
|
+
* @param type The noun type (required for routing)
|
|
192
|
+
* @returns True if item was removed, false if not found
|
|
193
|
+
*/
|
|
194
|
+
async removeItem(id, type) {
|
|
195
|
+
const index = this.indexes.get(type);
|
|
196
|
+
if (!index) {
|
|
197
|
+
return false; // Type has no index (no items ever added)
|
|
198
|
+
}
|
|
199
|
+
return await index.removeItem(id);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get total number of items across all types
|
|
203
|
+
*
|
|
204
|
+
* @returns Total item count
|
|
205
|
+
*/
|
|
206
|
+
size() {
|
|
207
|
+
let total = 0;
|
|
208
|
+
for (const index of this.indexes.values()) {
|
|
209
|
+
total += index.size();
|
|
210
|
+
}
|
|
211
|
+
return total;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get number of items for a specific type
|
|
215
|
+
*
|
|
216
|
+
* @param type The noun type
|
|
217
|
+
* @returns Item count for this type
|
|
218
|
+
*/
|
|
219
|
+
sizeForType(type) {
|
|
220
|
+
const index = this.indexes.get(type);
|
|
221
|
+
return index ? index.size() : 0;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Clear all indexes
|
|
225
|
+
*/
|
|
226
|
+
clear() {
|
|
227
|
+
for (const index of this.indexes.values()) {
|
|
228
|
+
index.clear();
|
|
229
|
+
}
|
|
230
|
+
this.indexes.clear();
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Clear index for a specific type
|
|
234
|
+
*
|
|
235
|
+
* @param type The noun type to clear
|
|
236
|
+
*/
|
|
237
|
+
clearType(type) {
|
|
238
|
+
const index = this.indexes.get(type);
|
|
239
|
+
if (index) {
|
|
240
|
+
index.clear();
|
|
241
|
+
this.indexes.delete(type);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get configuration
|
|
246
|
+
*
|
|
247
|
+
* @returns HNSW configuration
|
|
248
|
+
*/
|
|
249
|
+
getConfig() {
|
|
250
|
+
return { ...this.config };
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get distance function
|
|
254
|
+
*
|
|
255
|
+
* @returns Distance function
|
|
256
|
+
*/
|
|
257
|
+
getDistanceFunction() {
|
|
258
|
+
return this.distanceFunction;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Set parallelization (applies to all indexes)
|
|
262
|
+
*
|
|
263
|
+
* @param useParallelization Whether to use parallelization
|
|
264
|
+
*/
|
|
265
|
+
setUseParallelization(useParallelization) {
|
|
266
|
+
this.useParallelization = useParallelization;
|
|
267
|
+
for (const index of this.indexes.values()) {
|
|
268
|
+
index.setUseParallelization(useParallelization);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get parallelization setting
|
|
273
|
+
*
|
|
274
|
+
* @returns Whether parallelization is enabled
|
|
275
|
+
*/
|
|
276
|
+
getUseParallelization() {
|
|
277
|
+
return this.useParallelization;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Rebuild HNSW indexes from storage (type-aware)
|
|
281
|
+
*
|
|
282
|
+
* CRITICAL: This implementation uses type-filtered pagination to avoid
|
|
283
|
+
* loading ALL entities for each type (which would be 31 billion reads @ 1B scale).
|
|
284
|
+
*
|
|
285
|
+
* Can rebuild all types or specific types.
|
|
286
|
+
* Much faster than rebuilding a monolithic index.
|
|
287
|
+
*
|
|
288
|
+
* @param options Rebuild options
|
|
289
|
+
*/
|
|
290
|
+
async rebuild(options = {}) {
|
|
291
|
+
if (!this.storage) {
|
|
292
|
+
prodLog.warn('TypeAwareHNSW rebuild skipped: no storage adapter');
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
// Determine which types to rebuild
|
|
296
|
+
const typesToRebuild = options.types || this.getAllNounTypes();
|
|
297
|
+
prodLog.info(`Rebuilding ${typesToRebuild.length} type-aware HNSW indexes...`);
|
|
298
|
+
const errors = [];
|
|
299
|
+
// Rebuild each type's index with type-filtered pagination
|
|
300
|
+
for (const type of typesToRebuild) {
|
|
301
|
+
try {
|
|
302
|
+
prodLog.info(`Rebuilding HNSW index for type: ${type}`);
|
|
303
|
+
const index = this.getIndexForType(type);
|
|
304
|
+
index.clear(); // Clear before rebuild
|
|
305
|
+
// Load ONLY entities of this type from storage using pagination
|
|
306
|
+
let cursor = undefined;
|
|
307
|
+
let hasMore = true;
|
|
308
|
+
let loaded = 0;
|
|
309
|
+
while (hasMore) {
|
|
310
|
+
// CRITICAL: Use type filtering to load only this type's entities
|
|
311
|
+
const result = await this.storage.getNounsWithPagination({
|
|
312
|
+
limit: options.batchSize || 1000,
|
|
313
|
+
cursor,
|
|
314
|
+
filter: { nounType: type } // ← TYPE FILTER!
|
|
315
|
+
});
|
|
316
|
+
// Add each entity to this type's index
|
|
317
|
+
for (const noun of result.items) {
|
|
318
|
+
try {
|
|
319
|
+
await index.addItem({
|
|
320
|
+
id: noun.id,
|
|
321
|
+
vector: noun.vector
|
|
322
|
+
});
|
|
323
|
+
loaded++;
|
|
324
|
+
if (options.onProgress) {
|
|
325
|
+
options.onProgress(type, loaded, result.totalCount || loaded);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
prodLog.error(`Failed to add entity ${noun.id} to ${type} index:`, error);
|
|
330
|
+
// Continue with other entities
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
hasMore = result.hasMore;
|
|
334
|
+
cursor = result.nextCursor;
|
|
335
|
+
}
|
|
336
|
+
prodLog.info(`✅ Rebuilt ${type} index: ${index.size().toLocaleString()} entities`);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
prodLog.error(`Failed to rebuild ${type} index:`, error);
|
|
340
|
+
errors.push({ type, error: error });
|
|
341
|
+
// Continue with other types instead of failing completely
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// Report errors at end
|
|
345
|
+
if (errors.length > 0) {
|
|
346
|
+
const failedTypes = errors.map((e) => e.type).join(', ');
|
|
347
|
+
prodLog.warn(`⚠️ Failed to rebuild ${errors.length} type indexes: ${failedTypes}`);
|
|
348
|
+
// Throw if ALL rebuilds failed
|
|
349
|
+
if (errors.length === typesToRebuild.length) {
|
|
350
|
+
throw new Error('All type-aware HNSW rebuilds failed');
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
prodLog.info(`✅ TypeAwareHNSW rebuild complete: ${this.size().toLocaleString()} total entities across ${this.indexes.size} types`);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Get comprehensive statistics
|
|
357
|
+
*
|
|
358
|
+
* Shows memory reduction compared to monolithic approach.
|
|
359
|
+
*
|
|
360
|
+
* @returns Type-aware HNSW statistics
|
|
361
|
+
*/
|
|
362
|
+
getStats() {
|
|
363
|
+
const typeStats = new Map();
|
|
364
|
+
let totalNodes = 0;
|
|
365
|
+
let totalMemoryMB = 0;
|
|
366
|
+
// Collect stats from each type's index
|
|
367
|
+
for (const [type, index] of this.indexes.entries()) {
|
|
368
|
+
const cacheStats = index.getCacheStats();
|
|
369
|
+
const nodeCount = index.size();
|
|
370
|
+
const memoryMB = cacheStats.hnswCache.estimatedMemoryMB;
|
|
371
|
+
typeStats.set(type, {
|
|
372
|
+
nodeCount,
|
|
373
|
+
memoryMB,
|
|
374
|
+
maxLevel: index.getMaxLevel(),
|
|
375
|
+
entryPointId: index.getEntryPointId()
|
|
376
|
+
});
|
|
377
|
+
totalNodes += nodeCount;
|
|
378
|
+
totalMemoryMB += memoryMB;
|
|
379
|
+
}
|
|
380
|
+
// Estimate monolithic memory (for comparison)
|
|
381
|
+
// Monolithic would use ~384 bytes per entity @ 1B scale
|
|
382
|
+
const estimatedMonolithicMemoryMB = (totalNodes * 384) / (1024 * 1024);
|
|
383
|
+
// Calculate memory reduction
|
|
384
|
+
const memoryReductionPercent = estimatedMonolithicMemoryMB > 0
|
|
385
|
+
? ((estimatedMonolithicMemoryMB - totalMemoryMB) /
|
|
386
|
+
estimatedMonolithicMemoryMB) *
|
|
387
|
+
100
|
|
388
|
+
: 0;
|
|
389
|
+
return {
|
|
390
|
+
totalNodes,
|
|
391
|
+
totalMemoryMB: parseFloat(totalMemoryMB.toFixed(2)),
|
|
392
|
+
typeCount: this.indexes.size,
|
|
393
|
+
typeStats,
|
|
394
|
+
memoryReductionPercent: parseFloat(memoryReductionPercent.toFixed(2)),
|
|
395
|
+
estimatedMonolithicMemoryMB: parseFloat(estimatedMonolithicMemoryMB.toFixed(2))
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Get statistics for a specific type
|
|
400
|
+
*
|
|
401
|
+
* @param type The noun type
|
|
402
|
+
* @returns Statistics for this type's index (null if no index)
|
|
403
|
+
*/
|
|
404
|
+
getStatsForType(type) {
|
|
405
|
+
const index = this.indexes.get(type);
|
|
406
|
+
if (!index) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const cacheStats = index.getCacheStats();
|
|
410
|
+
return {
|
|
411
|
+
nodeCount: index.size(),
|
|
412
|
+
memoryMB: cacheStats.hnswCache.estimatedMemoryMB,
|
|
413
|
+
maxLevel: index.getMaxLevel(),
|
|
414
|
+
entryPointId: index.getEntryPointId(),
|
|
415
|
+
cacheStats
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Get all noun types (for iteration)
|
|
420
|
+
*
|
|
421
|
+
* @returns Array of all noun types
|
|
422
|
+
*/
|
|
423
|
+
getAllNounTypes() {
|
|
424
|
+
const types = [];
|
|
425
|
+
for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
|
|
426
|
+
types.push(TypeUtils.getNounFromIndex(i));
|
|
427
|
+
}
|
|
428
|
+
return types;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Get list of types that have indexes (have entities)
|
|
432
|
+
*
|
|
433
|
+
* @returns Array of types with indexes
|
|
434
|
+
*/
|
|
435
|
+
getActiveTypes() {
|
|
436
|
+
return Array.from(this.indexes.keys());
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=typeAwareHNSWIndex.js.map
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* - Fusion: O(k log k) where k = result count
|
|
14
14
|
*/
|
|
15
15
|
import { HNSWIndex } from '../hnsw/hnswIndex.js';
|
|
16
|
+
import { HNSWIndexOptimized } from '../hnsw/hnswIndexOptimized.js';
|
|
17
|
+
import { TypeAwareHNSWIndex } from '../hnsw/typeAwareHNSWIndex.js';
|
|
16
18
|
import { MetadataIndexManager } from '../utils/metadataIndex.js';
|
|
17
19
|
import { Vector } from '../coreTypes.js';
|
|
18
20
|
export interface TripleQuery {
|
|
@@ -64,7 +66,7 @@ export declare class TripleIntelligenceSystem {
|
|
|
64
66
|
private planner;
|
|
65
67
|
private embedder;
|
|
66
68
|
private storage;
|
|
67
|
-
constructor(metadataIndex: MetadataIndexManager, hnswIndex: HNSWIndex, graphIndex: GraphAdjacencyIndex, embedder: (text: string) => Promise<Vector>, storage: any);
|
|
69
|
+
constructor(metadataIndex: MetadataIndexManager, hnswIndex: HNSWIndex | HNSWIndexOptimized | TypeAwareHNSWIndex, graphIndex: GraphAdjacencyIndex, embedder: (text: string) => Promise<Vector>, storage: any);
|
|
68
70
|
/**
|
|
69
71
|
* Main find method - executes Triple Intelligence queries
|
|
70
72
|
*/
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Automatically updates indexes when data changes
|
|
5
5
|
*/
|
|
6
6
|
import { StorageAdapter } from '../coreTypes.js';
|
|
7
|
-
import { NounType } from '../types/graphTypes.js';
|
|
7
|
+
import { NounType, VerbType } from '../types/graphTypes.js';
|
|
8
8
|
export interface MetadataIndexEntry {
|
|
9
9
|
field: string;
|
|
10
10
|
value: string | number | boolean;
|
|
@@ -66,6 +66,8 @@ export declare class MetadataIndexManager {
|
|
|
66
66
|
private readonly FLOAT_PRECISION;
|
|
67
67
|
private typeFieldAffinity;
|
|
68
68
|
private totalEntitiesByType;
|
|
69
|
+
private entityCountsByTypeFixed;
|
|
70
|
+
private verbCountsByTypeFixed;
|
|
69
71
|
private unifiedCache;
|
|
70
72
|
private activeLocks;
|
|
71
73
|
private lockPromises;
|
|
@@ -85,6 +87,14 @@ export declare class MetadataIndexManager {
|
|
|
85
87
|
* Target: >80% cache hit rate for typical workloads
|
|
86
88
|
*/
|
|
87
89
|
warmCache(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Phase 1b: Warm cache for top types (type-aware optimization)
|
|
92
|
+
* Preloads metadata indices for the most common entity types and their top fields
|
|
93
|
+
* This significantly improves query performance for the most frequently accessed data
|
|
94
|
+
*
|
|
95
|
+
* @param topN Number of top types to warm (default: 3)
|
|
96
|
+
*/
|
|
97
|
+
warmCacheForTopTypes(topN?: number): Promise<void>;
|
|
88
98
|
/**
|
|
89
99
|
* Acquire an in-memory lock for coordinating concurrent metadata index writes
|
|
90
100
|
* Uses in-memory locks since MetadataIndexManager doesn't have direct file system access
|
|
@@ -105,6 +115,17 @@ export declare class MetadataIndexManager {
|
|
|
105
115
|
* This avoids rebuilding the entire index on startup
|
|
106
116
|
*/
|
|
107
117
|
private lazyLoadCounts;
|
|
118
|
+
/**
|
|
119
|
+
* Phase 1b: Sync Map-based counts to fixed-size Uint32Arrays
|
|
120
|
+
* This enables gradual migration from Maps to arrays while maintaining backward compatibility
|
|
121
|
+
* Called periodically and on demand to keep both representations in sync
|
|
122
|
+
*/
|
|
123
|
+
private syncTypeCountsToFixed;
|
|
124
|
+
/**
|
|
125
|
+
* Phase 1b: Sync from fixed-size arrays back to Maps (reverse direction)
|
|
126
|
+
* Used when Uint32Arrays are the source of truth and need to update Maps
|
|
127
|
+
*/
|
|
128
|
+
private syncTypeCountsFromFixed;
|
|
108
129
|
/**
|
|
109
130
|
* Update cardinality statistics for a field
|
|
110
131
|
*/
|
|
@@ -279,6 +300,43 @@ export declare class MetadataIndexManager {
|
|
|
279
300
|
* Get all entity types and their counts - O(1) operation
|
|
280
301
|
*/
|
|
281
302
|
getAllEntityCounts(): Map<string, number>;
|
|
303
|
+
/**
|
|
304
|
+
* Get entity count for a noun type using type enum (O(1) array access)
|
|
305
|
+
* More efficient than Map-based getEntityCountByType
|
|
306
|
+
* @param type Noun type from NounTypeEnum
|
|
307
|
+
* @returns Count of entities of this type
|
|
308
|
+
*/
|
|
309
|
+
getEntityCountByTypeEnum(type: NounType): number;
|
|
310
|
+
/**
|
|
311
|
+
* Get verb count for a verb type using type enum (O(1) array access)
|
|
312
|
+
* @param type Verb type from VerbTypeEnum
|
|
313
|
+
* @returns Count of verbs of this type
|
|
314
|
+
*/
|
|
315
|
+
getVerbCountByTypeEnum(type: VerbType): number;
|
|
316
|
+
/**
|
|
317
|
+
* Get top N noun types by entity count (using fixed-size arrays)
|
|
318
|
+
* Useful for type-aware cache warming and query optimization
|
|
319
|
+
* @param n Number of top types to return
|
|
320
|
+
* @returns Array of noun types sorted by count (highest first)
|
|
321
|
+
*/
|
|
322
|
+
getTopNounTypes(n: number): NounType[];
|
|
323
|
+
/**
|
|
324
|
+
* Get top N verb types by count (using fixed-size arrays)
|
|
325
|
+
* @param n Number of top types to return
|
|
326
|
+
* @returns Array of verb types sorted by count (highest first)
|
|
327
|
+
*/
|
|
328
|
+
getTopVerbTypes(n: number): VerbType[];
|
|
329
|
+
/**
|
|
330
|
+
* Get all noun type counts as a Map (using fixed-size arrays)
|
|
331
|
+
* More efficient than getAllEntityCounts for type-aware queries
|
|
332
|
+
* @returns Map of noun type to count
|
|
333
|
+
*/
|
|
334
|
+
getAllNounTypeCounts(): Map<NounType, number>;
|
|
335
|
+
/**
|
|
336
|
+
* Get all verb type counts as a Map (using fixed-size arrays)
|
|
337
|
+
* @returns Map of verb type to count
|
|
338
|
+
*/
|
|
339
|
+
getAllVerbTypeCounts(): Map<VerbType, number>;
|
|
282
340
|
/**
|
|
283
341
|
* Get count of entities matching field-value criteria - queries chunked sparse index
|
|
284
342
|
*/
|