@oml/owl 0.19.2 → 0.20.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/out/index.d.ts +3 -0
- package/out/index.js +3 -0
- package/out/index.js.map +1 -1
- package/out/owl/owl-abox.d.ts +16 -5
- package/out/owl/owl-abox.js +365 -188
- package/out/owl/owl-abox.js.map +1 -1
- package/out/owl/owl-imports.js +4 -2
- package/out/owl/owl-imports.js.map +1 -1
- package/out/owl/owl-interfaces.d.ts +26 -1
- package/out/owl/owl-mapper.d.ts +2 -0
- package/out/owl/owl-mapper.js +77 -38
- package/out/owl/owl-mapper.js.map +1 -1
- package/out/owl/owl-service.d.ts +4 -0
- package/out/owl/owl-service.js +139 -44
- package/out/owl/owl-service.js.map +1 -1
- package/out/owl/owl-shacl.d.ts +8 -9
- package/out/owl/owl-shacl.js +43 -117
- package/out/owl/owl-shacl.js.map +1 -1
- package/out/owl/owl-sparql-engine.d.ts +6 -0
- package/out/owl/owl-sparql-engine.js +11 -0
- package/out/owl/owl-sparql-engine.js.map +1 -0
- package/out/owl/owl-sparql-oxigraph.d.ts +34 -0
- package/out/owl/owl-sparql-oxigraph.js +436 -0
- package/out/owl/owl-sparql-oxigraph.js.map +1 -0
- package/out/owl/owl-sparql.d.ts +0 -44
- package/out/owl/owl-sparql.js +0 -320
- package/out/owl/owl-sparql.js.map +1 -1
- package/out/owl/owl-store-lean.d.ts +29 -0
- package/out/owl/owl-store-lean.js +445 -0
- package/out/owl/owl-store-lean.js.map +1 -0
- package/out/owl/owl-store.d.ts +11 -1
- package/out/owl/owl-store.js +80 -6
- package/out/owl/owl-store.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +3 -0
- package/src/owl/owl-abox.ts +401 -200
- package/src/owl/owl-imports.ts +4 -2
- package/src/owl/owl-interfaces.ts +28 -1
- package/src/owl/owl-mapper.ts +79 -41
- package/src/owl/owl-service.ts +149 -49
- package/src/owl/owl-shacl.ts +55 -132
- package/src/owl/owl-sparql-engine.ts +34 -0
- package/src/owl/owl-sparql-oxigraph.ts +527 -0
- package/src/owl/owl-sparql.ts +3 -366
- package/src/owl/owl-store-lean.ts +438 -0
- package/src/owl/owl-store.ts +86 -7
package/out/owl/owl-abox.js
CHANGED
|
@@ -70,41 +70,48 @@ export class ABoxEntailmentCache {
|
|
|
70
70
|
export class ABoxChainer {
|
|
71
71
|
constructor(reasoningStore) {
|
|
72
72
|
this.reasoningStore = reasoningStore;
|
|
73
|
+
// Interned terms for rule pattern constants (NamedNode/Literal). Pattern constants are
|
|
74
|
+
// immutable and recur across every fact match, so re-creating them per call was a top
|
|
75
|
+
// allocator; equality elsewhere compares by value/id, so identity reuse is safe.
|
|
76
|
+
this.termCache = new Map();
|
|
77
|
+
this.ruleAnchorIndexCache = new WeakMap();
|
|
73
78
|
}
|
|
74
79
|
chain(modelUri, tboxIndex, readModelUris, deltaPlus, deltaMinus) {
|
|
75
|
-
const scope = this.resolveScope(modelUri, readModelUris);
|
|
76
|
-
const store = this.reasoningStore.getStore();
|
|
77
|
-
const index = this.buildIndex(scope.facts);
|
|
78
80
|
const plusFacts = deltaPlus.map((q) => this.asFact(q));
|
|
79
81
|
const minusFacts = deltaMinus.map((q) => this.asFact(q));
|
|
80
82
|
const fullRecompute = plusFacts.length === 0 && minusFacts.length === 0;
|
|
83
|
+
const scope = this.resolveScope(modelUri, readModelUris, tboxIndex, !fullRecompute);
|
|
84
|
+
const index = scope.index;
|
|
81
85
|
let iterations = 0;
|
|
82
86
|
let newQuadsCount = 0;
|
|
83
87
|
if (fullRecompute) {
|
|
84
88
|
for (const key of [...scope.ownEntKeys]) {
|
|
85
89
|
const fact = scope.facts.get(key);
|
|
86
90
|
if (fact) {
|
|
87
|
-
this.removeOwnEntailedFact(scope, index,
|
|
91
|
+
this.removeOwnEntailedFact(scope, index, fact);
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
|
-
const over = fullRecompute ? [] : this.overdelete(scope, index, tboxIndex, minusFacts
|
|
95
|
+
const over = fullRecompute ? [] : this.overdelete(scope, index, tboxIndex, minusFacts);
|
|
92
96
|
const seed = fullRecompute ? scope.ownBaseFacts : [...plusFacts, ...over];
|
|
93
|
-
const result = this.materializeAdd(scope, index, tboxIndex, seed,
|
|
97
|
+
const result = this.materializeAdd(scope, index, tboxIndex, seed, !fullRecompute);
|
|
94
98
|
iterations += result.iterations;
|
|
95
99
|
newQuadsCount += result.newQuads;
|
|
100
|
+
const validationWarnings = this.collectValidationWarnings(index, tboxIndex);
|
|
96
101
|
return {
|
|
97
102
|
complete: true,
|
|
98
103
|
iterations,
|
|
99
104
|
newQuadsCount,
|
|
100
|
-
validationWarnings
|
|
105
|
+
validationWarnings,
|
|
101
106
|
};
|
|
102
107
|
}
|
|
103
|
-
overdelete(scope, index, tbox, minusFacts
|
|
108
|
+
overdelete(scope, index, tbox, minusFacts) {
|
|
104
109
|
const queue = [...minusFacts];
|
|
110
|
+
let cursor = 0;
|
|
105
111
|
const overByKey = new Map();
|
|
106
|
-
while (queue.length
|
|
107
|
-
const current = queue
|
|
112
|
+
while (cursor < queue.length) {
|
|
113
|
+
const current = queue[cursor];
|
|
114
|
+
cursor += 1;
|
|
108
115
|
if (!current) {
|
|
109
116
|
continue;
|
|
110
117
|
}
|
|
@@ -113,14 +120,14 @@ export class ABoxChainer {
|
|
|
113
120
|
if (!scope.ownEntKeys.has(key)) {
|
|
114
121
|
continue;
|
|
115
122
|
}
|
|
116
|
-
this.removeOwnEntailedFact(scope, index,
|
|
123
|
+
this.removeOwnEntailedFact(scope, index, head);
|
|
117
124
|
overByKey.set(key, head);
|
|
118
125
|
queue.push(head);
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
128
|
return [...overByKey.values()];
|
|
122
129
|
}
|
|
123
|
-
materializeAdd(scope, index, tbox, seedFacts,
|
|
130
|
+
materializeAdd(scope, index, tbox, seedFacts, checkSupport) {
|
|
124
131
|
const queue = [];
|
|
125
132
|
const queued = new Set();
|
|
126
133
|
let iterations = 0;
|
|
@@ -139,24 +146,38 @@ export class ABoxChainer {
|
|
|
139
146
|
enqueue(seed);
|
|
140
147
|
continue;
|
|
141
148
|
}
|
|
142
|
-
if (!this.canInsertEntailedFact(scope, seed)
|
|
149
|
+
if (!this.canInsertEntailedFact(scope, seed)) {
|
|
143
150
|
continue;
|
|
144
151
|
}
|
|
145
|
-
|
|
152
|
+
if (checkSupport) {
|
|
153
|
+
const supported = this.hasSupport(seed, index, tbox);
|
|
154
|
+
if (!supported) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
this.insertOwnEntailedFact(scope, index, seed);
|
|
146
159
|
newQuads += 1;
|
|
147
160
|
enqueue(seed);
|
|
148
161
|
}
|
|
149
|
-
|
|
150
|
-
|
|
162
|
+
let cursor = 0;
|
|
163
|
+
while (cursor < queue.length) {
|
|
164
|
+
const fact = queue[cursor];
|
|
165
|
+
cursor += 1;
|
|
151
166
|
if (!fact) {
|
|
152
167
|
continue;
|
|
153
168
|
}
|
|
154
169
|
iterations += 1;
|
|
155
170
|
for (const head of this.forwardRules(fact, index, tbox)) {
|
|
156
|
-
if (!this.canInsertEntailedFact(scope, head)
|
|
171
|
+
if (!this.canInsertEntailedFact(scope, head)) {
|
|
157
172
|
continue;
|
|
158
173
|
}
|
|
159
|
-
|
|
174
|
+
if (checkSupport) {
|
|
175
|
+
const supported = this.hasSupport(head, index, tbox);
|
|
176
|
+
if (!supported) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
this.insertOwnEntailedFact(scope, index, head);
|
|
160
181
|
newQuads += 1;
|
|
161
182
|
enqueue(head);
|
|
162
183
|
}
|
|
@@ -200,6 +221,7 @@ export class ABoxChainer {
|
|
|
200
221
|
heads.push(this.edgeFact(y, p, x));
|
|
201
222
|
}
|
|
202
223
|
if (this.isTransitiveProperty(tbox, p)) {
|
|
224
|
+
this.ensureWorkingIndex(index);
|
|
203
225
|
for (const next of this.getEdgeFacts(index, y, p)) {
|
|
204
226
|
heads.push(this.edgeFact(x, p, next.object));
|
|
205
227
|
}
|
|
@@ -215,6 +237,7 @@ export class ABoxChainer {
|
|
|
215
237
|
return this.forwardRules(fact, index, tbox);
|
|
216
238
|
}
|
|
217
239
|
hasSupport(fact, index, tbox) {
|
|
240
|
+
this.ensureWorkingIndex(index);
|
|
218
241
|
if (index.facts.has(this.factKey(fact))) {
|
|
219
242
|
return true;
|
|
220
243
|
}
|
|
@@ -303,133 +326,215 @@ export class ABoxChainer {
|
|
|
303
326
|
}
|
|
304
327
|
applyForwardRulesTriggeredByFact(fact, index, tbox) {
|
|
305
328
|
const heads = [];
|
|
329
|
+
const anchors = this.forwardRuleAnchorsForFact(fact, tbox);
|
|
330
|
+
for (const anchor of anchors) {
|
|
331
|
+
const bindings = new Map();
|
|
332
|
+
if (this.matchAtomToFactMut(anchor.atom, fact, bindings) === null) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
this.ensureWorkingIndex(index);
|
|
336
|
+
for (const resolved of this.resolveRuleBody(anchor.rule, index, bindings, anchor.anchorIndex)) {
|
|
337
|
+
for (const head of anchor.head) {
|
|
338
|
+
const inferred = this.instantiateRuleAtom(head, resolved);
|
|
339
|
+
if (inferred) {
|
|
340
|
+
heads.push(inferred);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return this.uniqueFacts(heads);
|
|
346
|
+
}
|
|
347
|
+
forwardRuleAnchorsForFact(fact, tbox) {
|
|
348
|
+
if (tbox.forwardRules.length === 0) {
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
const anchorIndex = this.getForwardRuleAnchorIndex(tbox);
|
|
352
|
+
if (this.isTypeFact(fact)) {
|
|
353
|
+
return anchorIndex.byClass.get(fact.object.value) ?? [];
|
|
354
|
+
}
|
|
355
|
+
return anchorIndex.byProperty.get(fact.predicate.value) ?? [];
|
|
356
|
+
}
|
|
357
|
+
getForwardRuleAnchorIndex(tbox) {
|
|
358
|
+
const cached = this.ruleAnchorIndexCache.get(tbox);
|
|
359
|
+
if (cached) {
|
|
360
|
+
return cached;
|
|
361
|
+
}
|
|
362
|
+
const created = {
|
|
363
|
+
byClass: new Map(),
|
|
364
|
+
byProperty: new Map(),
|
|
365
|
+
};
|
|
306
366
|
for (const rule of tbox.forwardRules) {
|
|
307
367
|
rule.body.forEach((atom, anchorIndex) => {
|
|
308
|
-
const
|
|
309
|
-
if (
|
|
310
|
-
|
|
368
|
+
const anchor = { rule: rule.body, head: rule.head, atom, anchorIndex };
|
|
369
|
+
if (atom.kind === 'class') {
|
|
370
|
+
const anchors = created.byClass.get(atom.classIri) ?? [];
|
|
371
|
+
anchors.push(anchor);
|
|
372
|
+
created.byClass.set(atom.classIri, anchors);
|
|
311
373
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
heads.push(inferred);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
374
|
+
else {
|
|
375
|
+
const anchors = created.byProperty.get(atom.propertyIri) ?? [];
|
|
376
|
+
anchors.push(anchor);
|
|
377
|
+
created.byProperty.set(atom.propertyIri, anchors);
|
|
319
378
|
}
|
|
320
379
|
});
|
|
321
380
|
}
|
|
322
|
-
|
|
381
|
+
this.ruleAnchorIndexCache.set(tbox, created);
|
|
382
|
+
return created;
|
|
323
383
|
}
|
|
324
384
|
hasForwardRuleSupport(fact, index, tbox) {
|
|
325
385
|
for (const rule of tbox.forwardRules) {
|
|
326
386
|
for (const head of rule.head) {
|
|
327
|
-
const bindings =
|
|
328
|
-
if (
|
|
387
|
+
const bindings = new Map();
|
|
388
|
+
if (this.matchAtomToFactMut(head, fact, bindings) === null) {
|
|
329
389
|
continue;
|
|
330
390
|
}
|
|
331
|
-
const
|
|
332
|
-
if (resolved.length > 0) {
|
|
391
|
+
for (const _resolved of this.resolveRuleBody(rule.body, index, bindings, -1)) {
|
|
333
392
|
return true;
|
|
334
393
|
}
|
|
335
394
|
}
|
|
336
395
|
}
|
|
337
396
|
return false;
|
|
338
397
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
for (const nextBindings of matches) {
|
|
351
|
-
resolved.push(...this.resolvePendingAtoms(rest, index, nextBindings));
|
|
398
|
+
// Join the (non-anchor) rule body atoms against the working index, yielding each complete
|
|
399
|
+
// variable binding. The bindings Map is mutated in place and backtracked, and yielded by
|
|
400
|
+
// reference: consumers must read the binding fully before requesting the next one (they do —
|
|
401
|
+
// they instantiate the rule head synchronously). This avoids the per-binding Map copies that
|
|
402
|
+
// dominated allocation. Semantics are identical to the prior copy-based cartesian expansion.
|
|
403
|
+
*resolveRuleBody(body, index, bindings, satisfiedIndex) {
|
|
404
|
+
const pending = [];
|
|
405
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
406
|
+
if (i !== satisfiedIndex) {
|
|
407
|
+
pending.push(body[i]);
|
|
408
|
+
}
|
|
352
409
|
}
|
|
353
|
-
|
|
410
|
+
yield* this.resolvePending(pending, 0, index, bindings);
|
|
354
411
|
}
|
|
355
|
-
|
|
356
|
-
|
|
412
|
+
*resolvePending(pending, pos, index, bindings) {
|
|
413
|
+
if (pos === pending.length) {
|
|
414
|
+
yield bindings;
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const atom = pending[pos];
|
|
357
418
|
for (const fact of this.candidateFacts(atom, index, bindings)) {
|
|
358
|
-
const
|
|
359
|
-
if (
|
|
360
|
-
|
|
419
|
+
const added = this.matchAtomToFactMut(atom, fact, bindings);
|
|
420
|
+
if (added === null) {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
yield* this.resolvePending(pending, pos + 1, index, bindings);
|
|
424
|
+
for (const key of added) {
|
|
425
|
+
bindings.delete(key);
|
|
361
426
|
}
|
|
362
427
|
}
|
|
363
|
-
return matches;
|
|
364
428
|
}
|
|
365
|
-
candidateFacts(atom, index, bindings) {
|
|
429
|
+
*candidateFacts(atom, index, bindings) {
|
|
366
430
|
if (atom.kind === 'class') {
|
|
367
431
|
const bound = this.resolvePatternTerm(atom.argument, bindings);
|
|
368
432
|
if (bound) {
|
|
369
|
-
|
|
433
|
+
for (const classIri of this.getTypes(index, bound)) {
|
|
434
|
+
yield this.typeFact(bound, classIri);
|
|
435
|
+
}
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const subjects = index.subjectsByType.get(atom.classIri);
|
|
439
|
+
if (!subjects) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
for (const subjectKey of subjects) {
|
|
443
|
+
const subject = index.termByKey.get(subjectKey);
|
|
444
|
+
if (subject) {
|
|
445
|
+
yield this.typeFact(subject, atom.classIri);
|
|
446
|
+
}
|
|
370
447
|
}
|
|
371
|
-
|
|
372
|
-
return [...subjects]
|
|
373
|
-
.map((subjectKey) => index.termByKey.get(subjectKey))
|
|
374
|
-
.filter((term) => Boolean(term))
|
|
375
|
-
.map((subject) => this.typeFact(subject, atom.classIri));
|
|
448
|
+
return;
|
|
376
449
|
}
|
|
377
450
|
const left = this.resolvePatternTerm(atom.argument1, bindings);
|
|
378
451
|
const right = this.resolvePatternTerm(atom.argument2, bindings);
|
|
379
452
|
if (left) {
|
|
380
|
-
|
|
453
|
+
yield* this.getEdgeFacts(index, left, atom.propertyIri);
|
|
454
|
+
return;
|
|
381
455
|
}
|
|
382
456
|
if (right) {
|
|
383
|
-
|
|
457
|
+
yield* this.getIncomingEdgesByProperty(index, right, atom.propertyIri);
|
|
458
|
+
return;
|
|
384
459
|
}
|
|
385
|
-
|
|
460
|
+
yield* index.edgeFactsByProperty.get(atom.propertyIri) ?? [];
|
|
386
461
|
}
|
|
387
|
-
|
|
462
|
+
// Match an atom against a fact, mutating `bindings` in place. Returns the list of variable
|
|
463
|
+
// names newly bound (for the caller to backtrack), or null if the atom does not match.
|
|
464
|
+
// On a partial-then-failed match the names bound so far are undone before returning null.
|
|
465
|
+
matchAtomToFactMut(atom, fact, bindings) {
|
|
466
|
+
const added = [];
|
|
388
467
|
if (atom.kind === 'class') {
|
|
389
468
|
if (!this.isTypeFact(fact) || fact.object.value !== atom.classIri) {
|
|
390
|
-
return
|
|
469
|
+
return null;
|
|
391
470
|
}
|
|
392
|
-
|
|
471
|
+
if (!this.matchPatternMut(atom.argument, fact.subject, bindings, added)) {
|
|
472
|
+
this.undoBindings(bindings, added);
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
return added;
|
|
393
476
|
}
|
|
394
477
|
if (fact.predicate.value !== atom.propertyIri) {
|
|
395
|
-
return
|
|
478
|
+
return null;
|
|
396
479
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
480
|
+
if (!this.matchPatternMut(atom.argument1, fact.subject, bindings, added)
|
|
481
|
+
|| !this.matchPatternMut(atom.argument2, fact.object, bindings, added)) {
|
|
482
|
+
this.undoBindings(bindings, added);
|
|
483
|
+
return null;
|
|
400
484
|
}
|
|
401
|
-
return
|
|
485
|
+
return added;
|
|
402
486
|
}
|
|
403
|
-
|
|
487
|
+
matchPatternMut(pattern, term, bindings, added) {
|
|
404
488
|
if (pattern.kind === 'variable') {
|
|
405
489
|
const existing = bindings.get(pattern.name);
|
|
406
490
|
if (existing) {
|
|
407
|
-
return this.sameTerm(existing, term)
|
|
491
|
+
return this.sameTerm(existing, term);
|
|
408
492
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
return
|
|
493
|
+
bindings.set(pattern.name, term);
|
|
494
|
+
added.push(pattern.name);
|
|
495
|
+
return true;
|
|
412
496
|
}
|
|
413
497
|
if (pattern.kind === 'named') {
|
|
414
|
-
return term.termType === 'NamedNode' && term.value === pattern.iri
|
|
498
|
+
return term.termType === 'NamedNode' && term.value === pattern.iri;
|
|
415
499
|
}
|
|
416
500
|
if (term.termType !== 'Literal') {
|
|
417
|
-
return
|
|
501
|
+
return false;
|
|
418
502
|
}
|
|
419
503
|
return term.value === pattern.value
|
|
420
504
|
&& term.datatype.value === pattern.datatype
|
|
421
|
-
&& term.language === pattern.language
|
|
422
|
-
|
|
423
|
-
|
|
505
|
+
&& term.language === pattern.language;
|
|
506
|
+
}
|
|
507
|
+
undoBindings(bindings, added) {
|
|
508
|
+
for (const key of added) {
|
|
509
|
+
bindings.delete(key);
|
|
510
|
+
}
|
|
424
511
|
}
|
|
425
512
|
resolvePatternTerm(pattern, bindings) {
|
|
426
513
|
if (pattern.kind === 'variable') {
|
|
427
514
|
return bindings.get(pattern.name);
|
|
428
515
|
}
|
|
429
516
|
if (pattern.kind === 'named') {
|
|
430
|
-
return
|
|
517
|
+
return this.internNamed(pattern.iri);
|
|
518
|
+
}
|
|
519
|
+
return this.internLiteral(pattern.value, pattern.datatype, pattern.language);
|
|
520
|
+
}
|
|
521
|
+
internNamed(iri) {
|
|
522
|
+
const key = `N|${iri}`;
|
|
523
|
+
let term = this.termCache.get(key);
|
|
524
|
+
if (!term) {
|
|
525
|
+
term = namedNode(iri);
|
|
526
|
+
this.termCache.set(key, term);
|
|
527
|
+
}
|
|
528
|
+
return term;
|
|
529
|
+
}
|
|
530
|
+
internLiteral(value, datatype, language) {
|
|
531
|
+
const key = `L|${value}|${datatype}|${language}`;
|
|
532
|
+
let term = this.termCache.get(key);
|
|
533
|
+
if (!term) {
|
|
534
|
+
term = DataFactory.literal(value, language || namedNode(datatype));
|
|
535
|
+
this.termCache.set(key, term);
|
|
431
536
|
}
|
|
432
|
-
return
|
|
537
|
+
return term;
|
|
433
538
|
}
|
|
434
539
|
instantiateRuleAtom(atom, bindings) {
|
|
435
540
|
if (atom.kind === 'class') {
|
|
@@ -454,96 +559,90 @@ export class ABoxChainer {
|
|
|
454
559
|
&& left.datatype.value === right.datatype.value
|
|
455
560
|
&& left.language === right.language));
|
|
456
561
|
}
|
|
457
|
-
resolveScope(modelUri, readModelUris) {
|
|
458
|
-
const store = this.reasoningStore.getStore();
|
|
562
|
+
resolveScope(modelUri, readModelUris, tbox, eagerIndex) {
|
|
459
563
|
const facts = new Map();
|
|
564
|
+
const index = this.emptyWorkingIndex(facts, tbox, eagerIndex || tbox.transitiveProperties.size > 0);
|
|
460
565
|
const baseKeys = new Set();
|
|
461
566
|
const importEntKeys = new Set();
|
|
462
567
|
const ownEntKeys = new Set();
|
|
463
|
-
const
|
|
568
|
+
const ownBaseFactsByKey = new Map();
|
|
464
569
|
const ownGraphs = this.reasoningStore.graphs(modelUri);
|
|
465
570
|
for (const uri of readModelUris) {
|
|
466
|
-
let graphs;
|
|
467
571
|
try {
|
|
468
|
-
|
|
572
|
+
this.reasoningStore.graphs(uri);
|
|
469
573
|
}
|
|
470
574
|
catch {
|
|
471
575
|
continue;
|
|
472
576
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const
|
|
476
|
-
|
|
577
|
+
const isOwn = uri === modelUri;
|
|
578
|
+
this.reasoningStore.forEachAssertedFact(uri, (subject, predicate, object, key) => {
|
|
579
|
+
const fact = { subject, predicate, object };
|
|
580
|
+
this.addResolvedScopeFact(facts, index, key, fact);
|
|
477
581
|
baseKeys.add(key);
|
|
478
|
-
if (
|
|
479
|
-
|
|
582
|
+
if (isOwn) {
|
|
583
|
+
ownBaseFactsByKey.set(key, fact);
|
|
480
584
|
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const fact =
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
if (uri === modelUri) {
|
|
585
|
+
});
|
|
586
|
+
this.reasoningStore.forEachEntailedFact(uri, (subject, predicate, object, key) => {
|
|
587
|
+
const fact = { subject, predicate, object };
|
|
588
|
+
this.addResolvedScopeFact(facts, index, key, fact);
|
|
589
|
+
if (isOwn) {
|
|
487
590
|
ownEntKeys.add(key);
|
|
488
591
|
}
|
|
489
592
|
else {
|
|
490
593
|
importEntKeys.add(key);
|
|
491
594
|
}
|
|
492
|
-
}
|
|
595
|
+
});
|
|
493
596
|
}
|
|
494
597
|
return {
|
|
598
|
+
modelUri,
|
|
495
599
|
ownEntailmentGraph: ownGraphs.entailments,
|
|
496
|
-
ownBaseFacts:
|
|
600
|
+
ownBaseFacts: [...ownBaseFactsByKey.values()],
|
|
497
601
|
baseKeys,
|
|
498
602
|
importEntKeys,
|
|
499
603
|
ownEntKeys,
|
|
500
604
|
facts,
|
|
605
|
+
index,
|
|
501
606
|
};
|
|
502
607
|
}
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
const
|
|
507
|
-
const edgeFactsByProperty = new Map();
|
|
508
|
-
const subjectsByPropertyObject = new Map();
|
|
509
|
-
const termByKey = new Map();
|
|
510
|
-
for (const fact of facts.values()) {
|
|
511
|
-
const sKey = fact.subject.id;
|
|
512
|
-
const oKey = fact.object.id;
|
|
513
|
-
termByKey.set(sKey, fact.subject);
|
|
514
|
-
termByKey.set(oKey, fact.object);
|
|
515
|
-
if (fact.predicate.value === RDF_TYPE.value && fact.object.termType === 'NamedNode') {
|
|
516
|
-
const values = typesBySubject.get(sKey) ?? new Set();
|
|
517
|
-
values.add(fact.object.value);
|
|
518
|
-
typesBySubject.set(sKey, values);
|
|
519
|
-
const subjects = subjectsByType.get(fact.object.value) ?? new Set();
|
|
520
|
-
subjects.add(sKey);
|
|
521
|
-
subjectsByType.set(fact.object.value, subjects);
|
|
522
|
-
continue;
|
|
523
|
-
}
|
|
524
|
-
const spKey = `${sKey}|${fact.predicate.value}`;
|
|
525
|
-
const bySp = edgeFactsBySubjectProperty.get(spKey) ?? [];
|
|
526
|
-
bySp.push(fact);
|
|
527
|
-
edgeFactsBySubjectProperty.set(spKey, bySp);
|
|
528
|
-
const byProperty = edgeFactsByProperty.get(fact.predicate.value) ?? [];
|
|
529
|
-
byProperty.push(fact);
|
|
530
|
-
edgeFactsByProperty.set(fact.predicate.value, byProperty);
|
|
531
|
-
const poKey = `${fact.predicate.value}|${oKey}`;
|
|
532
|
-
const subjects = subjectsByPropertyObject.get(poKey) ?? new Set();
|
|
533
|
-
subjects.add(sKey);
|
|
534
|
-
subjectsByPropertyObject.set(poKey, subjects);
|
|
535
|
-
}
|
|
608
|
+
emptyWorkingIndex(facts, tbox, indexed) {
|
|
609
|
+
const hasTransitiveProperties = tbox.transitiveProperties.size > 0;
|
|
610
|
+
const indexEdgesByObject = tbox.objectRange.size > 0;
|
|
611
|
+
const indexSubjectsByPropertyObject = hasTransitiveProperties;
|
|
536
612
|
return {
|
|
537
613
|
facts,
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
614
|
+
indexed,
|
|
615
|
+
indexEdgesByObject,
|
|
616
|
+
indexSubjectsByPropertyObject,
|
|
617
|
+
typesBySubject: new Map(),
|
|
618
|
+
subjectsByType: new Map(),
|
|
619
|
+
edgeFactsBySubject: new Map(),
|
|
620
|
+
edgeFactsByObject: new Map(),
|
|
621
|
+
edgeFactsBySubjectProperty: new Map(),
|
|
622
|
+
edgeFactsByProperty: new Map(),
|
|
623
|
+
subjectsByPropertyObject: new Map(),
|
|
624
|
+
termByKey: new Map(),
|
|
544
625
|
};
|
|
545
626
|
}
|
|
546
|
-
|
|
627
|
+
addResolvedScopeFact(facts, index, key, fact) {
|
|
628
|
+
if (facts.has(key)) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
facts.set(key, fact);
|
|
632
|
+
if (index.indexed) {
|
|
633
|
+
this.indexAdd(index, fact);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
ensureWorkingIndex(index) {
|
|
637
|
+
if (index.indexed) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
index.indexed = true;
|
|
641
|
+
for (const fact of index.facts.values()) {
|
|
642
|
+
this.indexAdd(index, fact);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
insertOwnEntailedFact(scope, index, fact) {
|
|
547
646
|
const key = this.factKey(fact);
|
|
548
647
|
if (scope.ownEntKeys.has(key)) {
|
|
549
648
|
return;
|
|
@@ -551,10 +650,12 @@ export class ABoxChainer {
|
|
|
551
650
|
scope.ownEntKeys.add(key);
|
|
552
651
|
scope.facts.set(key, fact);
|
|
553
652
|
index.facts.set(key, fact);
|
|
554
|
-
|
|
555
|
-
|
|
653
|
+
if (index.indexed) {
|
|
654
|
+
this.indexAdd(index, fact);
|
|
655
|
+
}
|
|
656
|
+
this.reasoningStore.addEntailment(scope.modelUri, quad(this.asQuadSubject(fact.subject), fact.predicate, this.asQuadObject(fact.object), scope.ownEntailmentGraph));
|
|
556
657
|
}
|
|
557
|
-
removeOwnEntailedFact(scope, index,
|
|
658
|
+
removeOwnEntailedFact(scope, index, fact) {
|
|
558
659
|
const key = this.factKey(fact);
|
|
559
660
|
if (!scope.ownEntKeys.has(key)) {
|
|
560
661
|
return;
|
|
@@ -562,8 +663,10 @@ export class ABoxChainer {
|
|
|
562
663
|
scope.ownEntKeys.delete(key);
|
|
563
664
|
scope.facts.delete(key);
|
|
564
665
|
index.facts.delete(key);
|
|
565
|
-
|
|
566
|
-
|
|
666
|
+
if (index.indexed) {
|
|
667
|
+
this.indexRemove(index, fact);
|
|
668
|
+
}
|
|
669
|
+
this.reasoningStore.removeEntailment(scope.modelUri, quad(this.asQuadSubject(fact.subject), fact.predicate, this.asQuadObject(fact.object), scope.ownEntailmentGraph));
|
|
567
670
|
}
|
|
568
671
|
canInsertEntailedFact(scope, fact) {
|
|
569
672
|
const key = this.factKey(fact);
|
|
@@ -576,33 +679,18 @@ export class ABoxChainer {
|
|
|
576
679
|
return index.edgeFactsBySubjectProperty.get(`${subject.id}|${property}`) ?? [];
|
|
577
680
|
}
|
|
578
681
|
getOutgoingEdges(index, subject) {
|
|
579
|
-
|
|
580
|
-
const prefix = `${subject.id}|`;
|
|
581
|
-
for (const [key, facts] of index.edgeFactsBySubjectProperty.entries()) {
|
|
582
|
-
if (key.startsWith(prefix)) {
|
|
583
|
-
edges.push(...facts);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
return edges;
|
|
682
|
+
return index.edgeFactsBySubject.get(subject.id) ?? [];
|
|
587
683
|
}
|
|
588
684
|
getIncomingEdges(index, object) {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
for (const [key, subjects] of index.subjectsByPropertyObject.entries()) {
|
|
592
|
-
if (!key.endsWith(suffix)) {
|
|
593
|
-
continue;
|
|
594
|
-
}
|
|
595
|
-
const property = key.slice(0, key.length - suffix.length);
|
|
596
|
-
for (const subjectKey of subjects) {
|
|
597
|
-
const subject = index.termByKey.get(subjectKey);
|
|
598
|
-
if (subject) {
|
|
599
|
-
edges.push(...this.getEdgeFacts(index, subject, property));
|
|
600
|
-
}
|
|
601
|
-
}
|
|
685
|
+
if (!index.indexEdgesByObject) {
|
|
686
|
+
return [];
|
|
602
687
|
}
|
|
603
|
-
return
|
|
688
|
+
return index.edgeFactsByObject.get(object.id) ?? [];
|
|
604
689
|
}
|
|
605
690
|
getIncomingEdgesByProperty(index, object, property) {
|
|
691
|
+
if (!index.indexSubjectsByPropertyObject) {
|
|
692
|
+
return (index.edgeFactsByProperty.get(property) ?? []).filter((fact) => fact.object.id === object.id);
|
|
693
|
+
}
|
|
606
694
|
const subjects = index.subjectsByPropertyObject.get(`${property}|${object.id}`) ?? new Set();
|
|
607
695
|
const edges = [];
|
|
608
696
|
for (const subjectKey of subjects) {
|
|
@@ -697,6 +785,14 @@ export class ABoxChainer {
|
|
|
697
785
|
index.subjectsByType.set(fact.object.value, subjects);
|
|
698
786
|
return;
|
|
699
787
|
}
|
|
788
|
+
const bySubject = index.edgeFactsBySubject.get(fact.subject.id) ?? [];
|
|
789
|
+
bySubject.push(fact);
|
|
790
|
+
index.edgeFactsBySubject.set(fact.subject.id, bySubject);
|
|
791
|
+
if (index.indexEdgesByObject) {
|
|
792
|
+
const byObject = index.edgeFactsByObject.get(fact.object.id) ?? [];
|
|
793
|
+
byObject.push(fact);
|
|
794
|
+
index.edgeFactsByObject.set(fact.object.id, byObject);
|
|
795
|
+
}
|
|
700
796
|
const spKey = `${fact.subject.id}|${fact.predicate.value}`;
|
|
701
797
|
const bySp = index.edgeFactsBySubjectProperty.get(spKey) ?? [];
|
|
702
798
|
bySp.push(fact);
|
|
@@ -704,10 +800,12 @@ export class ABoxChainer {
|
|
|
704
800
|
const byProperty = index.edgeFactsByProperty.get(fact.predicate.value) ?? [];
|
|
705
801
|
byProperty.push(fact);
|
|
706
802
|
index.edgeFactsByProperty.set(fact.predicate.value, byProperty);
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
803
|
+
if (index.indexSubjectsByPropertyObject) {
|
|
804
|
+
const poKey = `${fact.predicate.value}|${fact.object.id}`;
|
|
805
|
+
const subjects = index.subjectsByPropertyObject.get(poKey) ?? new Set();
|
|
806
|
+
subjects.add(fact.subject.id);
|
|
807
|
+
index.subjectsByPropertyObject.set(poKey, subjects);
|
|
808
|
+
}
|
|
711
809
|
}
|
|
712
810
|
indexRemove(index, fact) {
|
|
713
811
|
if (fact.predicate.value === RDF_TYPE.value && fact.object.termType === 'NamedNode') {
|
|
@@ -728,9 +826,27 @@ export class ABoxChainer {
|
|
|
728
826
|
}
|
|
729
827
|
return;
|
|
730
828
|
}
|
|
829
|
+
const bySubject = index.edgeFactsBySubject.get(fact.subject.id) ?? [];
|
|
830
|
+
const nextBySubject = this.removeFactFromArray(bySubject, fact);
|
|
831
|
+
if (nextBySubject.length === 0) {
|
|
832
|
+
index.edgeFactsBySubject.delete(fact.subject.id);
|
|
833
|
+
}
|
|
834
|
+
else {
|
|
835
|
+
index.edgeFactsBySubject.set(fact.subject.id, nextBySubject);
|
|
836
|
+
}
|
|
837
|
+
if (index.indexEdgesByObject) {
|
|
838
|
+
const byObject = index.edgeFactsByObject.get(fact.object.id) ?? [];
|
|
839
|
+
const nextByObject = this.removeFactFromArray(byObject, fact);
|
|
840
|
+
if (nextByObject.length === 0) {
|
|
841
|
+
index.edgeFactsByObject.delete(fact.object.id);
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
index.edgeFactsByObject.set(fact.object.id, nextByObject);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
731
847
|
const spKey = `${fact.subject.id}|${fact.predicate.value}`;
|
|
732
848
|
const bySp = index.edgeFactsBySubjectProperty.get(spKey) ?? [];
|
|
733
|
-
const nextBySp =
|
|
849
|
+
const nextBySp = this.removeFactFromArray(bySp, fact);
|
|
734
850
|
if (nextBySp.length === 0) {
|
|
735
851
|
index.edgeFactsBySubjectProperty.delete(spKey);
|
|
736
852
|
}
|
|
@@ -738,31 +854,41 @@ export class ABoxChainer {
|
|
|
738
854
|
index.edgeFactsBySubjectProperty.set(spKey, nextBySp);
|
|
739
855
|
}
|
|
740
856
|
const byProperty = index.edgeFactsByProperty.get(fact.predicate.value) ?? [];
|
|
741
|
-
const nextByProperty =
|
|
857
|
+
const nextByProperty = this.removeFactFromArray(byProperty, fact);
|
|
742
858
|
if (nextByProperty.length === 0) {
|
|
743
859
|
index.edgeFactsByProperty.delete(fact.predicate.value);
|
|
744
860
|
}
|
|
745
861
|
else {
|
|
746
862
|
index.edgeFactsByProperty.set(fact.predicate.value, nextByProperty);
|
|
747
863
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
864
|
+
if (index.indexSubjectsByPropertyObject) {
|
|
865
|
+
const poKey = `${fact.predicate.value}|${fact.object.id}`;
|
|
866
|
+
const subjects = index.subjectsByPropertyObject.get(poKey);
|
|
867
|
+
if (!subjects) {
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
const stillHas = nextBySp.some((entry) => entry.object.id === fact.object.id);
|
|
871
|
+
if (!stillHas) {
|
|
872
|
+
subjects.delete(fact.subject.id);
|
|
873
|
+
}
|
|
874
|
+
if (subjects.size === 0) {
|
|
875
|
+
index.subjectsByPropertyObject.delete(poKey);
|
|
876
|
+
}
|
|
759
877
|
}
|
|
760
878
|
}
|
|
879
|
+
removeFactFromArray(facts, target) {
|
|
880
|
+
return facts.filter((fact) => fact.subject.id !== target.subject.id
|
|
881
|
+
|| fact.predicate.id !== target.predicate.id
|
|
882
|
+
|| fact.object.id !== target.object.id);
|
|
883
|
+
}
|
|
761
884
|
collectValidationWarnings(_index, _tbox) {
|
|
762
885
|
const warnings = [];
|
|
886
|
+
if (!_index.indexed) {
|
|
887
|
+
return this.collectValidationWarningsFromFacts(_index.facts.values(), _tbox);
|
|
888
|
+
}
|
|
763
889
|
for (const propertyIri of _tbox.functionalProperties) {
|
|
764
890
|
const bySubject = new Map();
|
|
765
|
-
const facts =
|
|
891
|
+
const facts = this.validationFactsForProperty(_index, propertyIri);
|
|
766
892
|
for (const fact of facts) {
|
|
767
893
|
const objects = bySubject.get(fact.subject.id) ?? new Set();
|
|
768
894
|
objects.add(fact.object.id);
|
|
@@ -776,7 +902,7 @@ export class ABoxChainer {
|
|
|
776
902
|
}
|
|
777
903
|
for (const propertyIri of _tbox.inverseFunctionalProperties) {
|
|
778
904
|
const byObject = new Map();
|
|
779
|
-
const facts =
|
|
905
|
+
const facts = this.validationFactsForProperty(_index, propertyIri);
|
|
780
906
|
for (const fact of facts) {
|
|
781
907
|
const subjects = byObject.get(fact.object.id) ?? new Set();
|
|
782
908
|
subjects.add(fact.subject.id);
|
|
@@ -790,5 +916,56 @@ export class ABoxChainer {
|
|
|
790
916
|
}
|
|
791
917
|
return warnings;
|
|
792
918
|
}
|
|
919
|
+
collectValidationWarningsFromFacts(facts, tbox) {
|
|
920
|
+
const functionalByPropertySubject = new Map();
|
|
921
|
+
const inverseFunctionalByPropertyObject = new Map();
|
|
922
|
+
for (const fact of facts) {
|
|
923
|
+
const propertyIri = fact.predicate.value;
|
|
924
|
+
if (tbox.functionalProperties.has(propertyIri)) {
|
|
925
|
+
const key = `${propertyIri}|${fact.subject.id}`;
|
|
926
|
+
const objects = functionalByPropertySubject.get(key) ?? new Set();
|
|
927
|
+
objects.add(fact.object.id);
|
|
928
|
+
functionalByPropertySubject.set(key, objects);
|
|
929
|
+
}
|
|
930
|
+
if (tbox.inverseFunctionalProperties.has(propertyIri)) {
|
|
931
|
+
const key = `${propertyIri}|${fact.object.id}`;
|
|
932
|
+
const subjects = inverseFunctionalByPropertyObject.get(key) ?? new Set();
|
|
933
|
+
subjects.add(fact.subject.id);
|
|
934
|
+
inverseFunctionalByPropertyObject.set(key, subjects);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
const warnings = [];
|
|
938
|
+
for (const [key, objects] of functionalByPropertySubject) {
|
|
939
|
+
if (objects.size <= 1) {
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
const separator = key.lastIndexOf('|');
|
|
943
|
+
const propertyIri = key.slice(0, separator);
|
|
944
|
+
const subjectId = key.slice(separator + 1);
|
|
945
|
+
warnings.push(`Functional property violation: ${propertyIri} has multiple values for subject ${subjectId}`);
|
|
946
|
+
}
|
|
947
|
+
for (const [key, subjects] of inverseFunctionalByPropertyObject) {
|
|
948
|
+
if (subjects.size <= 1) {
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
const separator = key.lastIndexOf('|');
|
|
952
|
+
const propertyIri = key.slice(0, separator);
|
|
953
|
+
const objectId = key.slice(separator + 1);
|
|
954
|
+
warnings.push(`Inverse-functional property violation: ${propertyIri} has multiple subjects for object ${objectId}`);
|
|
955
|
+
}
|
|
956
|
+
return warnings;
|
|
957
|
+
}
|
|
958
|
+
validationFactsForProperty(index, propertyIri) {
|
|
959
|
+
if (index.indexed) {
|
|
960
|
+
return index.edgeFactsByProperty.get(propertyIri) ?? [];
|
|
961
|
+
}
|
|
962
|
+
const facts = [];
|
|
963
|
+
for (const fact of index.facts.values()) {
|
|
964
|
+
if (fact.predicate.value === propertyIri) {
|
|
965
|
+
facts.push(fact);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return facts;
|
|
969
|
+
}
|
|
793
970
|
}
|
|
794
971
|
//# sourceMappingURL=owl-abox.js.map
|