@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.
@@ -6,6 +6,7 @@
6
6
  import { MetadataIndexCache } from './metadataIndexCache.js';
7
7
  import { prodLog } from './logger.js';
8
8
  import { getGlobalCache } from './unifiedCache.js';
9
+ import { TypeUtils, NOUN_TYPE_COUNT, VERB_TYPE_COUNT } from '../types/graphTypes.js';
9
10
  import { SparseIndex, ChunkManager, AdaptiveChunkingStrategy } from './metadataIndexChunking.js';
10
11
  import { EntityIdMapper } from './entityIdMapper.js';
11
12
  import { RoaringBitmap32 } from 'roaring-wasm';
@@ -27,6 +28,13 @@ export class MetadataIndexManager {
27
28
  // Type-Field Affinity Tracking for intelligent NLP
28
29
  this.typeFieldAffinity = new Map(); // nounType -> field -> count
29
30
  this.totalEntitiesByType = new Map(); // nounType -> total count
31
+ // Phase 1b: Fixed-size type tracking (99.76% memory reduction vs Maps)
32
+ // Uint32Array provides O(1) access via type enum index
33
+ // 31 noun types × 4 bytes = 124 bytes (vs ~15KB with Map overhead)
34
+ // 40 verb types × 4 bytes = 160 bytes (vs ~20KB with Map overhead)
35
+ // Total: 284 bytes (vs ~35KB) = 99.2% memory reduction
36
+ this.entityCountsByTypeFixed = new Uint32Array(NOUN_TYPE_COUNT); // 124 bytes
37
+ this.verbCountsByTypeFixed = new Uint32Array(VERB_TYPE_COUNT); // 160 bytes
30
38
  // File locking for concurrent write protection (prevents race conditions)
31
39
  this.activeLocks = new Map();
32
40
  this.lockPromises = new Map();
@@ -83,6 +91,9 @@ export class MetadataIndexManager {
83
91
  async init() {
84
92
  // Initialize EntityIdMapper (loads UUID ↔ integer mappings from storage)
85
93
  await this.idMapper.init();
94
+ // Phase 1b: Sync loaded counts to fixed-size arrays
95
+ // This populates the Uint32Arrays from the Maps loaded by lazyLoadCounts()
96
+ this.syncTypeCountsToFixed();
86
97
  // Warm the cache with common fields (v3.44.1 - lazy loading optimization)
87
98
  await this.warmCache();
88
99
  }
@@ -107,6 +118,50 @@ export class MetadataIndexManager {
107
118
  }
108
119
  }));
109
120
  prodLog.debug('✅ Metadata cache warmed successfully');
121
+ // Phase 1b: Also warm cache for top types (type-aware optimization)
122
+ await this.warmCacheForTopTypes(3);
123
+ }
124
+ /**
125
+ * Phase 1b: Warm cache for top types (type-aware optimization)
126
+ * Preloads metadata indices for the most common entity types and their top fields
127
+ * This significantly improves query performance for the most frequently accessed data
128
+ *
129
+ * @param topN Number of top types to warm (default: 3)
130
+ */
131
+ async warmCacheForTopTypes(topN = 3) {
132
+ // Get top noun types by entity count
133
+ const topTypes = this.getTopNounTypes(topN);
134
+ if (topTypes.length === 0) {
135
+ prodLog.debug('⏭️ Skipping type-aware cache warming: no types found yet');
136
+ return;
137
+ }
138
+ prodLog.debug(`🔥 Warming cache for top ${topTypes.length} types: ${topTypes.join(', ')}`);
139
+ // For each top type, warm cache for its top fields
140
+ for (const type of topTypes) {
141
+ // Get fields with high affinity to this type
142
+ const typeFields = this.typeFieldAffinity.get(type);
143
+ if (!typeFields)
144
+ continue;
145
+ // Sort fields by count (most common first)
146
+ const topFields = Array.from(typeFields.entries())
147
+ .sort((a, b) => b[1] - a[1])
148
+ .slice(0, 5) // Top 5 fields per type
149
+ .map(([field]) => field);
150
+ if (topFields.length === 0)
151
+ continue;
152
+ prodLog.debug(` 📊 Type '${type}' - warming fields: ${topFields.join(', ')}`);
153
+ // Preload sparse indices for these fields in parallel
154
+ await Promise.all(topFields.map(async (field) => {
155
+ try {
156
+ await this.loadSparseIndex(field);
157
+ }
158
+ catch (error) {
159
+ // Silently ignore if field doesn't exist yet
160
+ prodLog.debug(` ⏭️ Field '${field}' not yet indexed for type '${type}'`);
161
+ }
162
+ }));
163
+ }
164
+ prodLog.debug('✅ Type-aware cache warming completed');
110
165
  }
111
166
  /**
112
167
  * Acquire an in-memory lock for coordinating concurrent metadata index writes
@@ -186,6 +241,49 @@ export class MetadataIndexManager {
186
241
  // This maintains zero-configuration principle
187
242
  }
188
243
  }
244
+ /**
245
+ * Phase 1b: Sync Map-based counts to fixed-size Uint32Arrays
246
+ * This enables gradual migration from Maps to arrays while maintaining backward compatibility
247
+ * Called periodically and on demand to keep both representations in sync
248
+ */
249
+ syncTypeCountsToFixed() {
250
+ // Sync noun counts from totalEntitiesByType Map to entityCountsByTypeFixed array
251
+ for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
252
+ const type = TypeUtils.getNounFromIndex(i);
253
+ const count = this.totalEntitiesByType.get(type) || 0;
254
+ this.entityCountsByTypeFixed[i] = count;
255
+ }
256
+ // Sync verb counts from totalEntitiesByType Map to verbCountsByTypeFixed array
257
+ // Note: Verb counts are currently tracked alongside noun counts in totalEntitiesByType
258
+ // In the future, we may want a separate Map for verb counts
259
+ for (let i = 0; i < VERB_TYPE_COUNT; i++) {
260
+ const type = TypeUtils.getVerbFromIndex(i);
261
+ const count = this.totalEntitiesByType.get(type) || 0;
262
+ this.verbCountsByTypeFixed[i] = count;
263
+ }
264
+ }
265
+ /**
266
+ * Phase 1b: Sync from fixed-size arrays back to Maps (reverse direction)
267
+ * Used when Uint32Arrays are the source of truth and need to update Maps
268
+ */
269
+ syncTypeCountsFromFixed() {
270
+ // Sync noun counts from array to Map
271
+ for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
272
+ const count = this.entityCountsByTypeFixed[i];
273
+ if (count > 0) {
274
+ const type = TypeUtils.getNounFromIndex(i);
275
+ this.totalEntitiesByType.set(type, count);
276
+ }
277
+ }
278
+ // Sync verb counts from array to Map
279
+ for (let i = 0; i < VERB_TYPE_COUNT; i++) {
280
+ const count = this.verbCountsByTypeFixed[i];
281
+ if (count > 0) {
282
+ const type = TypeUtils.getVerbFromIndex(i);
283
+ this.totalEntitiesByType.set(type, count);
284
+ }
285
+ }
286
+ }
189
287
  /**
190
288
  * Update cardinality statistics for a field
191
289
  */
@@ -1348,6 +1446,102 @@ export class MetadataIndexManager {
1348
1446
  getAllEntityCounts() {
1349
1447
  return new Map(this.totalEntitiesByType);
1350
1448
  }
1449
+ // ============================================================================
1450
+ // Phase 1b: Type Enum Methods (O(1) access via Uint32Arrays)
1451
+ // ============================================================================
1452
+ /**
1453
+ * Get entity count for a noun type using type enum (O(1) array access)
1454
+ * More efficient than Map-based getEntityCountByType
1455
+ * @param type Noun type from NounTypeEnum
1456
+ * @returns Count of entities of this type
1457
+ */
1458
+ getEntityCountByTypeEnum(type) {
1459
+ const index = TypeUtils.getNounIndex(type);
1460
+ return this.entityCountsByTypeFixed[index];
1461
+ }
1462
+ /**
1463
+ * Get verb count for a verb type using type enum (O(1) array access)
1464
+ * @param type Verb type from VerbTypeEnum
1465
+ * @returns Count of verbs of this type
1466
+ */
1467
+ getVerbCountByTypeEnum(type) {
1468
+ const index = TypeUtils.getVerbIndex(type);
1469
+ return this.verbCountsByTypeFixed[index];
1470
+ }
1471
+ /**
1472
+ * Get top N noun types by entity count (using fixed-size arrays)
1473
+ * Useful for type-aware cache warming and query optimization
1474
+ * @param n Number of top types to return
1475
+ * @returns Array of noun types sorted by count (highest first)
1476
+ */
1477
+ getTopNounTypes(n) {
1478
+ const types = [];
1479
+ // Iterate through all noun types
1480
+ for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
1481
+ const count = this.entityCountsByTypeFixed[i];
1482
+ if (count > 0) {
1483
+ const type = TypeUtils.getNounFromIndex(i);
1484
+ types.push({ type, count });
1485
+ }
1486
+ }
1487
+ // Sort by count (descending) and return top N
1488
+ return types
1489
+ .sort((a, b) => b.count - a.count)
1490
+ .slice(0, n)
1491
+ .map(t => t.type);
1492
+ }
1493
+ /**
1494
+ * Get top N verb types by count (using fixed-size arrays)
1495
+ * @param n Number of top types to return
1496
+ * @returns Array of verb types sorted by count (highest first)
1497
+ */
1498
+ getTopVerbTypes(n) {
1499
+ const types = [];
1500
+ // Iterate through all verb types
1501
+ for (let i = 0; i < VERB_TYPE_COUNT; i++) {
1502
+ const count = this.verbCountsByTypeFixed[i];
1503
+ if (count > 0) {
1504
+ const type = TypeUtils.getVerbFromIndex(i);
1505
+ types.push({ type, count });
1506
+ }
1507
+ }
1508
+ // Sort by count (descending) and return top N
1509
+ return types
1510
+ .sort((a, b) => b.count - a.count)
1511
+ .slice(0, n)
1512
+ .map(t => t.type);
1513
+ }
1514
+ /**
1515
+ * Get all noun type counts as a Map (using fixed-size arrays)
1516
+ * More efficient than getAllEntityCounts for type-aware queries
1517
+ * @returns Map of noun type to count
1518
+ */
1519
+ getAllNounTypeCounts() {
1520
+ const counts = new Map();
1521
+ for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
1522
+ const count = this.entityCountsByTypeFixed[i];
1523
+ if (count > 0) {
1524
+ const type = TypeUtils.getNounFromIndex(i);
1525
+ counts.set(type, count);
1526
+ }
1527
+ }
1528
+ return counts;
1529
+ }
1530
+ /**
1531
+ * Get all verb type counts as a Map (using fixed-size arrays)
1532
+ * @returns Map of verb type to count
1533
+ */
1534
+ getAllVerbTypeCounts() {
1535
+ const counts = new Map();
1536
+ for (let i = 0; i < VERB_TYPE_COUNT; i++) {
1537
+ const count = this.verbCountsByTypeFixed[i];
1538
+ if (count > 0) {
1539
+ const type = TypeUtils.getVerbFromIndex(i);
1540
+ counts.set(type, count);
1541
+ }
1542
+ }
1543
+ return counts;
1544
+ }
1351
1545
  /**
1352
1546
  * Get count of entities matching field-value criteria - queries chunked sparse index
1353
1547
  */
@@ -1763,7 +1957,17 @@ export class MetadataIndexManager {
1763
1957
  typeFields.set(field, currentCount + 1);
1764
1958
  // Update total entities of this type (only count once per entity)
1765
1959
  if (field === 'noun') {
1766
- this.totalEntitiesByType.set(entityType, this.totalEntitiesByType.get(entityType) + 1);
1960
+ const newCount = this.totalEntitiesByType.get(entityType) + 1;
1961
+ this.totalEntitiesByType.set(entityType, newCount);
1962
+ // Phase 1b: Also update fixed-size array
1963
+ // Try to parse as noun type - if it matches a known type, update the array
1964
+ try {
1965
+ const nounTypeIndex = TypeUtils.getNounIndex(entityType);
1966
+ this.entityCountsByTypeFixed[nounTypeIndex] = newCount;
1967
+ }
1968
+ catch {
1969
+ // Not a recognized noun type, skip fixed-size array update
1970
+ }
1767
1971
  }
1768
1972
  }
1769
1973
  else if (operation === 'remove') {
@@ -1779,11 +1983,28 @@ export class MetadataIndexManager {
1779
1983
  if (field === 'noun') {
1780
1984
  const total = this.totalEntitiesByType.get(entityType);
1781
1985
  if (total > 1) {
1782
- this.totalEntitiesByType.set(entityType, total - 1);
1986
+ const newCount = total - 1;
1987
+ this.totalEntitiesByType.set(entityType, newCount);
1988
+ // Phase 1b: Also update fixed-size array
1989
+ try {
1990
+ const nounTypeIndex = TypeUtils.getNounIndex(entityType);
1991
+ this.entityCountsByTypeFixed[nounTypeIndex] = newCount;
1992
+ }
1993
+ catch {
1994
+ // Not a recognized noun type, skip fixed-size array update
1995
+ }
1783
1996
  }
1784
1997
  else {
1785
1998
  this.totalEntitiesByType.delete(entityType);
1786
1999
  this.typeFieldAffinity.delete(entityType);
2000
+ // Phase 1b: Also zero out fixed-size array
2001
+ try {
2002
+ const nounTypeIndex = TypeUtils.getNounIndex(entityType);
2003
+ this.entityCountsByTypeFixed[nounTypeIndex] = 0;
2004
+ }
2005
+ catch {
2006
+ // Not a recognized noun type, skip fixed-size array update
2007
+ }
1787
2008
  }
1788
2009
  }
1789
2010
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/brainy",
3
- "version": "3.45.0",
3
+ "version": "3.47.0",
4
4
  "description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns × 40 verbs for infinite expressiveness.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",