@semiont/graph 0.2.30 → 0.2.31
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/README.md +5 -1
- package/dist/index.js +80 -46
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# @semiont/graph
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@semiont/graph)
|
|
4
3
|
[](https://github.com/The-AI-Alliance/semiont/actions/workflows/package-tests.yml?query=branch%3Amain+is%3Asuccess+job%3A%22Test+graph%22)
|
|
4
|
+
[](https://codecov.io/gh/The-AI-Alliance/semiont?flag=graph)
|
|
5
|
+
[](https://www.npmjs.com/package/@semiont/graph)
|
|
6
|
+
[](https://www.npmjs.com/package/@semiont/graph)
|
|
7
|
+
[](https://github.com/The-AI-Alliance/semiont/blob/main/LICENSE)
|
|
5
8
|
|
|
6
9
|
Graph database abstraction with Neo4j, Neptune, JanusGraph, and in-memory implementations.
|
|
7
10
|
|
|
@@ -84,6 +87,7 @@ const annotations = await graph.getAnnotationsForDocument('doc-123');
|
|
|
84
87
|
|
|
85
88
|
- [API Reference](./docs/API.md) - Complete API documentation
|
|
86
89
|
- [Architecture](./docs/ARCHITECTURE.md) - System design and principles
|
|
90
|
+
- [Eventual Consistency](./docs/EVENTUAL-CONSISTENCY.md) - Order-independent projections and race condition handling
|
|
87
91
|
- [Provider Guide](./docs/PROVIDERS.md) - Provider-specific details
|
|
88
92
|
|
|
89
93
|
## Examples
|
package/dist/index.js
CHANGED
|
@@ -880,19 +880,19 @@ var Neo4jGraphDatabase = class {
|
|
|
880
880
|
throw new Error("Resource must have at least one representation");
|
|
881
881
|
}
|
|
882
882
|
const result = await session.run(
|
|
883
|
-
`
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
883
|
+
`MERGE (d:Resource {id: $id})
|
|
884
|
+
SET d.name = $name,
|
|
885
|
+
d.entityTypes = $entityTypes,
|
|
886
|
+
d.format = $format,
|
|
887
|
+
d.archived = $archived,
|
|
888
|
+
d.created = datetime($created),
|
|
889
|
+
d.creator = $creator,
|
|
890
|
+
d.creationMethod = $creationMethod,
|
|
891
|
+
d.contentChecksum = $contentChecksum,
|
|
892
|
+
d.sourceAnnotationId = $sourceAnnotationId,
|
|
893
|
+
d.sourceResourceId = $sourceResourceId,
|
|
894
|
+
d.stub = false
|
|
895
|
+
RETURN d`,
|
|
896
896
|
{
|
|
897
897
|
id,
|
|
898
898
|
name: resource.name,
|
|
@@ -907,6 +907,7 @@ var Neo4jGraphDatabase = class {
|
|
|
907
907
|
sourceResourceId: resource.sourceResourceId ?? null
|
|
908
908
|
}
|
|
909
909
|
);
|
|
910
|
+
console.log(`[Neo4j] Resource created/enriched: ${id}`);
|
|
910
911
|
return this.parseResourceNode(result.records[0].get("d"));
|
|
911
912
|
} finally {
|
|
912
913
|
await session.close();
|
|
@@ -1189,18 +1190,24 @@ var Neo4jGraphDatabase = class {
|
|
|
1189
1190
|
console.log(`[Neo4j] \u2705 Creating REFERENCES edge: ${id} -> ${specificResource.source}`);
|
|
1190
1191
|
const refResult = await session.run(
|
|
1191
1192
|
`MATCH (a:Annotation {id: $annotationId})
|
|
1192
|
-
|
|
1193
|
+
MERGE (target:Resource {id: $targetResourceId})
|
|
1194
|
+
ON CREATE SET target.stub = true
|
|
1193
1195
|
MERGE (a)-[:REFERENCES]->(target)
|
|
1194
|
-
RETURN a, target`,
|
|
1196
|
+
RETURN a, target, target.stub AS wasStub`,
|
|
1195
1197
|
{
|
|
1196
1198
|
annotationId: id,
|
|
1197
1199
|
targetResourceId: specificResource.source
|
|
1198
1200
|
}
|
|
1199
1201
|
);
|
|
1200
|
-
console.log(`[Neo4j] \u2705 REFERENCES edge created! Matched ${refResult.records.length} nodes`);
|
|
1201
1202
|
if (refResult.records.length > 0) {
|
|
1202
|
-
|
|
1203
|
-
|
|
1203
|
+
const wasStub = refResult.records[0].get("wasStub");
|
|
1204
|
+
if (wasStub) {
|
|
1205
|
+
console.log(`[Neo4j] \u2705 REFERENCES edge created with stub node for ${specificResource.source} (will be enriched by resource.created event)`);
|
|
1206
|
+
} else {
|
|
1207
|
+
console.log(`[Neo4j] \u2705 REFERENCES edge created to existing resource ${specificResource.source}`);
|
|
1208
|
+
}
|
|
1209
|
+
} else {
|
|
1210
|
+
console.log(`[Neo4j] \u26A0\uFE0F REFERENCES edge creation returned no records`);
|
|
1204
1211
|
}
|
|
1205
1212
|
} else {
|
|
1206
1213
|
console.log(`[Neo4j] No SpecificResource in body - this is a stub reference (not yet resolved)`);
|
|
@@ -2258,7 +2265,7 @@ var JanusGraphDatabase = class {
|
|
|
2258
2265
|
// src/implementations/memorygraph.ts
|
|
2259
2266
|
import { getResourceEntityTypes } from "@semiont/api-client";
|
|
2260
2267
|
import { getEntityTypes as getEntityTypes4 } from "@semiont/ontology";
|
|
2261
|
-
import { resourceId as makeResourceId } from "@semiont/core";
|
|
2268
|
+
import { resourceId as makeResourceId, uriToResourceId } from "@semiont/core";
|
|
2262
2269
|
import { resourceUri as resourceUri2 } from "@semiont/api-client";
|
|
2263
2270
|
import { v4 as uuidv44 } from "uuid";
|
|
2264
2271
|
import { getBodySource as getBodySource4, getTargetSource as getTargetSource3 } from "@semiont/api-client";
|
|
@@ -2290,19 +2297,28 @@ var MemoryGraphDatabase = class {
|
|
|
2290
2297
|
return resource;
|
|
2291
2298
|
}
|
|
2292
2299
|
async getResource(id) {
|
|
2293
|
-
|
|
2300
|
+
try {
|
|
2301
|
+
const resourceId = uriToResourceId(id);
|
|
2302
|
+
return this.resources.get(resourceId) || null;
|
|
2303
|
+
} catch {
|
|
2304
|
+
return null;
|
|
2305
|
+
}
|
|
2294
2306
|
}
|
|
2295
2307
|
async updateResource(id, input) {
|
|
2296
2308
|
if (Object.keys(input).length !== 1 || input.archived === void 0) {
|
|
2297
2309
|
throw new Error("Resources are immutable. Only archiving is allowed.");
|
|
2298
2310
|
}
|
|
2299
|
-
const
|
|
2311
|
+
const resourceId = uriToResourceId(id);
|
|
2312
|
+
const doc = resourceId ? this.resources.get(resourceId) : null;
|
|
2300
2313
|
if (!doc) throw new Error("Resource not found");
|
|
2301
2314
|
doc.archived = input.archived;
|
|
2302
2315
|
return doc;
|
|
2303
2316
|
}
|
|
2304
2317
|
async deleteResource(id) {
|
|
2305
|
-
|
|
2318
|
+
const resourceId = uriToResourceId(id);
|
|
2319
|
+
if (resourceId) {
|
|
2320
|
+
this.resources.delete(resourceId);
|
|
2321
|
+
}
|
|
2306
2322
|
for (const [selId, sel] of this.annotations) {
|
|
2307
2323
|
if (getTargetSource3(sel.target) === id || getBodySource4(sel.body) === id) {
|
|
2308
2324
|
this.annotations.delete(selId);
|
|
@@ -2376,7 +2392,8 @@ var MemoryGraphDatabase = class {
|
|
|
2376
2392
|
const resourceIdStr = String(filter.resourceId);
|
|
2377
2393
|
results = results.filter((a) => {
|
|
2378
2394
|
const targetSource = getTargetSource3(a.target);
|
|
2379
|
-
|
|
2395
|
+
const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
|
|
2396
|
+
return targetResourceId === resourceIdStr;
|
|
2380
2397
|
});
|
|
2381
2398
|
}
|
|
2382
2399
|
if (filter.type) {
|
|
@@ -2389,7 +2406,8 @@ var MemoryGraphDatabase = class {
|
|
|
2389
2406
|
const resourceIdStr = String(resourceId);
|
|
2390
2407
|
const highlights = Array.from(this.annotations.values()).filter((sel) => {
|
|
2391
2408
|
const targetSource = getTargetSource3(sel.target);
|
|
2392
|
-
|
|
2409
|
+
const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
|
|
2410
|
+
return targetResourceId === resourceIdStr && sel.motivation === "highlighting";
|
|
2393
2411
|
});
|
|
2394
2412
|
console.log(`Memory: getHighlights for ${resourceId} found ${highlights.length} highlights`);
|
|
2395
2413
|
return highlights;
|
|
@@ -2397,11 +2415,13 @@ var MemoryGraphDatabase = class {
|
|
|
2397
2415
|
async resolveReference(annotationId, source) {
|
|
2398
2416
|
const annotation = this.annotations.get(annotationId);
|
|
2399
2417
|
if (!annotation) throw new Error("Annotation not found");
|
|
2418
|
+
const sourceResource = this.resources.get(String(source));
|
|
2419
|
+
if (!sourceResource) throw new Error("Source resource not found");
|
|
2400
2420
|
const updated = {
|
|
2401
2421
|
...annotation,
|
|
2402
2422
|
body: {
|
|
2403
2423
|
type: "SpecificResource",
|
|
2404
|
-
source,
|
|
2424
|
+
source: sourceResource["@id"],
|
|
2405
2425
|
purpose: "linking"
|
|
2406
2426
|
}
|
|
2407
2427
|
};
|
|
@@ -2412,7 +2432,8 @@ var MemoryGraphDatabase = class {
|
|
|
2412
2432
|
const resourceIdStr = String(resourceId);
|
|
2413
2433
|
const references = Array.from(this.annotations.values()).filter((sel) => {
|
|
2414
2434
|
const targetSource = getTargetSource3(sel.target);
|
|
2415
|
-
|
|
2435
|
+
const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
|
|
2436
|
+
return targetResourceId === resourceIdStr && sel.motivation === "linking";
|
|
2416
2437
|
});
|
|
2417
2438
|
console.log(`Memory: getReferences for ${resourceId} found ${references.length} references`);
|
|
2418
2439
|
references.forEach((ref) => {
|
|
@@ -2428,14 +2449,16 @@ var MemoryGraphDatabase = class {
|
|
|
2428
2449
|
async getEntityReferences(resourceId, entityTypes) {
|
|
2429
2450
|
const resourceIdStr = String(resourceId);
|
|
2430
2451
|
let refs = Array.from(this.annotations.values()).filter((sel) => {
|
|
2431
|
-
const selEntityTypes = getEntityTypes4(sel);
|
|
2432
2452
|
const targetSource = getTargetSource3(sel.target);
|
|
2433
|
-
|
|
2453
|
+
const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
|
|
2454
|
+
const bodyEntityTypes = sel.body?.entityTypes;
|
|
2455
|
+
const hasEntityTypes = Array.isArray(bodyEntityTypes) && bodyEntityTypes.length > 0;
|
|
2456
|
+
return targetResourceId === resourceIdStr && hasEntityTypes;
|
|
2434
2457
|
});
|
|
2435
2458
|
if (entityTypes && entityTypes.length > 0) {
|
|
2436
2459
|
refs = refs.filter((sel) => {
|
|
2437
|
-
const
|
|
2438
|
-
return
|
|
2460
|
+
const bodyEntityTypes = sel.body?.entityTypes || [];
|
|
2461
|
+
return bodyEntityTypes.some((type) => entityTypes.includes(type));
|
|
2439
2462
|
});
|
|
2440
2463
|
}
|
|
2441
2464
|
return refs;
|
|
@@ -2444,7 +2467,8 @@ var MemoryGraphDatabase = class {
|
|
|
2444
2467
|
const resourceIdStr = String(resourceId);
|
|
2445
2468
|
return Array.from(this.annotations.values()).filter((sel) => {
|
|
2446
2469
|
const targetSource = getTargetSource3(sel.target);
|
|
2447
|
-
|
|
2470
|
+
const targetResourceId = targetSource ? uriToResourceId(targetSource) : null;
|
|
2471
|
+
return targetResourceId === resourceIdStr;
|
|
2448
2472
|
});
|
|
2449
2473
|
}
|
|
2450
2474
|
async getResourceReferencedBy(resourceUri3, _motivation) {
|
|
@@ -2458,34 +2482,41 @@ var MemoryGraphDatabase = class {
|
|
|
2458
2482
|
if (bodySource) {
|
|
2459
2483
|
const targetDoc = await this.getResource(resourceUri2(bodySource));
|
|
2460
2484
|
if (targetDoc) {
|
|
2461
|
-
const
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
const
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2485
|
+
const bodySourceId = uriToResourceId(bodySource);
|
|
2486
|
+
if (bodySourceId) {
|
|
2487
|
+
const reverseRefs = await this.getReferences(makeResourceId(bodySourceId));
|
|
2488
|
+
const resourceIdStr = String(resourceId);
|
|
2489
|
+
const bidirectional = reverseRefs.some((r) => {
|
|
2490
|
+
const reverseBodySource = getBodySource4(r.body);
|
|
2491
|
+
const reverseBodyResourceId = reverseBodySource ? uriToResourceId(reverseBodySource) : null;
|
|
2492
|
+
return reverseBodyResourceId === resourceIdStr;
|
|
2493
|
+
});
|
|
2494
|
+
connections.push({
|
|
2495
|
+
targetResource: targetDoc,
|
|
2496
|
+
annotations: [ref],
|
|
2497
|
+
bidirectional
|
|
2498
|
+
});
|
|
2499
|
+
}
|
|
2472
2500
|
}
|
|
2473
2501
|
}
|
|
2474
2502
|
}
|
|
2475
2503
|
return connections;
|
|
2476
2504
|
}
|
|
2477
2505
|
async findPath(fromResourceId, toResourceId, maxDepth = 5) {
|
|
2506
|
+
const fromUuid = uriToResourceId(fromResourceId);
|
|
2507
|
+
const toUuid = uriToResourceId(toResourceId);
|
|
2508
|
+
if (!fromUuid || !toUuid) return [];
|
|
2478
2509
|
const visited = /* @__PURE__ */ new Set();
|
|
2479
2510
|
const queue = [];
|
|
2480
2511
|
const fromDoc = await this.getResource(resourceUri2(fromResourceId));
|
|
2481
2512
|
if (!fromDoc) return [];
|
|
2482
|
-
queue.push({ docId:
|
|
2483
|
-
visited.add(
|
|
2513
|
+
queue.push({ docId: fromUuid, path: [fromDoc], sels: [] });
|
|
2514
|
+
visited.add(fromUuid);
|
|
2484
2515
|
const paths = [];
|
|
2485
2516
|
while (queue.length > 0 && paths.length < 10) {
|
|
2486
2517
|
const { docId, path, sels } = queue.shift();
|
|
2487
2518
|
if (path.length > maxDepth) continue;
|
|
2488
|
-
if (docId ===
|
|
2519
|
+
if (docId === toUuid) {
|
|
2489
2520
|
paths.push({ resources: path, annotations: sels });
|
|
2490
2521
|
continue;
|
|
2491
2522
|
}
|
|
@@ -2532,7 +2563,10 @@ var MemoryGraphDatabase = class {
|
|
|
2532
2563
|
const annotations = Array.from(this.annotations.values());
|
|
2533
2564
|
const highlightCount = annotations.filter((a) => a.motivation === "highlighting").length;
|
|
2534
2565
|
const referenceCount = annotations.filter((a) => a.motivation === "linking").length;
|
|
2535
|
-
const entityReferenceCount = annotations.filter((a) =>
|
|
2566
|
+
const entityReferenceCount = annotations.filter((a) => {
|
|
2567
|
+
const bodyEntityTypes = a.body?.entityTypes;
|
|
2568
|
+
return a.motivation === "linking" && Array.isArray(bodyEntityTypes) && bodyEntityTypes.length > 0;
|
|
2569
|
+
}).length;
|
|
2536
2570
|
return {
|
|
2537
2571
|
resourceCount: this.resources.size,
|
|
2538
2572
|
annotationCount: this.annotations.size,
|