@soulcraft/brainy 3.0.0 → 3.0.1
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 +53 -3
- package/README.md +353 -110
- package/bin/brainy.js +340 -62
- package/dist/api/ConfigAPI.d.ts +67 -0
- package/dist/api/ConfigAPI.js +166 -0
- package/dist/api/DataAPI.d.ts +123 -0
- package/dist/api/DataAPI.js +391 -0
- package/dist/api/SecurityAPI.d.ts +50 -0
- package/dist/api/SecurityAPI.js +139 -0
- package/dist/api/UniversalImportAPI.d.ts +134 -0
- package/dist/api/UniversalImportAPI.js +615 -0
- package/dist/augmentationManager.js +12 -7
- package/dist/augmentationPipeline.d.ts +0 -61
- package/dist/augmentationPipeline.js +0 -87
- package/dist/augmentationRegistry.d.ts +1 -1
- package/dist/augmentationRegistry.js +1 -1
- package/dist/augmentations/apiServerAugmentation.d.ts +27 -1
- package/dist/augmentations/apiServerAugmentation.js +290 -9
- package/dist/augmentations/auditLogAugmentation.d.ts +109 -0
- package/dist/augmentations/auditLogAugmentation.js +358 -0
- package/dist/augmentations/batchProcessingAugmentation.d.ts +3 -2
- package/dist/augmentations/batchProcessingAugmentation.js +123 -22
- package/dist/augmentations/brainyAugmentation.d.ts +142 -8
- package/dist/augmentations/brainyAugmentation.js +179 -2
- package/dist/augmentations/cacheAugmentation.d.ts +8 -5
- package/dist/augmentations/cacheAugmentation.js +116 -17
- package/dist/augmentations/conduitAugmentations.d.ts +2 -2
- package/dist/augmentations/conduitAugmentations.js +2 -2
- package/dist/augmentations/configResolver.d.ts +122 -0
- package/dist/augmentations/configResolver.js +440 -0
- package/dist/augmentations/connectionPoolAugmentation.d.ts +3 -1
- package/dist/augmentations/connectionPoolAugmentation.js +37 -12
- package/dist/augmentations/defaultAugmentations.d.ts +14 -10
- package/dist/augmentations/defaultAugmentations.js +16 -11
- package/dist/augmentations/discovery/catalogDiscovery.d.ts +142 -0
- package/dist/augmentations/discovery/catalogDiscovery.js +249 -0
- package/dist/augmentations/discovery/localDiscovery.d.ts +84 -0
- package/dist/augmentations/discovery/localDiscovery.js +246 -0
- package/dist/augmentations/discovery/runtimeLoader.d.ts +97 -0
- package/dist/augmentations/discovery/runtimeLoader.js +337 -0
- package/dist/augmentations/discovery.d.ts +152 -0
- package/dist/augmentations/discovery.js +441 -0
- package/dist/augmentations/display/cache.d.ts +130 -0
- package/dist/augmentations/display/cache.js +319 -0
- package/dist/augmentations/display/fieldPatterns.d.ts +52 -0
- package/dist/augmentations/display/fieldPatterns.js +393 -0
- package/dist/augmentations/display/iconMappings.d.ts +57 -0
- package/dist/augmentations/display/iconMappings.js +68 -0
- package/dist/augmentations/display/intelligentComputation.d.ts +109 -0
- package/dist/augmentations/display/intelligentComputation.js +462 -0
- package/dist/augmentations/display/types.d.ts +203 -0
- package/dist/augmentations/display/types.js +7 -0
- package/dist/augmentations/entityRegistryAugmentation.d.ts +3 -1
- package/dist/augmentations/entityRegistryAugmentation.js +5 -1
- package/dist/augmentations/indexAugmentation.d.ts +5 -3
- package/dist/augmentations/indexAugmentation.js +5 -2
- package/dist/augmentations/intelligentVerbScoringAugmentation.d.ts +24 -7
- package/dist/augmentations/intelligentVerbScoringAugmentation.js +111 -27
- package/dist/augmentations/manifest.d.ts +176 -0
- package/dist/augmentations/manifest.js +8 -0
- package/dist/augmentations/marketplace/AugmentationMarketplace.d.ts +168 -0
- package/dist/augmentations/marketplace/AugmentationMarketplace.js +329 -0
- package/dist/augmentations/marketplace/cli.d.ts +47 -0
- package/dist/augmentations/marketplace/cli.js +265 -0
- package/dist/augmentations/metricsAugmentation.d.ts +3 -3
- package/dist/augmentations/metricsAugmentation.js +2 -2
- package/dist/augmentations/monitoringAugmentation.d.ts +3 -3
- package/dist/augmentations/monitoringAugmentation.js +2 -2
- package/dist/augmentations/neuralImport.d.ts +1 -1
- package/dist/augmentations/neuralImport.js +4 -4
- package/dist/augmentations/rateLimitAugmentation.d.ts +82 -0
- package/dist/augmentations/rateLimitAugmentation.js +321 -0
- package/dist/augmentations/requestDeduplicatorAugmentation.d.ts +2 -2
- package/dist/augmentations/requestDeduplicatorAugmentation.js +1 -1
- package/dist/augmentations/storageAugmentation.d.ts +1 -1
- package/dist/augmentations/storageAugmentation.js +2 -2
- package/dist/augmentations/storageAugmentations.d.ts +37 -8
- package/dist/augmentations/storageAugmentations.js +204 -15
- package/dist/augmentations/synapseAugmentation.d.ts +1 -1
- package/dist/augmentations/synapseAugmentation.js +35 -16
- package/dist/augmentations/typeMatching/brainyTypes.d.ts +83 -0
- package/dist/augmentations/typeMatching/brainyTypes.js +425 -0
- package/dist/augmentations/typeMatching/intelligentTypeMatcher.d.ts +39 -59
- package/dist/augmentations/typeMatching/intelligentTypeMatcher.js +103 -389
- package/dist/augmentations/universalDisplayAugmentation.d.ts +191 -0
- package/dist/augmentations/universalDisplayAugmentation.js +371 -0
- package/dist/brainy-unified.d.ts +106 -0
- package/dist/brainy-unified.js +327 -0
- package/dist/brainy.d.ts +273 -0
- package/dist/brainy.js +1181 -0
- package/dist/brainyData.d.ts +56 -111
- package/dist/brainyData.js +912 -756
- package/dist/brainyDataV3.d.ts +186 -0
- package/dist/brainyDataV3.js +337 -0
- package/dist/browserFramework.d.ts +6 -6
- package/dist/browserFramework.js +11 -8
- package/dist/browserFramework.minimal.d.ts +5 -5
- package/dist/browserFramework.minimal.js +11 -8
- package/dist/config/distributedPresets-new.d.ts +118 -0
- package/dist/config/distributedPresets-new.js +318 -0
- package/dist/config/distributedPresets.d.ts +118 -0
- package/dist/config/distributedPresets.js +318 -0
- package/dist/config/extensibleConfig.d.ts +99 -0
- package/dist/config/extensibleConfig.js +268 -0
- package/dist/config/index.d.ts +17 -0
- package/dist/config/index.js +35 -0
- package/dist/config/modelAutoConfig.d.ts +32 -0
- package/dist/config/modelAutoConfig.js +139 -0
- package/dist/config/modelPrecisionManager.d.ts +42 -0
- package/dist/config/modelPrecisionManager.js +98 -0
- package/dist/config/sharedConfigManager.d.ts +67 -0
- package/dist/config/sharedConfigManager.js +215 -0
- package/dist/config/storageAutoConfig.d.ts +41 -0
- package/dist/config/storageAutoConfig.js +328 -0
- package/dist/config/zeroConfig.d.ts +68 -0
- package/dist/config/zeroConfig.js +301 -0
- package/dist/cortex/backupRestore.d.ts +2 -2
- package/dist/cortex/backupRestore.js +85 -27
- package/dist/cortex/healthCheck.d.ts +2 -2
- package/dist/cortex/neuralImport.d.ts +2 -2
- package/dist/cortex/neuralImport.js +18 -13
- package/dist/cortex/performanceMonitor.d.ts +2 -2
- package/dist/critical/model-guardian.d.ts +4 -0
- package/dist/critical/model-guardian.js +31 -11
- package/dist/demo.d.ts +4 -4
- package/dist/demo.js +7 -7
- package/dist/distributed/cacheSync.d.ts +112 -0
- package/dist/distributed/cacheSync.js +265 -0
- package/dist/distributed/coordinator.d.ts +193 -0
- package/dist/distributed/coordinator.js +548 -0
- package/dist/distributed/httpTransport.d.ts +120 -0
- package/dist/distributed/httpTransport.js +446 -0
- package/dist/distributed/index.d.ts +8 -0
- package/dist/distributed/index.js +5 -0
- package/dist/distributed/networkTransport.d.ts +132 -0
- package/dist/distributed/networkTransport.js +633 -0
- package/dist/distributed/queryPlanner.d.ts +104 -0
- package/dist/distributed/queryPlanner.js +327 -0
- package/dist/distributed/readWriteSeparation.d.ts +134 -0
- package/dist/distributed/readWriteSeparation.js +350 -0
- package/dist/distributed/shardManager.d.ts +114 -0
- package/dist/distributed/shardManager.js +357 -0
- package/dist/distributed/shardMigration.d.ts +110 -0
- package/dist/distributed/shardMigration.js +289 -0
- package/dist/distributed/storageDiscovery.d.ts +160 -0
- package/dist/distributed/storageDiscovery.js +551 -0
- package/dist/embeddings/CachedEmbeddings.d.ts +40 -0
- package/dist/embeddings/CachedEmbeddings.js +146 -0
- package/dist/embeddings/EmbeddingManager.d.ts +102 -0
- package/dist/embeddings/EmbeddingManager.js +291 -0
- package/dist/embeddings/SingletonModelManager.d.ts +95 -0
- package/dist/embeddings/SingletonModelManager.js +220 -0
- package/dist/embeddings/index.d.ts +12 -0
- package/dist/embeddings/index.js +16 -0
- package/dist/embeddings/lightweight-embedder.d.ts +0 -1
- package/dist/embeddings/lightweight-embedder.js +4 -12
- package/dist/embeddings/model-manager.d.ts +11 -0
- package/dist/embeddings/model-manager.js +43 -7
- package/dist/embeddings/universal-memory-manager.d.ts +1 -1
- package/dist/embeddings/universal-memory-manager.js +27 -67
- package/dist/embeddings/worker-embedding.js +4 -8
- package/dist/errors/brainyError.d.ts +5 -1
- package/dist/errors/brainyError.js +12 -0
- package/dist/examples/basicUsage.js +7 -4
- package/dist/graph/graphAdjacencyIndex.d.ts +96 -0
- package/dist/graph/graphAdjacencyIndex.js +288 -0
- package/dist/graph/pathfinding.js +4 -2
- package/dist/hnsw/scaledHNSWSystem.js +11 -2
- package/dist/importManager.js +8 -5
- package/dist/index.d.ts +17 -22
- package/dist/index.js +37 -23
- package/dist/mcp/brainyMCPAdapter.d.ts +4 -4
- package/dist/mcp/brainyMCPAdapter.js +5 -5
- package/dist/mcp/brainyMCPService.d.ts +3 -3
- package/dist/mcp/brainyMCPService.js +3 -11
- package/dist/mcp/mcpAugmentationToolset.js +20 -30
- package/dist/neural/embeddedPatterns.d.ts +1 -1
- package/dist/neural/embeddedPatterns.js +2 -2
- package/dist/neural/entityExtractor.d.ts +65 -0
- package/dist/neural/entityExtractor.js +316 -0
- package/dist/neural/improvedNeuralAPI.d.ts +357 -0
- package/dist/neural/improvedNeuralAPI.js +2628 -0
- package/dist/neural/naturalLanguageProcessor.d.ts +155 -10
- package/dist/neural/naturalLanguageProcessor.js +941 -66
- package/dist/neural/naturalLanguageProcessorStatic.d.ts +2 -2
- package/dist/neural/naturalLanguageProcessorStatic.js +3 -3
- package/dist/neural/neuralAPI.js +8 -2
- package/dist/neural/patternLibrary.d.ts +57 -3
- package/dist/neural/patternLibrary.js +348 -13
- package/dist/neural/staticPatternMatcher.d.ts +2 -2
- package/dist/neural/staticPatternMatcher.js +2 -2
- package/dist/neural/types.d.ts +287 -0
- package/dist/neural/types.js +24 -0
- package/dist/shared/default-augmentations.d.ts +3 -3
- package/dist/shared/default-augmentations.js +5 -5
- package/dist/storage/adapters/baseStorageAdapter.d.ts +42 -0
- package/dist/storage/adapters/fileSystemStorage.d.ts +26 -2
- package/dist/storage/adapters/fileSystemStorage.js +218 -15
- package/dist/storage/adapters/memoryStorage.d.ts +4 -4
- package/dist/storage/adapters/memoryStorage.js +17 -12
- package/dist/storage/adapters/opfsStorage.d.ts +2 -2
- package/dist/storage/adapters/opfsStorage.js +2 -2
- package/dist/storage/adapters/s3CompatibleStorage.d.ts +2 -2
- package/dist/storage/adapters/s3CompatibleStorage.js +2 -2
- package/dist/storage/backwardCompatibility.d.ts +10 -78
- package/dist/storage/backwardCompatibility.js +17 -132
- package/dist/storage/baseStorage.d.ts +18 -2
- package/dist/storage/baseStorage.js +74 -3
- package/dist/storage/cacheManager.js +2 -2
- package/dist/storage/readOnlyOptimizations.js +8 -3
- package/dist/streaming/pipeline.d.ts +154 -0
- package/dist/streaming/pipeline.js +551 -0
- package/dist/triple/TripleIntelligence.d.ts +25 -110
- package/dist/triple/TripleIntelligence.js +4 -574
- package/dist/triple/TripleIntelligenceSystem.d.ts +159 -0
- package/dist/triple/TripleIntelligenceSystem.js +519 -0
- package/dist/types/apiTypes.d.ts +278 -0
- package/dist/types/apiTypes.js +33 -0
- package/dist/types/brainy.types.d.ts +308 -0
- package/dist/types/brainy.types.js +8 -0
- package/dist/types/brainyDataInterface.d.ts +5 -8
- package/dist/types/brainyDataInterface.js +2 -2
- package/dist/types/graphTypes.js +2 -2
- package/dist/utils/brainyTypes.d.ts +217 -0
- package/dist/utils/brainyTypes.js +261 -0
- package/dist/utils/cacheAutoConfig.d.ts +3 -3
- package/dist/utils/embedding.d.ts +9 -4
- package/dist/utils/embedding.js +89 -26
- package/dist/utils/enhancedLogger.d.ts +104 -0
- package/dist/utils/enhancedLogger.js +232 -0
- package/dist/utils/hybridModelManager.d.ts +19 -28
- package/dist/utils/hybridModelManager.js +36 -200
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/intelligentTypeMapper.d.ts +60 -0
- package/dist/utils/intelligentTypeMapper.js +349 -0
- package/dist/utils/metadataIndex.d.ts +118 -1
- package/dist/utils/metadataIndex.js +539 -16
- package/dist/utils/nodeVersionCheck.d.ts +24 -0
- package/dist/utils/nodeVersionCheck.js +65 -0
- package/dist/utils/paramValidation.d.ts +39 -0
- package/dist/utils/paramValidation.js +192 -0
- package/dist/utils/rateLimiter.d.ts +160 -0
- package/dist/utils/rateLimiter.js +271 -0
- package/dist/utils/statistics.d.ts +4 -4
- package/dist/utils/statistics.js +3 -3
- package/dist/utils/structuredLogger.d.ts +146 -0
- package/dist/utils/structuredLogger.js +394 -0
- package/dist/utils/textEncoding.js +2 -1
- package/dist/utils/typeValidation.d.ts +59 -0
- package/dist/utils/typeValidation.js +374 -0
- package/dist/utils/version.js +19 -3
- package/package.json +15 -4
- package/scripts/download-models.cjs +94 -20
- package/dist/augmentations/walAugmentation.d.ts +0 -109
- package/dist/augmentations/walAugmentation.js +0 -516
- package/dist/chat/BrainyChat.d.ts +0 -121
- package/dist/chat/BrainyChat.js +0 -396
- package/dist/chat/ChatCLI.d.ts +0 -61
- package/dist/chat/ChatCLI.js +0 -351
|
@@ -18,6 +18,17 @@ export class MetadataIndexManager {
|
|
|
18
18
|
// Sorted indices for range queries (only for numeric/date fields)
|
|
19
19
|
this.sortedIndices = new Map();
|
|
20
20
|
this.numericFields = new Set(); // Track which fields are numeric
|
|
21
|
+
// Cardinality and field statistics tracking
|
|
22
|
+
this.fieldStats = new Map();
|
|
23
|
+
this.cardinalityUpdateInterval = 100; // Update cardinality every N operations
|
|
24
|
+
this.operationCount = 0;
|
|
25
|
+
// Smart normalization thresholds
|
|
26
|
+
this.HIGH_CARDINALITY_THRESHOLD = 1000;
|
|
27
|
+
this.TIMESTAMP_PRECISION_MS = 60000; // 1 minute buckets
|
|
28
|
+
this.FLOAT_PRECISION = 2; // decimal places
|
|
29
|
+
// Type-Field Affinity Tracking for intelligent NLP
|
|
30
|
+
this.typeFieldAffinity = new Map(); // nounType -> field -> count
|
|
31
|
+
this.totalEntitiesByType = new Map(); // nounType -> total count
|
|
21
32
|
this.storage = storage;
|
|
22
33
|
this.config = {
|
|
23
34
|
maxIndexSize: config.maxIndexSize ?? 10000,
|
|
@@ -53,10 +64,10 @@ export class MetadataIndexManager {
|
|
|
53
64
|
this.sortedIndices.set(field, loaded);
|
|
54
65
|
}
|
|
55
66
|
else {
|
|
56
|
-
// Create new sorted index
|
|
67
|
+
// Create new sorted index - NOT dirty since we maintain incrementally
|
|
57
68
|
this.sortedIndices.set(field, {
|
|
58
69
|
values: [],
|
|
59
|
-
isDirty:
|
|
70
|
+
isDirty: false, // Clean by default with incremental updates
|
|
60
71
|
fieldType: 'mixed'
|
|
61
72
|
});
|
|
62
73
|
}
|
|
@@ -108,6 +119,107 @@ export class MetadataIndexManager {
|
|
|
108
119
|
sortedIndex.values = sorted;
|
|
109
120
|
sortedIndex.isDirty = false;
|
|
110
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Detect field type from value
|
|
124
|
+
*/
|
|
125
|
+
detectFieldType(value) {
|
|
126
|
+
if (typeof value === 'number' && !isNaN(value))
|
|
127
|
+
return 'number';
|
|
128
|
+
if (value instanceof Date)
|
|
129
|
+
return 'date';
|
|
130
|
+
return 'string';
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Compare two values based on field type for sorting
|
|
134
|
+
*/
|
|
135
|
+
compareValues(a, b, fieldType) {
|
|
136
|
+
switch (fieldType) {
|
|
137
|
+
case 'number':
|
|
138
|
+
return a - b;
|
|
139
|
+
case 'date':
|
|
140
|
+
return a.getTime() - b.getTime();
|
|
141
|
+
case 'string':
|
|
142
|
+
default:
|
|
143
|
+
const aStr = String(a);
|
|
144
|
+
const bStr = String(b);
|
|
145
|
+
return aStr < bStr ? -1 : aStr > bStr ? 1 : 0;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Binary search to find insertion position for a value
|
|
150
|
+
* Returns the index where the value should be inserted to maintain sorted order
|
|
151
|
+
*/
|
|
152
|
+
findInsertPosition(sortedArray, value, fieldType) {
|
|
153
|
+
if (sortedArray.length === 0)
|
|
154
|
+
return 0;
|
|
155
|
+
let left = 0;
|
|
156
|
+
let right = sortedArray.length - 1;
|
|
157
|
+
while (left <= right) {
|
|
158
|
+
const mid = Math.floor((left + right) / 2);
|
|
159
|
+
const midVal = sortedArray[mid][0];
|
|
160
|
+
const comparison = this.compareValues(midVal, value, fieldType);
|
|
161
|
+
if (comparison < 0) {
|
|
162
|
+
left = mid + 1;
|
|
163
|
+
}
|
|
164
|
+
else if (comparison > 0) {
|
|
165
|
+
right = mid - 1;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
return mid; // Value already exists at this position
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return left; // Insert position
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Incrementally update sorted index when adding an ID
|
|
175
|
+
*/
|
|
176
|
+
updateSortedIndexAdd(field, value, id) {
|
|
177
|
+
// Ensure sorted index exists
|
|
178
|
+
if (!this.sortedIndices.has(field)) {
|
|
179
|
+
this.sortedIndices.set(field, {
|
|
180
|
+
values: [],
|
|
181
|
+
isDirty: false,
|
|
182
|
+
fieldType: this.detectFieldType(value)
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const sortedIndex = this.sortedIndices.get(field);
|
|
186
|
+
const normalizedValue = this.normalizeValue(value);
|
|
187
|
+
// Find where this value should be in the sorted array
|
|
188
|
+
const insertPos = this.findInsertPosition(sortedIndex.values, normalizedValue, sortedIndex.fieldType);
|
|
189
|
+
if (insertPos < sortedIndex.values.length &&
|
|
190
|
+
sortedIndex.values[insertPos][0] === normalizedValue) {
|
|
191
|
+
// Value already exists, just add the ID to the existing Set
|
|
192
|
+
sortedIndex.values[insertPos][1].add(id);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// New value, insert at the correct position
|
|
196
|
+
sortedIndex.values.splice(insertPos, 0, [normalizedValue, new Set([id])]);
|
|
197
|
+
}
|
|
198
|
+
// Mark as clean since we're maintaining it incrementally
|
|
199
|
+
sortedIndex.isDirty = false;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Incrementally update sorted index when removing an ID
|
|
203
|
+
*/
|
|
204
|
+
updateSortedIndexRemove(field, value, id) {
|
|
205
|
+
const sortedIndex = this.sortedIndices.get(field);
|
|
206
|
+
if (!sortedIndex || sortedIndex.values.length === 0)
|
|
207
|
+
return;
|
|
208
|
+
const normalizedValue = this.normalizeValue(value);
|
|
209
|
+
// Binary search to find the value
|
|
210
|
+
const pos = this.findInsertPosition(sortedIndex.values, normalizedValue, sortedIndex.fieldType);
|
|
211
|
+
if (pos < sortedIndex.values.length &&
|
|
212
|
+
sortedIndex.values[pos][0] === normalizedValue) {
|
|
213
|
+
// Remove the ID from the Set
|
|
214
|
+
sortedIndex.values[pos][1].delete(id);
|
|
215
|
+
// If no IDs left for this value, remove the entire entry
|
|
216
|
+
if (sortedIndex.values[pos][1].size === 0) {
|
|
217
|
+
sortedIndex.values.splice(pos, 1);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Keep it clean
|
|
221
|
+
sortedIndex.isDirty = false;
|
|
222
|
+
}
|
|
111
223
|
/**
|
|
112
224
|
* Binary search for range start (inclusive or exclusive)
|
|
113
225
|
*/
|
|
@@ -148,14 +260,126 @@ export class MetadataIndexManager {
|
|
|
148
260
|
}
|
|
149
261
|
return result;
|
|
150
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Update cardinality statistics for a field
|
|
265
|
+
*/
|
|
266
|
+
updateCardinalityStats(field, value, operation) {
|
|
267
|
+
// Initialize field stats if needed
|
|
268
|
+
if (!this.fieldStats.has(field)) {
|
|
269
|
+
this.fieldStats.set(field, {
|
|
270
|
+
cardinality: {
|
|
271
|
+
uniqueValues: 0,
|
|
272
|
+
totalValues: 0,
|
|
273
|
+
distribution: 'uniform',
|
|
274
|
+
updateFrequency: 0,
|
|
275
|
+
lastAnalyzed: Date.now()
|
|
276
|
+
},
|
|
277
|
+
queryCount: 0,
|
|
278
|
+
rangeQueryCount: 0,
|
|
279
|
+
exactQueryCount: 0,
|
|
280
|
+
avgQueryTime: 0,
|
|
281
|
+
indexType: 'hash'
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
const stats = this.fieldStats.get(field);
|
|
285
|
+
const cardinality = stats.cardinality;
|
|
286
|
+
// Track unique values
|
|
287
|
+
const fieldIndexKey = `${field}:${String(value)}`;
|
|
288
|
+
const entry = this.indexCache.get(fieldIndexKey);
|
|
289
|
+
if (operation === 'add') {
|
|
290
|
+
if (!entry || entry.ids.size === 1) {
|
|
291
|
+
cardinality.uniqueValues++;
|
|
292
|
+
}
|
|
293
|
+
cardinality.totalValues++;
|
|
294
|
+
}
|
|
295
|
+
else if (operation === 'remove') {
|
|
296
|
+
if (entry && entry.ids.size === 0) {
|
|
297
|
+
cardinality.uniqueValues--;
|
|
298
|
+
}
|
|
299
|
+
cardinality.totalValues = Math.max(0, cardinality.totalValues - 1);
|
|
300
|
+
}
|
|
301
|
+
// Update frequency tracking
|
|
302
|
+
cardinality.updateFrequency++;
|
|
303
|
+
// Periodically analyze distribution
|
|
304
|
+
if (++this.operationCount % this.cardinalityUpdateInterval === 0) {
|
|
305
|
+
this.analyzeFieldDistribution(field);
|
|
306
|
+
}
|
|
307
|
+
// Determine optimal index type based on cardinality
|
|
308
|
+
this.updateIndexStrategy(field, stats);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Analyze field distribution for optimization
|
|
312
|
+
*/
|
|
313
|
+
analyzeFieldDistribution(field) {
|
|
314
|
+
const stats = this.fieldStats.get(field);
|
|
315
|
+
if (!stats)
|
|
316
|
+
return;
|
|
317
|
+
const cardinality = stats.cardinality;
|
|
318
|
+
const ratio = cardinality.uniqueValues / Math.max(1, cardinality.totalValues);
|
|
319
|
+
// Determine distribution type
|
|
320
|
+
if (ratio > 0.9) {
|
|
321
|
+
cardinality.distribution = 'sparse'; // High uniqueness (like IDs, timestamps)
|
|
322
|
+
}
|
|
323
|
+
else if (ratio < 0.1) {
|
|
324
|
+
cardinality.distribution = 'skewed'; // Low uniqueness (like status, type)
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
cardinality.distribution = 'uniform'; // Balanced distribution
|
|
328
|
+
}
|
|
329
|
+
cardinality.lastAnalyzed = Date.now();
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Update index strategy based on field statistics
|
|
333
|
+
*/
|
|
334
|
+
updateIndexStrategy(field, stats) {
|
|
335
|
+
const isNumeric = this.numericFields.has(field);
|
|
336
|
+
const hasHighCardinality = stats.cardinality.uniqueValues > this.HIGH_CARDINALITY_THRESHOLD;
|
|
337
|
+
const hasRangeQueries = stats.rangeQueryCount > stats.exactQueryCount * 0.3;
|
|
338
|
+
// Determine optimal index type
|
|
339
|
+
if (isNumeric && hasRangeQueries) {
|
|
340
|
+
stats.indexType = 'both'; // Need both hash and sorted
|
|
341
|
+
}
|
|
342
|
+
else if (hasRangeQueries) {
|
|
343
|
+
stats.indexType = 'sorted';
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
stats.indexType = 'hash';
|
|
347
|
+
}
|
|
348
|
+
// Determine normalization strategy for high cardinality fields
|
|
349
|
+
if (hasHighCardinality) {
|
|
350
|
+
if (field.toLowerCase().includes('time') || field.toLowerCase().includes('date')) {
|
|
351
|
+
stats.normalizationStrategy = 'bucket'; // Time bucketing
|
|
352
|
+
}
|
|
353
|
+
else if (isNumeric) {
|
|
354
|
+
stats.normalizationStrategy = 'precision'; // Reduce float precision
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
stats.normalizationStrategy = 'none'; // Keep as-is for strings
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
stats.normalizationStrategy = 'none';
|
|
362
|
+
}
|
|
363
|
+
}
|
|
151
364
|
/**
|
|
152
365
|
* Get IDs matching a range query
|
|
153
366
|
*/
|
|
154
367
|
async getIdsForRange(field, min, max, includeMin = true, includeMax = true) {
|
|
155
|
-
//
|
|
368
|
+
// Track range query for field statistics
|
|
369
|
+
if (this.fieldStats.has(field)) {
|
|
370
|
+
const stats = this.fieldStats.get(field);
|
|
371
|
+
stats.rangeQueryCount++;
|
|
372
|
+
}
|
|
373
|
+
// Ensure sorted index exists
|
|
156
374
|
await this.ensureSortedIndex(field);
|
|
157
|
-
|
|
158
|
-
|
|
375
|
+
// With incremental updates, we should rarely need to rebuild
|
|
376
|
+
// Only rebuild if it's marked dirty (e.g., after a bulk load or migration)
|
|
377
|
+
let sortedIndex = this.sortedIndices.get(field);
|
|
378
|
+
if (sortedIndex?.isDirty) {
|
|
379
|
+
prodLog.warn(`MetadataIndex: Sorted index for field '${field}' was dirty, rebuilding...`);
|
|
380
|
+
await this.buildSortedIndex(field);
|
|
381
|
+
sortedIndex = this.sortedIndices.get(field);
|
|
382
|
+
}
|
|
159
383
|
if (!sortedIndex || sortedIndex.values.length === 0)
|
|
160
384
|
return [];
|
|
161
385
|
const sorted = sortedIndex.values;
|
|
@@ -201,17 +425,36 @@ export class MetadataIndexManager {
|
|
|
201
425
|
.toLowerCase();
|
|
202
426
|
}
|
|
203
427
|
/**
|
|
204
|
-
* Normalize value for consistent indexing
|
|
428
|
+
* Normalize value for consistent indexing with smart optimization
|
|
205
429
|
*/
|
|
206
|
-
normalizeValue(value) {
|
|
430
|
+
normalizeValue(value, field) {
|
|
207
431
|
if (value === null || value === undefined)
|
|
208
432
|
return '__NULL__';
|
|
209
433
|
if (typeof value === 'boolean')
|
|
210
434
|
return value ? '__TRUE__' : '__FALSE__';
|
|
435
|
+
// Apply smart normalization based on field statistics
|
|
436
|
+
if (field && this.fieldStats.has(field)) {
|
|
437
|
+
const stats = this.fieldStats.get(field);
|
|
438
|
+
const strategy = stats.normalizationStrategy;
|
|
439
|
+
if (strategy === 'bucket' && typeof value === 'number') {
|
|
440
|
+
// Time bucketing for timestamps
|
|
441
|
+
if (field.toLowerCase().includes('time') || field.toLowerCase().includes('date')) {
|
|
442
|
+
const bucketSize = this.TIMESTAMP_PRECISION_MS;
|
|
443
|
+
const bucketed = Math.floor(value / bucketSize) * bucketSize;
|
|
444
|
+
return bucketed.toString();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
else if (strategy === 'precision' && typeof value === 'number') {
|
|
448
|
+
// Reduce float precision for high cardinality numeric fields
|
|
449
|
+
const rounded = Math.round(value * Math.pow(10, this.FLOAT_PRECISION)) / Math.pow(10, this.FLOAT_PRECISION);
|
|
450
|
+
return rounded.toString();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// Default normalization
|
|
211
454
|
if (typeof value === 'number')
|
|
212
455
|
return value.toString();
|
|
213
456
|
if (Array.isArray(value)) {
|
|
214
|
-
const joined = value.map(v => this.normalizeValue(v)).join(',');
|
|
457
|
+
const joined = value.map(v => this.normalizeValue(v, field)).join(',');
|
|
215
458
|
// Hash very long array values to avoid filesystem limits
|
|
216
459
|
if (joined.length > 100) {
|
|
217
460
|
return this.hashValue(joined);
|
|
@@ -285,13 +528,16 @@ export class MetadataIndexManager {
|
|
|
285
528
|
*/
|
|
286
529
|
async addToIndex(id, metadata, skipFlush = false) {
|
|
287
530
|
const fields = this.extractIndexableFields(metadata);
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
531
|
+
// Sort fields to process 'noun' field first for type-field affinity tracking
|
|
532
|
+
fields.sort((a, b) => {
|
|
533
|
+
if (a.field === 'noun')
|
|
534
|
+
return -1;
|
|
535
|
+
if (b.field === 'noun')
|
|
536
|
+
return 1;
|
|
537
|
+
return 0;
|
|
538
|
+
});
|
|
539
|
+
// Track which fields we're updating for incremental sorted index maintenance
|
|
540
|
+
const updatedFields = new Set();
|
|
295
541
|
for (let i = 0; i < fields.length; i++) {
|
|
296
542
|
const { field, value } = fields[i];
|
|
297
543
|
const key = this.getIndexKey(field, value);
|
|
@@ -308,9 +554,25 @@ export class MetadataIndexManager {
|
|
|
308
554
|
this.indexCache.set(key, entry);
|
|
309
555
|
}
|
|
310
556
|
// Add ID to entry
|
|
557
|
+
const wasNew = entry.ids.size === 0;
|
|
311
558
|
entry.ids.add(id);
|
|
312
559
|
entry.lastUpdated = Date.now();
|
|
313
560
|
this.dirtyEntries.add(key);
|
|
561
|
+
// Update cardinality statistics
|
|
562
|
+
if (wasNew) {
|
|
563
|
+
this.updateCardinalityStats(field, value, 'add');
|
|
564
|
+
}
|
|
565
|
+
// Track type-field affinity for intelligent NLP
|
|
566
|
+
this.updateTypeFieldAffinity(id, field, value, 'add');
|
|
567
|
+
// Incrementally update sorted index for this field
|
|
568
|
+
if (!updatedFields.has(field)) {
|
|
569
|
+
this.updateSortedIndexAdd(field, value, id);
|
|
570
|
+
updatedFields.add(field);
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
// Multiple values for same field - still update
|
|
574
|
+
this.updateSortedIndexAdd(field, value, id);
|
|
575
|
+
}
|
|
314
576
|
// Update field index
|
|
315
577
|
await this.updateFieldIndex(field, value, 1);
|
|
316
578
|
// Yield to event loop every 5 fields to prevent blocking
|
|
@@ -382,9 +644,20 @@ export class MetadataIndexManager {
|
|
|
382
644
|
entry = loadedEntry ?? undefined;
|
|
383
645
|
}
|
|
384
646
|
if (entry) {
|
|
647
|
+
const hadId = entry.ids.has(id);
|
|
385
648
|
entry.ids.delete(id);
|
|
386
649
|
entry.lastUpdated = Date.now();
|
|
387
650
|
this.dirtyEntries.add(key);
|
|
651
|
+
// Update cardinality statistics
|
|
652
|
+
if (hadId && entry.ids.size === 0) {
|
|
653
|
+
this.updateCardinalityStats(field, value, 'remove');
|
|
654
|
+
}
|
|
655
|
+
// Track type-field affinity for intelligent NLP
|
|
656
|
+
if (hadId) {
|
|
657
|
+
this.updateTypeFieldAffinity(id, field, value, 'remove');
|
|
658
|
+
}
|
|
659
|
+
// Incrementally update sorted index when removing
|
|
660
|
+
this.updateSortedIndexRemove(field, value, id);
|
|
388
661
|
// Update field index
|
|
389
662
|
await this.updateFieldIndex(field, value, -1);
|
|
390
663
|
// If no IDs left, mark for cleanup
|
|
@@ -404,6 +677,8 @@ export class MetadataIndexManager {
|
|
|
404
677
|
entry.ids.delete(id);
|
|
405
678
|
entry.lastUpdated = Date.now();
|
|
406
679
|
this.dirtyEntries.add(key);
|
|
680
|
+
// Incrementally update sorted index
|
|
681
|
+
this.updateSortedIndexRemove(entry.field, entry.value, id);
|
|
407
682
|
if (entry.ids.size === 0) {
|
|
408
683
|
this.indexCache.delete(key);
|
|
409
684
|
await this.deleteIndexEntry(key);
|
|
@@ -446,6 +721,11 @@ export class MetadataIndexManager {
|
|
|
446
721
|
* Get IDs for a specific field-value combination with caching
|
|
447
722
|
*/
|
|
448
723
|
async getIds(field, value) {
|
|
724
|
+
// Track exact query for field statistics
|
|
725
|
+
if (this.fieldStats.has(field)) {
|
|
726
|
+
const stats = this.fieldStats.get(field);
|
|
727
|
+
stats.exactQueryCount++;
|
|
728
|
+
}
|
|
449
729
|
const key = this.getIndexKey(field, value);
|
|
450
730
|
// Check metadata cache first
|
|
451
731
|
const cacheKey = `ids_${key}`;
|
|
@@ -944,7 +1224,7 @@ export class MetadataIndexManager {
|
|
|
944
1224
|
totalEntries,
|
|
945
1225
|
totalIds,
|
|
946
1226
|
fieldsIndexed: Array.from(fields),
|
|
947
|
-
lastRebuild:
|
|
1227
|
+
lastRebuild: Date.now(),
|
|
948
1228
|
indexSize: totalEntries * 100 // rough estimate
|
|
949
1229
|
};
|
|
950
1230
|
}
|
|
@@ -1174,5 +1454,248 @@ export class MetadataIndexManager {
|
|
|
1174
1454
|
// Entry might not exist
|
|
1175
1455
|
}
|
|
1176
1456
|
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Get field statistics for optimization and discovery
|
|
1459
|
+
*/
|
|
1460
|
+
async getFieldStatistics() {
|
|
1461
|
+
// Initialize stats for fields we haven't seen yet
|
|
1462
|
+
for (const field of this.fieldIndexes.keys()) {
|
|
1463
|
+
if (!this.fieldStats.has(field)) {
|
|
1464
|
+
this.fieldStats.set(field, {
|
|
1465
|
+
cardinality: {
|
|
1466
|
+
uniqueValues: 0,
|
|
1467
|
+
totalValues: 0,
|
|
1468
|
+
distribution: 'uniform',
|
|
1469
|
+
updateFrequency: 0,
|
|
1470
|
+
lastAnalyzed: Date.now()
|
|
1471
|
+
},
|
|
1472
|
+
queryCount: 0,
|
|
1473
|
+
rangeQueryCount: 0,
|
|
1474
|
+
exactQueryCount: 0,
|
|
1475
|
+
avgQueryTime: 0,
|
|
1476
|
+
indexType: 'hash'
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
return new Map(this.fieldStats);
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Get field cardinality information
|
|
1484
|
+
*/
|
|
1485
|
+
async getFieldCardinality(field) {
|
|
1486
|
+
const stats = this.fieldStats.get(field);
|
|
1487
|
+
return stats ? stats.cardinality : null;
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Get all field names with their cardinality (for query optimization)
|
|
1491
|
+
*/
|
|
1492
|
+
async getFieldsWithCardinality() {
|
|
1493
|
+
const fields = [];
|
|
1494
|
+
for (const [field, stats] of this.fieldStats) {
|
|
1495
|
+
fields.push({
|
|
1496
|
+
field,
|
|
1497
|
+
cardinality: stats.cardinality.uniqueValues,
|
|
1498
|
+
distribution: stats.cardinality.distribution
|
|
1499
|
+
});
|
|
1500
|
+
}
|
|
1501
|
+
// Sort by cardinality (low cardinality fields are better for filtering)
|
|
1502
|
+
fields.sort((a, b) => a.cardinality - b.cardinality);
|
|
1503
|
+
return fields;
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Get optimal query plan based on field statistics
|
|
1507
|
+
*/
|
|
1508
|
+
async getOptimalQueryPlan(filters) {
|
|
1509
|
+
const fieldOrder = [];
|
|
1510
|
+
let hasRangeQueries = false;
|
|
1511
|
+
let totalEstimatedCost = 0;
|
|
1512
|
+
// Analyze each filter
|
|
1513
|
+
for (const [field, value] of Object.entries(filters)) {
|
|
1514
|
+
const stats = this.fieldStats.get(field);
|
|
1515
|
+
if (!stats)
|
|
1516
|
+
continue;
|
|
1517
|
+
// Check if this is a range query
|
|
1518
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1519
|
+
hasRangeQueries = true;
|
|
1520
|
+
}
|
|
1521
|
+
// Estimate cost based on cardinality
|
|
1522
|
+
const cardinality = stats.cardinality.uniqueValues;
|
|
1523
|
+
const estimatedCost = Math.log2(Math.max(1, cardinality));
|
|
1524
|
+
totalEstimatedCost += estimatedCost;
|
|
1525
|
+
fieldOrder.push(field);
|
|
1526
|
+
}
|
|
1527
|
+
// Sort fields by cardinality (process low cardinality first)
|
|
1528
|
+
fieldOrder.sort((a, b) => {
|
|
1529
|
+
const statsA = this.fieldStats.get(a);
|
|
1530
|
+
const statsB = this.fieldStats.get(b);
|
|
1531
|
+
if (!statsA || !statsB)
|
|
1532
|
+
return 0;
|
|
1533
|
+
return statsA.cardinality.uniqueValues - statsB.cardinality.uniqueValues;
|
|
1534
|
+
});
|
|
1535
|
+
return {
|
|
1536
|
+
strategy: hasRangeQueries ? 'hybrid' : 'exact',
|
|
1537
|
+
fieldOrder,
|
|
1538
|
+
estimatedCost: totalEstimatedCost
|
|
1539
|
+
};
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Export field statistics for analysis
|
|
1543
|
+
*/
|
|
1544
|
+
async exportFieldStats() {
|
|
1545
|
+
const stats = {
|
|
1546
|
+
fields: {},
|
|
1547
|
+
summary: {
|
|
1548
|
+
totalFields: this.fieldStats.size,
|
|
1549
|
+
highCardinalityFields: 0,
|
|
1550
|
+
sparseFields: 0,
|
|
1551
|
+
skewedFields: 0,
|
|
1552
|
+
uniformFields: 0
|
|
1553
|
+
}
|
|
1554
|
+
};
|
|
1555
|
+
for (const [field, fieldStats] of this.fieldStats) {
|
|
1556
|
+
stats.fields[field] = {
|
|
1557
|
+
cardinality: fieldStats.cardinality,
|
|
1558
|
+
queryStats: {
|
|
1559
|
+
total: fieldStats.queryCount,
|
|
1560
|
+
exact: fieldStats.exactQueryCount,
|
|
1561
|
+
range: fieldStats.rangeQueryCount,
|
|
1562
|
+
avgTime: fieldStats.avgQueryTime
|
|
1563
|
+
},
|
|
1564
|
+
indexType: fieldStats.indexType,
|
|
1565
|
+
normalization: fieldStats.normalizationStrategy
|
|
1566
|
+
};
|
|
1567
|
+
// Update summary
|
|
1568
|
+
if (fieldStats.cardinality.uniqueValues > this.HIGH_CARDINALITY_THRESHOLD) {
|
|
1569
|
+
stats.summary.highCardinalityFields++;
|
|
1570
|
+
}
|
|
1571
|
+
switch (fieldStats.cardinality.distribution) {
|
|
1572
|
+
case 'sparse':
|
|
1573
|
+
stats.summary.sparseFields++;
|
|
1574
|
+
break;
|
|
1575
|
+
case 'skewed':
|
|
1576
|
+
stats.summary.skewedFields++;
|
|
1577
|
+
break;
|
|
1578
|
+
case 'uniform':
|
|
1579
|
+
stats.summary.uniformFields++;
|
|
1580
|
+
break;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
return stats;
|
|
1584
|
+
}
|
|
1585
|
+
/**
|
|
1586
|
+
* Update type-field affinity tracking for intelligent NLP
|
|
1587
|
+
* Tracks which fields commonly appear with which entity types
|
|
1588
|
+
*/
|
|
1589
|
+
updateTypeFieldAffinity(entityId, field, value, operation) {
|
|
1590
|
+
// Only track affinity for non-system fields (but allow 'noun' for type detection)
|
|
1591
|
+
if (this.config.excludeFields.includes(field) && field !== 'noun')
|
|
1592
|
+
return;
|
|
1593
|
+
// For the 'noun' field, the value IS the entity type
|
|
1594
|
+
let entityType = null;
|
|
1595
|
+
if (field === 'noun') {
|
|
1596
|
+
// This is the type definition itself
|
|
1597
|
+
entityType = this.normalizeValue(value);
|
|
1598
|
+
}
|
|
1599
|
+
else {
|
|
1600
|
+
// Find the noun type for this entity by looking for entries with this entityId
|
|
1601
|
+
for (const [key, entry] of this.indexCache.entries()) {
|
|
1602
|
+
if (key.startsWith('noun:') && entry.ids.has(entityId)) {
|
|
1603
|
+
entityType = key.split(':', 2)[1];
|
|
1604
|
+
break;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
if (!entityType)
|
|
1609
|
+
return; // No type found, skip affinity tracking
|
|
1610
|
+
// Initialize affinity tracking for this type
|
|
1611
|
+
if (!this.typeFieldAffinity.has(entityType)) {
|
|
1612
|
+
this.typeFieldAffinity.set(entityType, new Map());
|
|
1613
|
+
}
|
|
1614
|
+
if (!this.totalEntitiesByType.has(entityType)) {
|
|
1615
|
+
this.totalEntitiesByType.set(entityType, 0);
|
|
1616
|
+
}
|
|
1617
|
+
const typeFields = this.typeFieldAffinity.get(entityType);
|
|
1618
|
+
if (operation === 'add') {
|
|
1619
|
+
// Increment field count for this type
|
|
1620
|
+
const currentCount = typeFields.get(field) || 0;
|
|
1621
|
+
typeFields.set(field, currentCount + 1);
|
|
1622
|
+
// Update total entities of this type (only count once per entity)
|
|
1623
|
+
if (field === 'noun') {
|
|
1624
|
+
this.totalEntitiesByType.set(entityType, this.totalEntitiesByType.get(entityType) + 1);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
else if (operation === 'remove') {
|
|
1628
|
+
// Decrement field count for this type
|
|
1629
|
+
const currentCount = typeFields.get(field) || 0;
|
|
1630
|
+
if (currentCount > 1) {
|
|
1631
|
+
typeFields.set(field, currentCount - 1);
|
|
1632
|
+
}
|
|
1633
|
+
else {
|
|
1634
|
+
typeFields.delete(field);
|
|
1635
|
+
}
|
|
1636
|
+
// Update total entities of this type
|
|
1637
|
+
if (field === 'noun') {
|
|
1638
|
+
const total = this.totalEntitiesByType.get(entityType);
|
|
1639
|
+
if (total > 1) {
|
|
1640
|
+
this.totalEntitiesByType.set(entityType, total - 1);
|
|
1641
|
+
}
|
|
1642
|
+
else {
|
|
1643
|
+
this.totalEntitiesByType.delete(entityType);
|
|
1644
|
+
this.typeFieldAffinity.delete(entityType);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Get fields that commonly appear with a specific entity type
|
|
1651
|
+
* Returns fields with their affinity scores (0-1)
|
|
1652
|
+
*/
|
|
1653
|
+
async getFieldsForType(nounType) {
|
|
1654
|
+
const typeFields = this.typeFieldAffinity.get(nounType);
|
|
1655
|
+
const totalEntities = this.totalEntitiesByType.get(nounType);
|
|
1656
|
+
if (!typeFields || !totalEntities) {
|
|
1657
|
+
return [];
|
|
1658
|
+
}
|
|
1659
|
+
const fieldsWithAffinity = [];
|
|
1660
|
+
for (const [field, count] of typeFields.entries()) {
|
|
1661
|
+
const affinity = count / totalEntities; // 0-1 score
|
|
1662
|
+
fieldsWithAffinity.push({
|
|
1663
|
+
field,
|
|
1664
|
+
affinity,
|
|
1665
|
+
occurrences: count,
|
|
1666
|
+
totalEntities
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
// Sort by affinity (most common fields first)
|
|
1670
|
+
fieldsWithAffinity.sort((a, b) => b.affinity - a.affinity);
|
|
1671
|
+
return fieldsWithAffinity;
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Get type-field affinity statistics for analysis
|
|
1675
|
+
*/
|
|
1676
|
+
async getTypeFieldAffinityStats() {
|
|
1677
|
+
const typeBreakdown = {};
|
|
1678
|
+
let totalFields = 0;
|
|
1679
|
+
for (const [nounType, fieldsMap] of this.typeFieldAffinity.entries()) {
|
|
1680
|
+
const totalEntities = this.totalEntitiesByType.get(nounType) || 0;
|
|
1681
|
+
const fields = Array.from(fieldsMap.entries());
|
|
1682
|
+
// Get top 5 fields for this type
|
|
1683
|
+
const topFields = fields
|
|
1684
|
+
.map(([field, count]) => ({ field, affinity: count / totalEntities }))
|
|
1685
|
+
.sort((a, b) => b.affinity - a.affinity)
|
|
1686
|
+
.slice(0, 5);
|
|
1687
|
+
typeBreakdown[nounType] = {
|
|
1688
|
+
totalEntities,
|
|
1689
|
+
uniqueFields: fieldsMap.size,
|
|
1690
|
+
topFields
|
|
1691
|
+
};
|
|
1692
|
+
totalFields += fieldsMap.size;
|
|
1693
|
+
}
|
|
1694
|
+
return {
|
|
1695
|
+
totalTypes: this.typeFieldAffinity.size,
|
|
1696
|
+
averageFieldsPerType: totalFields / Math.max(1, this.typeFieldAffinity.size),
|
|
1697
|
+
typeBreakdown
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1177
1700
|
}
|
|
1178
1701
|
//# sourceMappingURL=metadataIndex.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js Version Compatibility Check
|
|
3
|
+
*
|
|
4
|
+
* Brainy requires Node.js 22.x LTS for maximum stability with ONNX Runtime.
|
|
5
|
+
* This prevents V8 HandleScope locking issues in worker threads.
|
|
6
|
+
*/
|
|
7
|
+
export interface VersionInfo {
|
|
8
|
+
current: string;
|
|
9
|
+
major: number;
|
|
10
|
+
isSupported: boolean;
|
|
11
|
+
recommendation: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check if the current Node.js version is supported
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkNodeVersion(): VersionInfo;
|
|
17
|
+
/**
|
|
18
|
+
* Enforce Node.js version requirement with helpful error messaging
|
|
19
|
+
*/
|
|
20
|
+
export declare function enforceNodeVersion(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Soft warning for version issues (non-blocking)
|
|
23
|
+
*/
|
|
24
|
+
export declare function warnNodeVersion(): boolean;
|