@soulcraft/brainy 4.7.4 → 4.8.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/dist/api/DataAPI.js +3 -3
- package/dist/brainy.d.ts +7 -2
- package/dist/brainy.js +69 -39
- package/dist/coreTypes.d.ts +64 -14
- package/dist/coreTypes.js +3 -1
- package/dist/graph/graphAdjacencyIndex.js +38 -2
- package/dist/neural/embeddedTypeEmbeddings.d.ts +1 -1
- package/dist/neural/embeddedTypeEmbeddings.js +2 -2
- package/dist/storage/adapters/azureBlobStorage.js +68 -9
- package/dist/storage/adapters/fileSystemStorage.js +35 -6
- package/dist/storage/adapters/gcsStorage.js +24 -4
- package/dist/storage/adapters/memoryStorage.js +30 -5
- package/dist/storage/adapters/opfsStorage.js +24 -4
- package/dist/storage/adapters/r2Storage.js +13 -2
- package/dist/storage/adapters/s3CompatibleStorage.js +24 -3
- package/dist/storage/adapters/typeAwareStorageAdapter.js +33 -3
- package/dist/storage/baseStorage.js +43 -6
- package/dist/types/brainy.types.d.ts +4 -0
- package/dist/types/graphTypes.d.ts +1 -0
- package/dist/utils/entityIdMapper.js +3 -2
- package/dist/utils/metadataIndex.d.ts +23 -2
- package/dist/utils/metadataIndex.js +43 -12
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* 3. Service Account Credentials Object
|
|
9
9
|
* 4. HMAC Keys (fallback for backward compatibility)
|
|
10
10
|
*/
|
|
11
|
+
import { NounType } from '../../coreTypes.js';
|
|
11
12
|
import { BaseStorage, SYSTEM_DIR, STATISTICS_KEY, getDirectoryPath } from '../baseStorage.js';
|
|
12
13
|
import { BrainyError } from '../../errors/brainyError.js';
|
|
13
14
|
import { CacheManager } from '../cacheManager.js';
|
|
@@ -832,13 +833,23 @@ export class GcsStorage extends BaseStorage {
|
|
|
832
833
|
continue;
|
|
833
834
|
}
|
|
834
835
|
}
|
|
835
|
-
//
|
|
836
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
837
|
+
const metadataObj = (metadata || {});
|
|
838
|
+
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
836
839
|
const nounWithMetadata = {
|
|
837
840
|
id: node.id,
|
|
838
841
|
vector: [...node.vector],
|
|
839
842
|
connections: new Map(node.connections),
|
|
840
843
|
level: node.level || 0,
|
|
841
|
-
|
|
844
|
+
type: nounType || NounType.Thing,
|
|
845
|
+
createdAt: createdAt || Date.now(),
|
|
846
|
+
updatedAt: updatedAt || Date.now(),
|
|
847
|
+
confidence: confidence,
|
|
848
|
+
weight: weight,
|
|
849
|
+
service: service,
|
|
850
|
+
data: data,
|
|
851
|
+
createdBy,
|
|
852
|
+
metadata: customMetadata
|
|
842
853
|
};
|
|
843
854
|
items.push(nounWithMetadata);
|
|
844
855
|
}
|
|
@@ -1076,7 +1087,9 @@ export class GcsStorage extends BaseStorage {
|
|
|
1076
1087
|
continue;
|
|
1077
1088
|
}
|
|
1078
1089
|
}
|
|
1079
|
-
//
|
|
1090
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
1091
|
+
const metadataObj = (metadata || {});
|
|
1092
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
1080
1093
|
const verbWithMetadata = {
|
|
1081
1094
|
id: hnswVerb.id,
|
|
1082
1095
|
vector: [...hnswVerb.vector],
|
|
@@ -1084,7 +1097,14 @@ export class GcsStorage extends BaseStorage {
|
|
|
1084
1097
|
verb: hnswVerb.verb,
|
|
1085
1098
|
sourceId: hnswVerb.sourceId,
|
|
1086
1099
|
targetId: hnswVerb.targetId,
|
|
1087
|
-
|
|
1100
|
+
createdAt: createdAt || Date.now(),
|
|
1101
|
+
updatedAt: updatedAt || Date.now(),
|
|
1102
|
+
confidence: confidence,
|
|
1103
|
+
weight: weight,
|
|
1104
|
+
service: service,
|
|
1105
|
+
data: data,
|
|
1106
|
+
createdBy,
|
|
1107
|
+
metadata: customMetadata
|
|
1088
1108
|
};
|
|
1089
1109
|
items.push(verbWithMetadata);
|
|
1090
1110
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Memory Storage Adapter
|
|
3
3
|
* In-memory storage adapter for environments where persistent storage is not available or needed
|
|
4
4
|
*/
|
|
5
|
+
import { NounType } from '../../coreTypes.js';
|
|
5
6
|
import { BaseStorage } from '../baseStorage.js';
|
|
6
7
|
// No type aliases needed - using the original types directly
|
|
7
8
|
/**
|
|
@@ -154,13 +155,26 @@ export class MemoryStorage extends BaseStorage {
|
|
|
154
155
|
// Get metadata from separate storage
|
|
155
156
|
// FIX v4.7.4: Don't skip nouns without metadata - metadata is optional in v4.0.0
|
|
156
157
|
const metadata = await this.getNounMetadata(id);
|
|
157
|
-
// v4.
|
|
158
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
159
|
+
const metadataObj = (metadata || {});
|
|
160
|
+
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
161
|
+
// v4.8.0: Create HNSWNounWithMetadata with standard fields at top-level
|
|
158
162
|
const nounWithMetadata = {
|
|
159
163
|
id: noun.id,
|
|
160
164
|
vector: [...noun.vector],
|
|
161
165
|
connections: new Map(),
|
|
162
166
|
level: noun.level || 0,
|
|
163
|
-
|
|
167
|
+
// v4.8.0: Standard fields at top-level
|
|
168
|
+
type: nounType || NounType.Thing,
|
|
169
|
+
createdAt: createdAt || Date.now(),
|
|
170
|
+
updatedAt: updatedAt || Date.now(),
|
|
171
|
+
confidence: confidence,
|
|
172
|
+
weight: weight,
|
|
173
|
+
service: service,
|
|
174
|
+
data: data,
|
|
175
|
+
createdBy,
|
|
176
|
+
// Only custom user fields in metadata
|
|
177
|
+
metadata: customMetadata
|
|
164
178
|
};
|
|
165
179
|
// Copy connections
|
|
166
180
|
for (const [level, connections] of noun.connections.entries()) {
|
|
@@ -359,7 +373,10 @@ export class MemoryStorage extends BaseStorage {
|
|
|
359
373
|
// FIX v4.7.4: Don't skip verbs without metadata - metadata is optional in v4.0.0
|
|
360
374
|
// Core fields (verb, sourceId, targetId) are in HNSWVerb itself
|
|
361
375
|
const metadata = await this.getVerbMetadata(id);
|
|
362
|
-
// v4.
|
|
376
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
377
|
+
const metadataObj = metadata || {};
|
|
378
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
379
|
+
// v4.8.0: Create HNSWVerbWithMetadata with standard fields at top-level
|
|
363
380
|
const verbWithMetadata = {
|
|
364
381
|
id: hnswVerb.id,
|
|
365
382
|
vector: [...hnswVerb.vector],
|
|
@@ -368,8 +385,16 @@ export class MemoryStorage extends BaseStorage {
|
|
|
368
385
|
verb: hnswVerb.verb,
|
|
369
386
|
sourceId: hnswVerb.sourceId,
|
|
370
387
|
targetId: hnswVerb.targetId,
|
|
371
|
-
//
|
|
372
|
-
|
|
388
|
+
// v4.8.0: Standard fields at top-level
|
|
389
|
+
createdAt: createdAt || Date.now(),
|
|
390
|
+
updatedAt: updatedAt || Date.now(),
|
|
391
|
+
confidence: confidence,
|
|
392
|
+
weight: weight,
|
|
393
|
+
service: service,
|
|
394
|
+
data: data,
|
|
395
|
+
createdBy,
|
|
396
|
+
// Only custom user fields in metadata
|
|
397
|
+
metadata: customMetadata
|
|
373
398
|
};
|
|
374
399
|
// Copy connections
|
|
375
400
|
for (const [level, connections] of hnswVerb.connections.entries()) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* OPFS (Origin Private File System) Storage Adapter
|
|
3
3
|
* Provides persistent storage for the vector database using the Origin Private File System API
|
|
4
4
|
*/
|
|
5
|
+
import { NounType } from '../../coreTypes.js';
|
|
5
6
|
import { BaseStorage, NOUNS_DIR, VERBS_DIR, METADATA_DIR, NOUN_METADATA_DIR, VERB_METADATA_DIR, INDEX_DIR } from '../baseStorage.js';
|
|
6
7
|
import { getShardIdFromUuid } from '../sharding.js';
|
|
7
8
|
import '../../types/fileSystemTypes.js';
|
|
@@ -1446,13 +1447,23 @@ export class OPFSStorage extends BaseStorage {
|
|
|
1446
1447
|
continue;
|
|
1447
1448
|
}
|
|
1448
1449
|
}
|
|
1449
|
-
// v4.
|
|
1450
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
1451
|
+
const metadataObj = (metadata || {});
|
|
1452
|
+
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
1450
1453
|
const nounWithMetadata = {
|
|
1451
1454
|
id: noun.id,
|
|
1452
1455
|
vector: [...noun.vector],
|
|
1453
1456
|
connections: new Map(noun.connections),
|
|
1454
1457
|
level: noun.level || 0,
|
|
1455
|
-
|
|
1458
|
+
type: nounType || NounType.Thing,
|
|
1459
|
+
createdAt: createdAt || Date.now(),
|
|
1460
|
+
updatedAt: updatedAt || Date.now(),
|
|
1461
|
+
confidence: confidence,
|
|
1462
|
+
weight: weight,
|
|
1463
|
+
service: service,
|
|
1464
|
+
data: data,
|
|
1465
|
+
createdBy,
|
|
1466
|
+
metadata: customMetadata
|
|
1456
1467
|
};
|
|
1457
1468
|
items.push(nounWithMetadata);
|
|
1458
1469
|
}
|
|
@@ -1572,7 +1583,9 @@ export class OPFSStorage extends BaseStorage {
|
|
|
1572
1583
|
continue;
|
|
1573
1584
|
}
|
|
1574
1585
|
}
|
|
1575
|
-
// v4.
|
|
1586
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
1587
|
+
const metadataObj = (metadata || {});
|
|
1588
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
1576
1589
|
const verbWithMetadata = {
|
|
1577
1590
|
id: hnswVerb.id,
|
|
1578
1591
|
vector: [...hnswVerb.vector],
|
|
@@ -1580,7 +1593,14 @@ export class OPFSStorage extends BaseStorage {
|
|
|
1580
1593
|
verb: hnswVerb.verb,
|
|
1581
1594
|
sourceId: hnswVerb.sourceId,
|
|
1582
1595
|
targetId: hnswVerb.targetId,
|
|
1583
|
-
|
|
1596
|
+
createdAt: createdAt || Date.now(),
|
|
1597
|
+
updatedAt: updatedAt || Date.now(),
|
|
1598
|
+
confidence: confidence,
|
|
1599
|
+
weight: weight,
|
|
1600
|
+
service: service,
|
|
1601
|
+
data: data,
|
|
1602
|
+
createdBy,
|
|
1603
|
+
metadata: customMetadata
|
|
1584
1604
|
};
|
|
1585
1605
|
items.push(verbWithMetadata);
|
|
1586
1606
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Based on latest GCS and S3 implementations with R2-specific enhancements
|
|
13
13
|
*/
|
|
14
|
+
import { NounType } from '../../coreTypes.js';
|
|
14
15
|
import { BaseStorage, SYSTEM_DIR, STATISTICS_KEY, getDirectoryPath } from '../baseStorage.js';
|
|
15
16
|
import { BrainyError } from '../../errors/brainyError.js';
|
|
16
17
|
import { CacheManager } from '../cacheManager.js';
|
|
@@ -918,13 +919,23 @@ export class R2Storage extends BaseStorage {
|
|
|
918
919
|
continue;
|
|
919
920
|
}
|
|
920
921
|
}
|
|
921
|
-
// v4.
|
|
922
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
923
|
+
const metadataObj = (metadata || {});
|
|
924
|
+
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
922
925
|
const nounWithMetadata = {
|
|
923
926
|
id: noun.id,
|
|
924
927
|
vector: [...noun.vector],
|
|
925
928
|
connections: new Map(noun.connections),
|
|
926
929
|
level: noun.level || 0,
|
|
927
|
-
|
|
930
|
+
type: nounType || NounType.Thing,
|
|
931
|
+
createdAt: createdAt || Date.now(),
|
|
932
|
+
updatedAt: updatedAt || Date.now(),
|
|
933
|
+
confidence: confidence,
|
|
934
|
+
weight: weight,
|
|
935
|
+
service: service,
|
|
936
|
+
data: data,
|
|
937
|
+
createdBy,
|
|
938
|
+
metadata: customMetadata
|
|
928
939
|
};
|
|
929
940
|
items.push(nounWithMetadata);
|
|
930
941
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Uses the AWS S3 client to interact with S3-compatible storage services
|
|
4
4
|
* including Amazon S3, Cloudflare R2, and Google Cloud Storage
|
|
5
5
|
*/
|
|
6
|
+
import { NounType } from '../../coreTypes.js';
|
|
6
7
|
import { BaseStorage, INDEX_DIR, SYSTEM_DIR, STATISTICS_KEY, getDirectoryPath } from '../baseStorage.js';
|
|
7
8
|
import { StorageCompatibilityLayer } from '../backwardCompatibility.js';
|
|
8
9
|
import { StorageOperationExecutors } from '../../utils/operationUtils.js';
|
|
@@ -1479,6 +1480,9 @@ export class S3CompatibleStorage extends BaseStorage {
|
|
|
1479
1480
|
const verbsWithMetadata = [];
|
|
1480
1481
|
for (const hnswVerb of result.edges) {
|
|
1481
1482
|
const metadata = await this.getVerbMetadata(hnswVerb.id);
|
|
1483
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
1484
|
+
const metadataObj = (metadata || {});
|
|
1485
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
1482
1486
|
const verbWithMetadata = {
|
|
1483
1487
|
id: hnswVerb.id,
|
|
1484
1488
|
vector: [...hnswVerb.vector],
|
|
@@ -1486,7 +1490,14 @@ export class S3CompatibleStorage extends BaseStorage {
|
|
|
1486
1490
|
verb: hnswVerb.verb,
|
|
1487
1491
|
sourceId: hnswVerb.sourceId,
|
|
1488
1492
|
targetId: hnswVerb.targetId,
|
|
1489
|
-
|
|
1493
|
+
createdAt: createdAt || Date.now(),
|
|
1494
|
+
updatedAt: updatedAt || Date.now(),
|
|
1495
|
+
confidence: confidence,
|
|
1496
|
+
weight: weight,
|
|
1497
|
+
service: service,
|
|
1498
|
+
data: data,
|
|
1499
|
+
createdBy,
|
|
1500
|
+
metadata: customMetadata
|
|
1490
1501
|
};
|
|
1491
1502
|
verbsWithMetadata.push(verbWithMetadata);
|
|
1492
1503
|
}
|
|
@@ -2899,13 +2910,23 @@ export class S3CompatibleStorage extends BaseStorage {
|
|
|
2899
2910
|
}
|
|
2900
2911
|
}
|
|
2901
2912
|
}
|
|
2902
|
-
//
|
|
2913
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
2914
|
+
const metadataObj = (metadata || {});
|
|
2915
|
+
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
2903
2916
|
const nounWithMetadata = {
|
|
2904
2917
|
id: node.id,
|
|
2905
2918
|
vector: [...node.vector],
|
|
2906
2919
|
connections: new Map(node.connections),
|
|
2907
2920
|
level: node.level || 0,
|
|
2908
|
-
|
|
2921
|
+
type: nounType || NounType.Thing,
|
|
2922
|
+
createdAt: createdAt || Date.now(),
|
|
2923
|
+
updatedAt: updatedAt || Date.now(),
|
|
2924
|
+
confidence: confidence,
|
|
2925
|
+
weight: weight,
|
|
2926
|
+
service: service,
|
|
2927
|
+
data: data,
|
|
2928
|
+
createdBy,
|
|
2929
|
+
metadata: customMetadata
|
|
2909
2930
|
};
|
|
2910
2931
|
nounsWithMetadata.push(nounWithMetadata);
|
|
2911
2932
|
}
|
|
@@ -366,6 +366,9 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
366
366
|
connectionsMap.set(Number(level), new Set(ids));
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
370
|
+
const metadataObj = (metadata || {});
|
|
371
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
369
372
|
const verbWithMetadata = {
|
|
370
373
|
id: hnswVerb.id,
|
|
371
374
|
vector: [...hnswVerb.vector],
|
|
@@ -373,7 +376,14 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
373
376
|
verb: hnswVerb.verb,
|
|
374
377
|
sourceId: hnswVerb.sourceId,
|
|
375
378
|
targetId: hnswVerb.targetId,
|
|
376
|
-
|
|
379
|
+
createdAt: createdAt || Date.now(),
|
|
380
|
+
updatedAt: updatedAt || Date.now(),
|
|
381
|
+
confidence: confidence,
|
|
382
|
+
weight: weight,
|
|
383
|
+
service: service,
|
|
384
|
+
data: data,
|
|
385
|
+
createdBy,
|
|
386
|
+
metadata: customMetadata
|
|
377
387
|
};
|
|
378
388
|
verbs.push(verbWithMetadata);
|
|
379
389
|
}
|
|
@@ -417,6 +427,9 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
417
427
|
connectionsMap.set(Number(level), new Set(ids));
|
|
418
428
|
}
|
|
419
429
|
}
|
|
430
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
431
|
+
const metadataObj = (metadata || {});
|
|
432
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
420
433
|
const verbWithMetadata = {
|
|
421
434
|
id: hnswVerb.id,
|
|
422
435
|
vector: [...hnswVerb.vector],
|
|
@@ -424,7 +437,14 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
424
437
|
verb: hnswVerb.verb,
|
|
425
438
|
sourceId: hnswVerb.sourceId,
|
|
426
439
|
targetId: hnswVerb.targetId,
|
|
427
|
-
|
|
440
|
+
createdAt: createdAt || Date.now(),
|
|
441
|
+
updatedAt: updatedAt || Date.now(),
|
|
442
|
+
confidence: confidence,
|
|
443
|
+
weight: weight,
|
|
444
|
+
service: service,
|
|
445
|
+
data: data,
|
|
446
|
+
createdBy,
|
|
447
|
+
metadata: customMetadata
|
|
428
448
|
};
|
|
429
449
|
verbs.push(verbWithMetadata);
|
|
430
450
|
}
|
|
@@ -463,6 +483,9 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
463
483
|
connectionsMap.set(Number(level), new Set(ids));
|
|
464
484
|
}
|
|
465
485
|
}
|
|
486
|
+
// v4.8.0: Extract standard fields from metadata to top-level
|
|
487
|
+
const metadataObj = (metadata || {});
|
|
488
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
|
|
466
489
|
const verbWithMetadata = {
|
|
467
490
|
id: hnswVerb.id,
|
|
468
491
|
vector: [...hnswVerb.vector],
|
|
@@ -470,7 +493,14 @@ export class TypeAwareStorageAdapter extends BaseStorage {
|
|
|
470
493
|
verb: hnswVerb.verb,
|
|
471
494
|
sourceId: hnswVerb.sourceId,
|
|
472
495
|
targetId: hnswVerb.targetId,
|
|
473
|
-
|
|
496
|
+
createdAt: createdAt || Date.now(),
|
|
497
|
+
updatedAt: updatedAt || Date.now(),
|
|
498
|
+
confidence: confidence,
|
|
499
|
+
weight: weight,
|
|
500
|
+
service: service,
|
|
501
|
+
data: data,
|
|
502
|
+
createdBy,
|
|
503
|
+
metadata: customMetadata
|
|
474
504
|
};
|
|
475
505
|
verbs.push(verbWithMetadata);
|
|
476
506
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { GraphAdjacencyIndex } from '../graph/graphAdjacencyIndex.js';
|
|
6
6
|
import { BaseStorageAdapter } from './adapters/baseStorageAdapter.js';
|
|
7
7
|
import { validateNounType, validateVerbType } from '../utils/typeValidation.js';
|
|
8
|
+
import { NounType } from '../types/graphTypes.js';
|
|
8
9
|
import { getShardIdFromUuid } from './sharding.js';
|
|
9
10
|
// Clean directory structure (v4.7.2+)
|
|
10
11
|
// All storage adapters use this consistent structure
|
|
@@ -46,6 +47,10 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
46
47
|
* @private
|
|
47
48
|
*/
|
|
48
49
|
analyzeKey(id, context) {
|
|
50
|
+
// v4.8.0: Guard against undefined/null IDs
|
|
51
|
+
if (!id || typeof id !== 'string') {
|
|
52
|
+
throw new Error(`Invalid storage key: ${id} (must be a non-empty string)`);
|
|
53
|
+
}
|
|
49
54
|
// System resource detection
|
|
50
55
|
const isSystemKey = id.startsWith('__metadata_') ||
|
|
51
56
|
id.startsWith('__index_') ||
|
|
@@ -142,13 +147,24 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
142
147
|
console.warn(`[Storage] Noun ${id} has vector but no metadata - this should not happen in v4.0.0`);
|
|
143
148
|
return null;
|
|
144
149
|
}
|
|
145
|
-
// Combine into HNSWNounWithMetadata
|
|
150
|
+
// Combine into HNSWNounWithMetadata - v4.8.0: Extract standard fields to top-level
|
|
151
|
+
const { noun, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadata;
|
|
146
152
|
return {
|
|
147
153
|
id: vector.id,
|
|
148
154
|
vector: vector.vector,
|
|
149
155
|
connections: vector.connections,
|
|
150
156
|
level: vector.level,
|
|
151
|
-
|
|
157
|
+
// v4.8.0: Standard fields at top-level
|
|
158
|
+
type: noun || NounType.Thing,
|
|
159
|
+
createdAt: createdAt || Date.now(),
|
|
160
|
+
updatedAt: updatedAt || Date.now(),
|
|
161
|
+
confidence: confidence,
|
|
162
|
+
weight: weight,
|
|
163
|
+
service: service,
|
|
164
|
+
data: data,
|
|
165
|
+
createdBy,
|
|
166
|
+
// Only custom user fields remain in metadata
|
|
167
|
+
metadata: customMetadata
|
|
152
168
|
};
|
|
153
169
|
}
|
|
154
170
|
/**
|
|
@@ -160,14 +176,25 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
160
176
|
await this.ensureInitialized();
|
|
161
177
|
// Internal method returns HNSWNoun[], need to combine with metadata
|
|
162
178
|
const nouns = await this.getNounsByNounType_internal(nounType);
|
|
163
|
-
// Combine each noun with its metadata
|
|
179
|
+
// Combine each noun with its metadata - v4.8.0: Extract standard fields to top-level
|
|
164
180
|
const nounsWithMetadata = [];
|
|
165
181
|
for (const noun of nouns) {
|
|
166
182
|
const metadata = await this.getNounMetadata(noun.id);
|
|
167
183
|
if (metadata) {
|
|
184
|
+
const { noun: nounType, createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadata;
|
|
168
185
|
nounsWithMetadata.push({
|
|
169
186
|
...noun,
|
|
170
|
-
|
|
187
|
+
// v4.8.0: Standard fields at top-level
|
|
188
|
+
type: nounType || NounType.Thing,
|
|
189
|
+
createdAt: createdAt || Date.now(),
|
|
190
|
+
updatedAt: updatedAt || Date.now(),
|
|
191
|
+
confidence: confidence,
|
|
192
|
+
weight: weight,
|
|
193
|
+
service: service,
|
|
194
|
+
data: data,
|
|
195
|
+
createdBy,
|
|
196
|
+
// Only custom user fields in metadata
|
|
197
|
+
metadata: customMetadata
|
|
171
198
|
});
|
|
172
199
|
}
|
|
173
200
|
}
|
|
@@ -220,7 +247,8 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
220
247
|
console.warn(`[Storage] Verb ${id} has vector but no metadata - this should not happen in v4.0.0`);
|
|
221
248
|
return null;
|
|
222
249
|
}
|
|
223
|
-
// Combine into HNSWVerbWithMetadata
|
|
250
|
+
// Combine into HNSWVerbWithMetadata - v4.8.0: Extract standard fields to top-level
|
|
251
|
+
const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadata;
|
|
224
252
|
return {
|
|
225
253
|
id: verb.id,
|
|
226
254
|
vector: verb.vector,
|
|
@@ -228,7 +256,16 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
228
256
|
verb: verb.verb,
|
|
229
257
|
sourceId: verb.sourceId,
|
|
230
258
|
targetId: verb.targetId,
|
|
231
|
-
|
|
259
|
+
// v4.8.0: Standard fields at top-level
|
|
260
|
+
createdAt: createdAt || Date.now(),
|
|
261
|
+
updatedAt: updatedAt || Date.now(),
|
|
262
|
+
confidence: confidence,
|
|
263
|
+
weight: weight,
|
|
264
|
+
service: service,
|
|
265
|
+
data: data,
|
|
266
|
+
createdBy,
|
|
267
|
+
// Only custom user fields remain in metadata
|
|
268
|
+
metadata: customMetadata
|
|
232
269
|
};
|
|
233
270
|
}
|
|
234
271
|
/**
|
|
@@ -33,8 +33,9 @@ export class EntityIdMapper {
|
|
|
33
33
|
async init() {
|
|
34
34
|
try {
|
|
35
35
|
const metadata = await this.storage.getMetadata(this.storageKey);
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// v4.8.0: metadata IS the data (no nested 'data' property)
|
|
37
|
+
if (metadata && metadata.nextId !== undefined) {
|
|
38
|
+
const data = metadata;
|
|
38
39
|
this.nextId = data.nextId;
|
|
39
40
|
// Rebuild maps from serialized data
|
|
40
41
|
this.uuidToInt = new Map(Object.entries(data.uuidToInt).map(([k, v]) => [k, Number(v)]));
|
|
@@ -228,7 +228,13 @@ export declare class MetadataIndexManager {
|
|
|
228
228
|
*/
|
|
229
229
|
private shouldIndexField;
|
|
230
230
|
/**
|
|
231
|
-
* Extract indexable field-value pairs from metadata
|
|
231
|
+
* Extract indexable field-value pairs from entity or metadata
|
|
232
|
+
*
|
|
233
|
+
* v4.8.0: Now handles BOTH entity structure (with top-level fields) AND plain metadata
|
|
234
|
+
* - Extracts from top-level fields (confidence, weight, timestamps, type, service, etc.)
|
|
235
|
+
* - Also extracts from nested metadata field (custom user fields)
|
|
236
|
+
* - Skips HNSW-specific fields (vector, connections, level, id)
|
|
237
|
+
* - Maps 'type' → 'noun' for backward compatibility with existing indexes
|
|
232
238
|
*
|
|
233
239
|
* BUG FIX (v3.50.1): Exclude vector embeddings and large arrays from indexing
|
|
234
240
|
* BUG FIX (v3.50.2): Also exclude purely numeric field names (array indices)
|
|
@@ -238,14 +244,29 @@ export declare class MetadataIndexManager {
|
|
|
238
244
|
private extractIndexableFields;
|
|
239
245
|
/**
|
|
240
246
|
* Add item to metadata indexes
|
|
247
|
+
*
|
|
248
|
+
* v4.8.0: Now accepts either entity structure or plain metadata
|
|
249
|
+
* - Entity structure: { id, type, confidence, weight, createdAt, metadata: {...} }
|
|
250
|
+
* - Plain metadata: { noun, confidence, weight, createdAt, ... }
|
|
251
|
+
*
|
|
252
|
+
* @param id - Entity ID
|
|
253
|
+
* @param entityOrMetadata - Either full entity structure (v4.8.0+) or plain metadata (backward compat)
|
|
254
|
+
* @param skipFlush - Skip automatic flush (used during batch operations)
|
|
241
255
|
*/
|
|
242
|
-
addToIndex(id: string,
|
|
256
|
+
addToIndex(id: string, entityOrMetadata: any, skipFlush?: boolean): Promise<void>;
|
|
243
257
|
/**
|
|
244
258
|
* Update field index with value count
|
|
245
259
|
*/
|
|
246
260
|
private updateFieldIndex;
|
|
247
261
|
/**
|
|
248
262
|
* Remove item from metadata indexes
|
|
263
|
+
*
|
|
264
|
+
* v4.8.0: Now accepts either entity structure or plain metadata (same as addToIndex)
|
|
265
|
+
* - Entity structure: { id, type, confidence, weight, createdAt, metadata: {...} }
|
|
266
|
+
* - Plain metadata: { noun, confidence, weight, createdAt, ... }
|
|
267
|
+
*
|
|
268
|
+
* @param id - Entity ID to remove
|
|
269
|
+
* @param metadata - Optional entity or metadata structure (if not provided, requires scanning all fields - slow!)
|
|
249
270
|
*/
|
|
250
271
|
removeFromIndex(id: string, metadata?: any): Promise<void>;
|
|
251
272
|
/**
|
|
@@ -856,22 +856,28 @@ export class MetadataIndexManager {
|
|
|
856
856
|
return true;
|
|
857
857
|
}
|
|
858
858
|
/**
|
|
859
|
-
* Extract indexable field-value pairs from metadata
|
|
859
|
+
* Extract indexable field-value pairs from entity or metadata
|
|
860
|
+
*
|
|
861
|
+
* v4.8.0: Now handles BOTH entity structure (with top-level fields) AND plain metadata
|
|
862
|
+
* - Extracts from top-level fields (confidence, weight, timestamps, type, service, etc.)
|
|
863
|
+
* - Also extracts from nested metadata field (custom user fields)
|
|
864
|
+
* - Skips HNSW-specific fields (vector, connections, level, id)
|
|
865
|
+
* - Maps 'type' → 'noun' for backward compatibility with existing indexes
|
|
860
866
|
*
|
|
861
867
|
* BUG FIX (v3.50.1): Exclude vector embeddings and large arrays from indexing
|
|
862
868
|
* BUG FIX (v3.50.2): Also exclude purely numeric field names (array indices)
|
|
863
869
|
* - Vector fields (384+ dimensions) were creating 825K chunk files for 1,144 entities
|
|
864
870
|
* - Arrays converted to objects with numeric keys were still being indexed
|
|
865
871
|
*/
|
|
866
|
-
extractIndexableFields(
|
|
872
|
+
extractIndexableFields(data) {
|
|
867
873
|
const fields = [];
|
|
868
|
-
// Fields that should NEVER be indexed (vectors, embeddings, large arrays)
|
|
869
|
-
const NEVER_INDEX = new Set(['vector', 'embedding', 'embeddings', 'connections']);
|
|
874
|
+
// Fields that should NEVER be indexed (vectors, embeddings, large arrays, HNSW internals)
|
|
875
|
+
const NEVER_INDEX = new Set(['vector', 'embedding', 'embeddings', 'connections', 'level', 'id']);
|
|
870
876
|
const extract = (obj, prefix = '') => {
|
|
871
877
|
for (const [key, value] of Object.entries(obj)) {
|
|
872
878
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
873
|
-
// Skip fields in never-index list (CRITICAL: prevents vector indexing bug)
|
|
874
|
-
if (NEVER_INDEX.has(key))
|
|
879
|
+
// Skip fields in never-index list (CRITICAL: prevents vector indexing bug + HNSW fields)
|
|
880
|
+
if (!prefix && NEVER_INDEX.has(key))
|
|
875
881
|
continue;
|
|
876
882
|
// Skip purely numeric field names (array indices converted to object keys)
|
|
877
883
|
// Legitimate field names should never be purely numeric
|
|
@@ -881,6 +887,14 @@ export class MetadataIndexManager {
|
|
|
881
887
|
// Skip fields based on user configuration
|
|
882
888
|
if (!this.shouldIndexField(fullKey))
|
|
883
889
|
continue;
|
|
890
|
+
// Special handling for metadata field at top level
|
|
891
|
+
// v4.8.0: Flatten metadata fields to top-level (no prefix) for cleaner queries
|
|
892
|
+
// Standard fields are already at top-level, custom fields go in metadata
|
|
893
|
+
// By flattening here, queries can use { category: 'B' } instead of { 'metadata.category': 'B' }
|
|
894
|
+
if (key === 'metadata' && !prefix && typeof value === 'object' && !Array.isArray(value)) {
|
|
895
|
+
extract(value, ''); // Flatten to top-level, no prefix
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
884
898
|
// Skip large arrays (> 10 elements) - likely vectors or bulk data
|
|
885
899
|
if (Array.isArray(value) && value.length > 10)
|
|
886
900
|
continue;
|
|
@@ -900,20 +914,30 @@ export class MetadataIndexManager {
|
|
|
900
914
|
}
|
|
901
915
|
else {
|
|
902
916
|
// Primitive value: index it
|
|
903
|
-
|
|
917
|
+
// v4.8.0: Map 'type' → 'noun' for backward compatibility
|
|
918
|
+
const indexField = (!prefix && key === 'type') ? 'noun' : fullKey;
|
|
919
|
+
fields.push({ field: indexField, value });
|
|
904
920
|
}
|
|
905
921
|
}
|
|
906
922
|
};
|
|
907
|
-
if (
|
|
908
|
-
extract(
|
|
923
|
+
if (data && typeof data === 'object') {
|
|
924
|
+
extract(data);
|
|
909
925
|
}
|
|
910
926
|
return fields;
|
|
911
927
|
}
|
|
912
928
|
/**
|
|
913
929
|
* Add item to metadata indexes
|
|
930
|
+
*
|
|
931
|
+
* v4.8.0: Now accepts either entity structure or plain metadata
|
|
932
|
+
* - Entity structure: { id, type, confidence, weight, createdAt, metadata: {...} }
|
|
933
|
+
* - Plain metadata: { noun, confidence, weight, createdAt, ... }
|
|
934
|
+
*
|
|
935
|
+
* @param id - Entity ID
|
|
936
|
+
* @param entityOrMetadata - Either full entity structure (v4.8.0+) or plain metadata (backward compat)
|
|
937
|
+
* @param skipFlush - Skip automatic flush (used during batch operations)
|
|
914
938
|
*/
|
|
915
|
-
async addToIndex(id,
|
|
916
|
-
const fields = this.extractIndexableFields(
|
|
939
|
+
async addToIndex(id, entityOrMetadata, skipFlush = false) {
|
|
940
|
+
const fields = this.extractIndexableFields(entityOrMetadata);
|
|
917
941
|
// Sort fields to process 'noun' field first for type-field affinity tracking
|
|
918
942
|
fields.sort((a, b) => {
|
|
919
943
|
if (a.field === 'noun')
|
|
@@ -930,7 +954,7 @@ export class MetadataIndexManager {
|
|
|
930
954
|
await this.addToChunkedIndex(field, value, id);
|
|
931
955
|
// Update statistics and tracking
|
|
932
956
|
this.updateCardinalityStats(field, value, 'add');
|
|
933
|
-
this.updateTypeFieldAffinity(id, field, value, 'add',
|
|
957
|
+
this.updateTypeFieldAffinity(id, field, value, 'add', entityOrMetadata);
|
|
934
958
|
await this.updateFieldIndex(field, value, 1);
|
|
935
959
|
// Yield to event loop every 5 fields to prevent blocking
|
|
936
960
|
if (i % 5 === 4) {
|
|
@@ -988,6 +1012,13 @@ export class MetadataIndexManager {
|
|
|
988
1012
|
}
|
|
989
1013
|
/**
|
|
990
1014
|
* Remove item from metadata indexes
|
|
1015
|
+
*
|
|
1016
|
+
* v4.8.0: Now accepts either entity structure or plain metadata (same as addToIndex)
|
|
1017
|
+
* - Entity structure: { id, type, confidence, weight, createdAt, metadata: {...} }
|
|
1018
|
+
* - Plain metadata: { noun, confidence, weight, createdAt, ... }
|
|
1019
|
+
*
|
|
1020
|
+
* @param id - Entity ID to remove
|
|
1021
|
+
* @param metadata - Optional entity or metadata structure (if not provided, requires scanning all fields - slow!)
|
|
991
1022
|
*/
|
|
992
1023
|
async removeFromIndex(id, metadata) {
|
|
993
1024
|
if (metadata) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.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",
|