@soulcraft/brainy 6.1.0 → 6.2.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 +271 -0
- package/dist/augmentations/KnowledgeAugmentation.d.ts +40 -0
- package/dist/augmentations/KnowledgeAugmentation.js +251 -0
- package/dist/brainy.d.ts +17 -13
- package/dist/brainy.js +172 -41
- package/dist/coreTypes.d.ts +12 -0
- package/dist/graph/graphAdjacencyIndex.d.ts +23 -0
- package/dist/graph/graphAdjacencyIndex.js +49 -0
- package/dist/importManager.d.ts +78 -0
- package/dist/importManager.js +267 -0
- package/dist/query/typeInference.d.ts +158 -0
- package/dist/query/typeInference.js +760 -0
- package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +252 -0
- package/dist/storage/adapters/typeAwareStorageAdapter.js +814 -0
- package/dist/storage/baseStorage.d.ts +36 -0
- package/dist/storage/baseStorage.js +159 -4
- package/dist/storage/cow/binaryDataCodec.d.ts +13 -2
- package/dist/storage/cow/binaryDataCodec.js +15 -2
- package/dist/types/brainy.types.d.ts +1 -0
- package/dist/types/brainyDataInterface.d.ts +52 -0
- package/dist/types/brainyDataInterface.js +10 -0
- package/dist/utils/metadataIndex.d.ts +17 -0
- package/dist/utils/metadataIndex.js +63 -0
- package/dist/vfs/ConceptSystem.d.ts +203 -0
- package/dist/vfs/ConceptSystem.js +545 -0
- package/dist/vfs/EntityManager.d.ts +75 -0
- package/dist/vfs/EntityManager.js +216 -0
- package/dist/vfs/EventRecorder.d.ts +84 -0
- package/dist/vfs/EventRecorder.js +269 -0
- package/dist/vfs/GitBridge.d.ts +167 -0
- package/dist/vfs/GitBridge.js +537 -0
- package/dist/vfs/KnowledgeLayer.d.ts +35 -0
- package/dist/vfs/KnowledgeLayer.js +443 -0
- package/dist/vfs/PersistentEntitySystem.d.ts +165 -0
- package/dist/vfs/PersistentEntitySystem.js +503 -0
- package/dist/vfs/SemanticVersioning.d.ts +105 -0
- package/dist/vfs/SemanticVersioning.js +309 -0
- package/dist/vfs/VirtualFileSystem.d.ts +37 -2
- package/dist/vfs/VirtualFileSystem.js +105 -68
- package/package.json +1 -1
package/dist/brainy.js
CHANGED
|
@@ -575,13 +575,12 @@ export class Brainy {
|
|
|
575
575
|
return results;
|
|
576
576
|
const includeVectors = options?.includeVectors ?? false;
|
|
577
577
|
if (includeVectors) {
|
|
578
|
-
// FULL PATH
|
|
579
|
-
//
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
}
|
|
578
|
+
// v6.2.0: FULL PATH optimized with batch vector loading (10x faster on GCS)
|
|
579
|
+
// GCS: 10 entities with vectors = 1×50ms vs 10×50ms = 500ms (10x faster)
|
|
580
|
+
const nounsMap = await this.storage.getNounBatch(ids);
|
|
581
|
+
for (const [id, noun] of nounsMap.entries()) {
|
|
582
|
+
const entity = await this.convertNounToEntity(noun);
|
|
583
|
+
results.set(id, entity);
|
|
585
584
|
}
|
|
586
585
|
}
|
|
587
586
|
else {
|
|
@@ -769,7 +768,13 @@ export class Brainy {
|
|
|
769
768
|
}
|
|
770
769
|
}
|
|
771
770
|
// Operation 5-6: Update metadata index (remove old, add new)
|
|
772
|
-
|
|
771
|
+
// v6.2.1: Fix - Include type in removal metadata so noun index is properly updated
|
|
772
|
+
// existing.metadata only contains custom fields, not 'type' which maps to 'noun'
|
|
773
|
+
const removalMetadata = {
|
|
774
|
+
...existing.metadata,
|
|
775
|
+
type: existing.type // Include type so it maps to 'noun' during removal
|
|
776
|
+
};
|
|
777
|
+
tx.addOperation(new RemoveFromMetadataIndexOperation(this.metadataIndex, params.id, removalMetadata));
|
|
773
778
|
tx.addOperation(new AddToMetadataIndexOperation(this.metadataIndex, params.id, entityForIndexing));
|
|
774
779
|
});
|
|
775
780
|
});
|
|
@@ -941,13 +946,16 @@ export class Brainy {
|
|
|
941
946
|
// Bug #1 showed incrementing verb counts (7→8→9...) indicating duplicates
|
|
942
947
|
// v5.8.0 OPTIMIZATION: Use GraphAdjacencyIndex for O(log n) lookup instead of O(n) storage scan
|
|
943
948
|
const verbIds = await this.graphIndex.getVerbIdsBySource(params.from);
|
|
944
|
-
//
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
949
|
+
// v6.2.0: Batch-load verbs for 5x faster duplicate checking on GCS
|
|
950
|
+
// GCS: 5 verbs = 1×50ms vs 5×50ms = 250ms (5x faster)
|
|
951
|
+
if (verbIds.length > 0) {
|
|
952
|
+
const verbsMap = await this.graphIndex.getVerbsBatchCached(verbIds);
|
|
953
|
+
for (const [verbId, verb] of verbsMap.entries()) {
|
|
954
|
+
if (verb.targetId === params.to && verb.verb === params.type) {
|
|
955
|
+
// Relationship already exists - return existing ID instead of creating duplicate
|
|
956
|
+
console.log(`[DEBUG] Skipping duplicate relationship: ${params.from} → ${params.to} (${params.type})`);
|
|
957
|
+
return verb.id;
|
|
958
|
+
}
|
|
951
959
|
}
|
|
952
960
|
}
|
|
953
961
|
// No duplicate found - proceed with creation
|
|
@@ -1382,9 +1390,11 @@ export class Brainy {
|
|
|
1382
1390
|
const limit = params.limit || 10;
|
|
1383
1391
|
const offset = params.offset || 0;
|
|
1384
1392
|
const pageIds = filteredIds.slice(offset, offset + limit);
|
|
1385
|
-
//
|
|
1393
|
+
// v6.2.0: Batch-load entities for 10x faster cloud storage performance
|
|
1394
|
+
// GCS: 10 entities = 1×50ms vs 10×50ms = 500ms (10x faster)
|
|
1395
|
+
const entitiesMap = await this.batchGet(pageIds);
|
|
1386
1396
|
for (const id of pageIds) {
|
|
1387
|
-
const entity =
|
|
1397
|
+
const entity = entitiesMap.get(id);
|
|
1388
1398
|
if (entity) {
|
|
1389
1399
|
results.push(this.createResult(id, 1.0, entity));
|
|
1390
1400
|
}
|
|
@@ -1406,8 +1416,10 @@ export class Brainy {
|
|
|
1406
1416
|
if (Object.keys(filter).length > 0) {
|
|
1407
1417
|
const filteredIds = await this.metadataIndex.getIdsForFilter(filter);
|
|
1408
1418
|
const pageIds = filteredIds.slice(offset, offset + limit);
|
|
1419
|
+
// v6.2.0: Batch-load entities for 10x faster cloud storage performance
|
|
1420
|
+
const entitiesMap = await this.batchGet(pageIds);
|
|
1409
1421
|
for (const id of pageIds) {
|
|
1410
|
-
const entity =
|
|
1422
|
+
const entity = entitiesMap.get(id);
|
|
1411
1423
|
if (entity) {
|
|
1412
1424
|
results.push(this.createResult(id, 1.0, entity));
|
|
1413
1425
|
}
|
|
@@ -1499,12 +1511,16 @@ export class Brainy {
|
|
|
1499
1511
|
if (results.length >= offset + limit) {
|
|
1500
1512
|
results.sort((a, b) => b.score - a.score);
|
|
1501
1513
|
results = results.slice(offset, offset + limit);
|
|
1502
|
-
//
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1514
|
+
// v6.2.0: Batch-load entities only for the paginated results (10x faster on GCS)
|
|
1515
|
+
const idsToLoad = results.filter(r => !r.entity).map(r => r.id);
|
|
1516
|
+
if (idsToLoad.length > 0) {
|
|
1517
|
+
const entitiesMap = await this.batchGet(idsToLoad);
|
|
1518
|
+
for (const result of results) {
|
|
1519
|
+
if (!result.entity) {
|
|
1520
|
+
const entity = entitiesMap.get(result.id);
|
|
1521
|
+
if (entity) {
|
|
1522
|
+
result.entity = entity;
|
|
1523
|
+
}
|
|
1508
1524
|
}
|
|
1509
1525
|
}
|
|
1510
1526
|
}
|
|
@@ -1519,9 +1535,11 @@ export class Brainy {
|
|
|
1519
1535
|
const limit = params.limit || 10;
|
|
1520
1536
|
const offset = params.offset || 0;
|
|
1521
1537
|
const pageIds = filteredIds.slice(offset, offset + limit);
|
|
1522
|
-
//
|
|
1538
|
+
// v6.2.0: Batch-load entities for current page - O(page_size) instead of O(total_results)
|
|
1539
|
+
// GCS: 10 entities = 1×50ms vs 10×50ms = 500ms (10x faster)
|
|
1540
|
+
const entitiesMap = await this.batchGet(pageIds);
|
|
1523
1541
|
for (const id of pageIds) {
|
|
1524
|
-
const entity =
|
|
1542
|
+
const entity = entitiesMap.get(id);
|
|
1525
1543
|
if (entity) {
|
|
1526
1544
|
results.push(this.createResult(id, 1.0, entity));
|
|
1527
1545
|
}
|
|
@@ -1535,10 +1553,11 @@ export class Brainy {
|
|
|
1535
1553
|
const limit = params.limit || 10;
|
|
1536
1554
|
const offset = params.offset || 0;
|
|
1537
1555
|
const pageIds = sortedIds.slice(offset, offset + limit);
|
|
1538
|
-
//
|
|
1556
|
+
// v6.2.0: Batch-load entities for paginated results (10x faster on GCS)
|
|
1539
1557
|
const sortedResults = [];
|
|
1558
|
+
const entitiesMap = await this.batchGet(pageIds);
|
|
1540
1559
|
for (const id of pageIds) {
|
|
1541
|
-
const entity =
|
|
1560
|
+
const entity = entitiesMap.get(id);
|
|
1542
1561
|
if (entity) {
|
|
1543
1562
|
sortedResults.push(this.createResult(id, 1.0, entity));
|
|
1544
1563
|
}
|
|
@@ -1847,16 +1866,67 @@ export class Brainy {
|
|
|
1847
1866
|
duration: 0
|
|
1848
1867
|
};
|
|
1849
1868
|
const startTime = Date.now();
|
|
1850
|
-
for
|
|
1869
|
+
// v6.2.0: Batch deletes into chunks for 10x faster performance with proper error handling
|
|
1870
|
+
// Single transaction per chunk (10 entities) = atomic within chunk, graceful failure across chunks
|
|
1871
|
+
const chunkSize = 10;
|
|
1872
|
+
for (let i = 0; i < idsToDelete.length; i += chunkSize) {
|
|
1873
|
+
const chunk = idsToDelete.slice(i, i + chunkSize);
|
|
1851
1874
|
try {
|
|
1852
|
-
|
|
1853
|
-
|
|
1875
|
+
// Process chunk in single transaction for atomic deletion
|
|
1876
|
+
await this.transactionManager.executeTransaction(async (tx) => {
|
|
1877
|
+
for (const id of chunk) {
|
|
1878
|
+
try {
|
|
1879
|
+
// Load entity data
|
|
1880
|
+
const metadata = await this.storage.getNounMetadata(id);
|
|
1881
|
+
const noun = await this.storage.getNoun(id);
|
|
1882
|
+
const verbs = await this.storage.getVerbsBySource(id);
|
|
1883
|
+
const targetVerbs = await this.storage.getVerbsByTarget(id);
|
|
1884
|
+
const allVerbs = [...verbs, ...targetVerbs];
|
|
1885
|
+
// Add delete operations to transaction
|
|
1886
|
+
if (noun && metadata) {
|
|
1887
|
+
if (this.index instanceof TypeAwareHNSWIndex && metadata.noun) {
|
|
1888
|
+
tx.addOperation(new RemoveFromTypeAwareHNSWOperation(this.index, id, noun.vector, metadata.noun));
|
|
1889
|
+
}
|
|
1890
|
+
else if (this.index instanceof HNSWIndex || this.index instanceof HNSWIndexOptimized) {
|
|
1891
|
+
tx.addOperation(new RemoveFromHNSWOperation(this.index, id, noun.vector));
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
if (metadata) {
|
|
1895
|
+
tx.addOperation(new RemoveFromMetadataIndexOperation(this.metadataIndex, id, metadata));
|
|
1896
|
+
}
|
|
1897
|
+
tx.addOperation(new DeleteNounMetadataOperation(this.storage, id));
|
|
1898
|
+
for (const verb of allVerbs) {
|
|
1899
|
+
tx.addOperation(new RemoveFromGraphIndexOperation(this.graphIndex, verb));
|
|
1900
|
+
tx.addOperation(new DeleteVerbMetadataOperation(this.storage, verb.id));
|
|
1901
|
+
}
|
|
1902
|
+
result.successful.push(id);
|
|
1903
|
+
}
|
|
1904
|
+
catch (error) {
|
|
1905
|
+
result.failed.push({
|
|
1906
|
+
item: id,
|
|
1907
|
+
error: error.message
|
|
1908
|
+
});
|
|
1909
|
+
if (!params.continueOnError) {
|
|
1910
|
+
throw error;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1854
1915
|
}
|
|
1855
1916
|
catch (error) {
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1917
|
+
// Transaction failed - mark remaining entities in chunk as failed if not already recorded
|
|
1918
|
+
for (const id of chunk) {
|
|
1919
|
+
if (!result.successful.includes(id) && !result.failed.find(f => f.item === id)) {
|
|
1920
|
+
result.failed.push({
|
|
1921
|
+
item: id,
|
|
1922
|
+
error: error.message
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
// Stop processing if continueOnError is false
|
|
1927
|
+
if (!params.continueOnError) {
|
|
1928
|
+
break;
|
|
1929
|
+
}
|
|
1860
1930
|
}
|
|
1861
1931
|
if (params.onProgress) {
|
|
1862
1932
|
params.onProgress(result.successful.length + result.failed.length, result.total);
|
|
@@ -3341,7 +3411,39 @@ export class Brainy {
|
|
|
3341
3411
|
// O(1) total relationship count
|
|
3342
3412
|
relationships: () => this.graphIndex.getTotalRelationshipCount(),
|
|
3343
3413
|
// O(1) count by type (string-based, backward compatible)
|
|
3344
|
-
|
|
3414
|
+
// v6.2.1: Added optional excludeVFS using Roaring bitmap intersection
|
|
3415
|
+
byType: async (typeOrOptions, options) => {
|
|
3416
|
+
// Handle overloaded signature: byType(type), byType({ excludeVFS }), byType(type, { excludeVFS })
|
|
3417
|
+
let type;
|
|
3418
|
+
let excludeVFS = false;
|
|
3419
|
+
if (typeof typeOrOptions === 'string') {
|
|
3420
|
+
type = typeOrOptions;
|
|
3421
|
+
excludeVFS = options?.excludeVFS ?? false;
|
|
3422
|
+
}
|
|
3423
|
+
else if (typeOrOptions && typeof typeOrOptions === 'object') {
|
|
3424
|
+
excludeVFS = typeOrOptions.excludeVFS ?? false;
|
|
3425
|
+
}
|
|
3426
|
+
if (excludeVFS) {
|
|
3427
|
+
const allCounts = this.metadataIndex.getAllEntityCounts();
|
|
3428
|
+
// Uses Roaring bitmap intersection - hardware accelerated
|
|
3429
|
+
const vfsCounts = await this.metadataIndex.getAllVFSEntityCounts();
|
|
3430
|
+
if (type) {
|
|
3431
|
+
const total = allCounts.get(type) || 0;
|
|
3432
|
+
const vfs = vfsCounts.get(type) || 0;
|
|
3433
|
+
return total - vfs;
|
|
3434
|
+
}
|
|
3435
|
+
// Return all counts with VFS subtracted
|
|
3436
|
+
const result = {};
|
|
3437
|
+
for (const [t, total] of allCounts) {
|
|
3438
|
+
const vfs = vfsCounts.get(t) || 0;
|
|
3439
|
+
const nonVfs = total - vfs;
|
|
3440
|
+
if (nonVfs > 0) {
|
|
3441
|
+
result[t] = nonVfs;
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
return result;
|
|
3445
|
+
}
|
|
3446
|
+
// Default path (unchanged) - synchronous for backward compatibility
|
|
3345
3447
|
if (type) {
|
|
3346
3448
|
return this.metadataIndex.getEntityCountByType(type);
|
|
3347
3449
|
}
|
|
@@ -3383,7 +3485,32 @@ export class Brainy {
|
|
|
3383
3485
|
// Get all type counts as Map for performance-critical operations
|
|
3384
3486
|
getAllTypeCounts: () => this.metadataIndex.getAllEntityCounts(),
|
|
3385
3487
|
// Get complete statistics
|
|
3386
|
-
|
|
3488
|
+
// v6.2.1: Added optional excludeVFS using Roaring bitmap intersection
|
|
3489
|
+
getStats: async (options) => {
|
|
3490
|
+
if (options?.excludeVFS) {
|
|
3491
|
+
const allCounts = this.metadataIndex.getAllEntityCounts();
|
|
3492
|
+
// Uses Roaring bitmap intersection - hardware accelerated
|
|
3493
|
+
const vfsCounts = await this.metadataIndex.getAllVFSEntityCounts();
|
|
3494
|
+
// Compute non-VFS counts via subtraction
|
|
3495
|
+
const byType = {};
|
|
3496
|
+
let total = 0;
|
|
3497
|
+
for (const [type, count] of allCounts) {
|
|
3498
|
+
const vfs = vfsCounts.get(type) || 0;
|
|
3499
|
+
const nonVfs = count - vfs;
|
|
3500
|
+
if (nonVfs > 0) {
|
|
3501
|
+
byType[type] = nonVfs;
|
|
3502
|
+
total += nonVfs;
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
const entityStats = { total, byType };
|
|
3506
|
+
const relationshipStats = this.graphIndex.getRelationshipStats();
|
|
3507
|
+
return {
|
|
3508
|
+
entities: entityStats,
|
|
3509
|
+
relationships: relationshipStats,
|
|
3510
|
+
density: total > 0 ? relationshipStats.totalRelationships / total : 0
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3513
|
+
// Default path (unchanged) - synchronous for backward compatibility
|
|
3387
3514
|
const entityStats = {
|
|
3388
3515
|
total: this.metadataIndex.getTotalEntityCount(),
|
|
3389
3516
|
byType: Object.fromEntries(this.metadataIndex.getAllEntityCounts())
|
|
@@ -3410,10 +3537,12 @@ export class Brainy {
|
|
|
3410
3537
|
/**
|
|
3411
3538
|
* Get complete statistics - convenience method
|
|
3412
3539
|
* For more granular counting, use brain.counts API
|
|
3540
|
+
* v6.2.1: Added optional excludeVFS using Roaring bitmap intersection
|
|
3541
|
+
* @param options Optional settings - excludeVFS: filter out VFS entities
|
|
3413
3542
|
* @returns Complete statistics including entities, relationships, and density
|
|
3414
3543
|
*/
|
|
3415
|
-
getStats() {
|
|
3416
|
-
return this.counts.getStats();
|
|
3544
|
+
async getStats(options) {
|
|
3545
|
+
return this.counts.getStats(options);
|
|
3417
3546
|
}
|
|
3418
3547
|
// ============= HELPER METHODS =============
|
|
3419
3548
|
/**
|
|
@@ -3544,10 +3673,12 @@ export class Brainy {
|
|
|
3544
3673
|
const connectedIdSet = new Set(connectedIds);
|
|
3545
3674
|
return existingResults.filter(r => connectedIdSet.has(r.id));
|
|
3546
3675
|
}
|
|
3547
|
-
//
|
|
3676
|
+
// v6.2.0: Batch-load connected entities for 10x faster cloud storage performance
|
|
3677
|
+
// GCS: 20 entities = 1×50ms vs 20×50ms = 1000ms (20x faster)
|
|
3548
3678
|
const results = [];
|
|
3679
|
+
const entitiesMap = await this.batchGet(connectedIds);
|
|
3549
3680
|
for (const id of connectedIds) {
|
|
3550
|
-
const entity =
|
|
3681
|
+
const entity = entitiesMap.get(id);
|
|
3551
3682
|
if (entity) {
|
|
3552
3683
|
results.push(this.createResult(id, 1.0, entity));
|
|
3553
3684
|
}
|
package/dist/coreTypes.d.ts
CHANGED
|
@@ -632,6 +632,12 @@ export interface StorageAdapter {
|
|
|
632
632
|
* @returns Promise that resolves to the metadata or null if not found
|
|
633
633
|
*/
|
|
634
634
|
getNounMetadata(id: string): Promise<NounMetadata | null>;
|
|
635
|
+
/**
|
|
636
|
+
* Batch get multiple nouns with vectors (v6.2.0 - N+1 fix)
|
|
637
|
+
* @param ids Array of noun IDs to fetch
|
|
638
|
+
* @returns Map of id → HNSWNounWithMetadata (only successful reads included)
|
|
639
|
+
*/
|
|
640
|
+
getNounBatch?(ids: string[]): Promise<Map<string, HNSWNounWithMetadata>>;
|
|
635
641
|
/**
|
|
636
642
|
* Save verb metadata to storage (v4.0.0: now typed)
|
|
637
643
|
* @param id The ID of the verb
|
|
@@ -645,6 +651,12 @@ export interface StorageAdapter {
|
|
|
645
651
|
* @returns Promise that resolves to the metadata or null if not found
|
|
646
652
|
*/
|
|
647
653
|
getVerbMetadata(id: string): Promise<VerbMetadata | null>;
|
|
654
|
+
/**
|
|
655
|
+
* Batch get multiple verbs (v6.2.0 - N+1 fix)
|
|
656
|
+
* @param ids Array of verb IDs to fetch
|
|
657
|
+
* @returns Map of id → HNSWVerbWithMetadata (only successful reads included)
|
|
658
|
+
*/
|
|
659
|
+
getVerbsBatch?(ids: string[]): Promise<Map<string, HNSWVerbWithMetadata>>;
|
|
648
660
|
clear(): Promise<void>;
|
|
649
661
|
/**
|
|
650
662
|
* Batch delete multiple objects from storage (v4.0.0)
|
|
@@ -153,6 +153,29 @@ export declare class GraphAdjacencyIndex {
|
|
|
153
153
|
* @returns GraphVerb or null if not found
|
|
154
154
|
*/
|
|
155
155
|
getVerbCached(verbId: string): Promise<GraphVerb | null>;
|
|
156
|
+
/**
|
|
157
|
+
* Batch get multiple verbs with caching (v6.2.0 - N+1 fix)
|
|
158
|
+
*
|
|
159
|
+
* **Performance**: Eliminates N+1 pattern for verb loading
|
|
160
|
+
* - Current: N × getVerbCached() = N × 50ms on GCS = 250ms for 5 verbs
|
|
161
|
+
* - Batched: 1 × getVerbsBatchCached() = 1 × 50ms on GCS = 50ms (**5x faster**)
|
|
162
|
+
*
|
|
163
|
+
* **Use cases:**
|
|
164
|
+
* - relate() duplicate checking (check multiple existing relationships)
|
|
165
|
+
* - Loading relationship chains
|
|
166
|
+
* - Pre-loading verbs for analysis
|
|
167
|
+
*
|
|
168
|
+
* **Cache behavior:**
|
|
169
|
+
* - Checks UnifiedCache first (fast path)
|
|
170
|
+
* - Batch-loads uncached verbs from storage
|
|
171
|
+
* - Caches loaded verbs for future access
|
|
172
|
+
*
|
|
173
|
+
* @param verbIds Array of verb IDs to fetch
|
|
174
|
+
* @returns Map of verbId → GraphVerb (only successful reads included)
|
|
175
|
+
*
|
|
176
|
+
* @since v6.2.0
|
|
177
|
+
*/
|
|
178
|
+
getVerbsBatchCached(verbIds: string[]): Promise<Map<string, GraphVerb>>;
|
|
156
179
|
/**
|
|
157
180
|
* Get total relationship count - O(1) operation
|
|
158
181
|
*/
|
|
@@ -264,6 +264,55 @@ export class GraphAdjacencyIndex {
|
|
|
264
264
|
});
|
|
265
265
|
return verb;
|
|
266
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Batch get multiple verbs with caching (v6.2.0 - N+1 fix)
|
|
269
|
+
*
|
|
270
|
+
* **Performance**: Eliminates N+1 pattern for verb loading
|
|
271
|
+
* - Current: N × getVerbCached() = N × 50ms on GCS = 250ms for 5 verbs
|
|
272
|
+
* - Batched: 1 × getVerbsBatchCached() = 1 × 50ms on GCS = 50ms (**5x faster**)
|
|
273
|
+
*
|
|
274
|
+
* **Use cases:**
|
|
275
|
+
* - relate() duplicate checking (check multiple existing relationships)
|
|
276
|
+
* - Loading relationship chains
|
|
277
|
+
* - Pre-loading verbs for analysis
|
|
278
|
+
*
|
|
279
|
+
* **Cache behavior:**
|
|
280
|
+
* - Checks UnifiedCache first (fast path)
|
|
281
|
+
* - Batch-loads uncached verbs from storage
|
|
282
|
+
* - Caches loaded verbs for future access
|
|
283
|
+
*
|
|
284
|
+
* @param verbIds Array of verb IDs to fetch
|
|
285
|
+
* @returns Map of verbId → GraphVerb (only successful reads included)
|
|
286
|
+
*
|
|
287
|
+
* @since v6.2.0
|
|
288
|
+
*/
|
|
289
|
+
async getVerbsBatchCached(verbIds) {
|
|
290
|
+
const results = new Map();
|
|
291
|
+
const uncached = [];
|
|
292
|
+
// Phase 1: Check cache for each verb
|
|
293
|
+
for (const verbId of verbIds) {
|
|
294
|
+
const cacheKey = `graph:verb:${verbId}`;
|
|
295
|
+
const cached = this.unifiedCache.getSync(cacheKey);
|
|
296
|
+
if (cached) {
|
|
297
|
+
results.set(verbId, cached);
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
uncached.push(verbId);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Phase 2: Batch-load uncached verbs from storage
|
|
304
|
+
if (uncached.length > 0 && this.storage.getVerbsBatch) {
|
|
305
|
+
const loadedVerbs = await this.storage.getVerbsBatch(uncached);
|
|
306
|
+
for (const [verbId, verb] of loadedVerbs.entries()) {
|
|
307
|
+
const cacheKey = `graph:verb:${verbId}`;
|
|
308
|
+
// Cache the loaded verb with metadata
|
|
309
|
+
// Note: HNSWVerbWithMetadata is compatible with GraphVerb (both interfaces)
|
|
310
|
+
this.unifiedCache.set(cacheKey, verb, 'other', 128, 50); // 128 bytes estimated size, 50ms rebuild cost
|
|
311
|
+
results.set(verbId, verb);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return results;
|
|
315
|
+
}
|
|
267
316
|
/**
|
|
268
317
|
* Get total relationship count - O(1) operation
|
|
269
318
|
*/
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import Manager - Comprehensive data import with intelligent type detection
|
|
3
|
+
*
|
|
4
|
+
* Handles multiple data sources:
|
|
5
|
+
* - Direct data (objects, arrays)
|
|
6
|
+
* - Files (JSON, CSV, text)
|
|
7
|
+
* - URLs (fetch and parse)
|
|
8
|
+
* - Streams (for large files)
|
|
9
|
+
*
|
|
10
|
+
* Uses NeuralImportAugmentation for intelligent processing
|
|
11
|
+
*/
|
|
12
|
+
import { NounType } from './types/graphTypes.js';
|
|
13
|
+
export interface ImportOptions {
|
|
14
|
+
source?: 'data' | 'file' | 'url' | 'auto';
|
|
15
|
+
format?: 'json' | 'csv' | 'text' | 'yaml' | 'auto';
|
|
16
|
+
batchSize?: number;
|
|
17
|
+
autoDetect?: boolean;
|
|
18
|
+
typeHint?: NounType;
|
|
19
|
+
extractRelationships?: boolean;
|
|
20
|
+
csvDelimiter?: string;
|
|
21
|
+
csvHeaders?: boolean;
|
|
22
|
+
parallel?: boolean;
|
|
23
|
+
maxConcurrency?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface ImportResult {
|
|
26
|
+
success: boolean;
|
|
27
|
+
nouns: string[];
|
|
28
|
+
verbs: string[];
|
|
29
|
+
errors: string[];
|
|
30
|
+
stats: {
|
|
31
|
+
total: number;
|
|
32
|
+
imported: number;
|
|
33
|
+
failed: number;
|
|
34
|
+
relationships: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export declare class ImportManager {
|
|
38
|
+
private neuralImport;
|
|
39
|
+
private typeMatcher;
|
|
40
|
+
private brain;
|
|
41
|
+
constructor(brain: any);
|
|
42
|
+
/**
|
|
43
|
+
* Initialize the import manager
|
|
44
|
+
*/
|
|
45
|
+
init(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Main import method - handles all sources
|
|
48
|
+
*/
|
|
49
|
+
import(source: string | Buffer | any[] | any, options?: ImportOptions): Promise<ImportResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Import from file
|
|
52
|
+
*/
|
|
53
|
+
importFile(filePath: string, options?: ImportOptions): Promise<ImportResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Import from URL
|
|
56
|
+
*/
|
|
57
|
+
importUrl(url: string, options?: ImportOptions): Promise<ImportResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Detect source type
|
|
60
|
+
*/
|
|
61
|
+
private detectSourceType;
|
|
62
|
+
/**
|
|
63
|
+
* Detect format from file path
|
|
64
|
+
*/
|
|
65
|
+
private detectFormatFromPath;
|
|
66
|
+
/**
|
|
67
|
+
* Read file
|
|
68
|
+
*/
|
|
69
|
+
private readFile;
|
|
70
|
+
/**
|
|
71
|
+
* Fetch from URL
|
|
72
|
+
*/
|
|
73
|
+
private fetchFromUrl;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create an import manager instance
|
|
77
|
+
*/
|
|
78
|
+
export declare function createImportManager(brain: any): ImportManager;
|