@oml/owl 0.19.3 → 0.20.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.
Files changed (46) hide show
  1. package/out/index.d.ts +3 -0
  2. package/out/index.js +3 -0
  3. package/out/index.js.map +1 -1
  4. package/out/owl/owl-abox.d.ts +16 -5
  5. package/out/owl/owl-abox.js +365 -188
  6. package/out/owl/owl-abox.js.map +1 -1
  7. package/out/owl/owl-imports.js +4 -2
  8. package/out/owl/owl-imports.js.map +1 -1
  9. package/out/owl/owl-interfaces.d.ts +26 -1
  10. package/out/owl/owl-mapper.d.ts +2 -0
  11. package/out/owl/owl-mapper.js +77 -38
  12. package/out/owl/owl-mapper.js.map +1 -1
  13. package/out/owl/owl-service.d.ts +4 -0
  14. package/out/owl/owl-service.js +139 -44
  15. package/out/owl/owl-service.js.map +1 -1
  16. package/out/owl/owl-shacl.d.ts +8 -9
  17. package/out/owl/owl-shacl.js +43 -117
  18. package/out/owl/owl-shacl.js.map +1 -1
  19. package/out/owl/owl-sparql-engine.d.ts +6 -0
  20. package/out/owl/owl-sparql-engine.js +11 -0
  21. package/out/owl/owl-sparql-engine.js.map +1 -0
  22. package/out/owl/owl-sparql-oxigraph.d.ts +34 -0
  23. package/out/owl/owl-sparql-oxigraph.js +436 -0
  24. package/out/owl/owl-sparql-oxigraph.js.map +1 -0
  25. package/out/owl/owl-sparql.d.ts +0 -44
  26. package/out/owl/owl-sparql.js +0 -320
  27. package/out/owl/owl-sparql.js.map +1 -1
  28. package/out/owl/owl-store-lean.d.ts +29 -0
  29. package/out/owl/owl-store-lean.js +445 -0
  30. package/out/owl/owl-store-lean.js.map +1 -0
  31. package/out/owl/owl-store.d.ts +11 -1
  32. package/out/owl/owl-store.js +80 -6
  33. package/out/owl/owl-store.js.map +1 -1
  34. package/package.json +3 -3
  35. package/src/index.ts +3 -0
  36. package/src/owl/owl-abox.ts +401 -200
  37. package/src/owl/owl-imports.ts +4 -2
  38. package/src/owl/owl-interfaces.ts +28 -1
  39. package/src/owl/owl-mapper.ts +79 -41
  40. package/src/owl/owl-service.ts +149 -49
  41. package/src/owl/owl-shacl.ts +55 -132
  42. package/src/owl/owl-sparql-engine.ts +34 -0
  43. package/src/owl/owl-sparql-oxigraph.ts +527 -0
  44. package/src/owl/owl-sparql.ts +3 -366
  45. package/src/owl/owl-store-lean.ts +438 -0
  46. package/src/owl/owl-store.ts +86 -7
@@ -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, store, fact);
91
+ this.removeOwnEntailedFact(scope, index, fact);
88
92
  }
89
93
  }
90
94
  }
91
- const over = fullRecompute ? [] : this.overdelete(scope, index, tboxIndex, minusFacts, store);
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, store);
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: this.collectValidationWarnings(index, tboxIndex),
105
+ validationWarnings,
101
106
  };
102
107
  }
103
- overdelete(scope, index, tbox, minusFacts, store) {
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 > 0) {
107
- const current = queue.shift();
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, store, head);
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, store) {
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) || !this.hasSupport(seed, index, tbox)) {
149
+ if (!this.canInsertEntailedFact(scope, seed)) {
143
150
  continue;
144
151
  }
145
- this.insertOwnEntailedFact(scope, index, store, seed);
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
- while (queue.length > 0) {
150
- const fact = queue.shift();
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) || !this.hasSupport(head, index, tbox)) {
171
+ if (!this.canInsertEntailedFact(scope, head)) {
157
172
  continue;
158
173
  }
159
- this.insertOwnEntailedFact(scope, index, store, head);
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 bindings = this.matchAtomToFact(atom, fact, new Map());
309
- if (!bindings) {
310
- return;
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
- for (const resolved of this.resolveRuleBody(rule.body, index, bindings, anchorIndex)) {
313
- for (const head of rule.head) {
314
- const inferred = this.instantiateRuleAtom(head, resolved);
315
- if (inferred) {
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
- return this.uniqueFacts(heads);
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 = this.matchAtomToFact(head, fact, new Map());
328
- if (!bindings) {
387
+ const bindings = new Map();
388
+ if (this.matchAtomToFactMut(head, fact, bindings) === null) {
329
389
  continue;
330
390
  }
331
- const resolved = this.resolveRuleBody(rule.body, index, bindings, -1);
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
- resolveRuleBody(body, index, bindings, satisfiedIndex) {
340
- const pending = body.filter((_atom, index) => index !== satisfiedIndex);
341
- return this.resolvePendingAtoms(pending, index, bindings);
342
- }
343
- resolvePendingAtoms(pending, index, bindings) {
344
- if (pending.length === 0) {
345
- return [bindings];
346
- }
347
- const [current, ...rest] = pending;
348
- const matches = this.findMatchesForAtom(current, index, bindings);
349
- const resolved = [];
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
- return resolved;
410
+ yield* this.resolvePending(pending, 0, index, bindings);
354
411
  }
355
- findMatchesForAtom(atom, index, bindings) {
356
- const matches = [];
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 nextBindings = this.matchAtomToFact(atom, fact, bindings);
359
- if (nextBindings) {
360
- matches.push(nextBindings);
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
- return [...this.getTypes(index, bound)].map((classIri) => this.typeFact(bound, classIri));
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
- const subjects = index.subjectsByType.get(atom.classIri) ?? new Set();
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
- return this.getEdgeFacts(index, left, atom.propertyIri);
453
+ yield* this.getEdgeFacts(index, left, atom.propertyIri);
454
+ return;
381
455
  }
382
456
  if (right) {
383
- return this.getIncomingEdgesByProperty(index, right, atom.propertyIri);
457
+ yield* this.getIncomingEdgesByProperty(index, right, atom.propertyIri);
458
+ return;
384
459
  }
385
- return index.edgeFactsByProperty.get(atom.propertyIri) ?? [];
460
+ yield* index.edgeFactsByProperty.get(atom.propertyIri) ?? [];
386
461
  }
387
- matchAtomToFact(atom, fact, bindings) {
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 undefined;
469
+ return null;
391
470
  }
392
- return this.matchPattern(atom.argument, fact.subject, bindings);
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 undefined;
478
+ return null;
396
479
  }
397
- const afterLeft = this.matchPattern(atom.argument1, fact.subject, bindings);
398
- if (!afterLeft) {
399
- return undefined;
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 this.matchPattern(atom.argument2, fact.object, afterLeft);
485
+ return added;
402
486
  }
403
- matchPattern(pattern, term, bindings) {
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) ? bindings : undefined;
491
+ return this.sameTerm(existing, term);
408
492
  }
409
- const next = new Map(bindings);
410
- next.set(pattern.name, term);
411
- return next;
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 ? bindings : undefined;
498
+ return term.termType === 'NamedNode' && term.value === pattern.iri;
415
499
  }
416
500
  if (term.termType !== 'Literal') {
417
- return undefined;
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
- ? bindings
423
- : undefined;
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 namedNode(pattern.iri);
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 DataFactory.literal(pattern.value, pattern.language || namedNode(pattern.datatype));
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 ownBaseFacts = [];
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
- graphs = this.reasoningStore.graphs(uri);
572
+ this.reasoningStore.graphs(uri);
469
573
  }
470
574
  catch {
471
575
  continue;
472
576
  }
473
- for (const q of store.getQuads(null, null, null, graphs.own)) {
474
- const fact = this.asFact(q);
475
- const key = this.factKey(fact);
476
- facts.set(key, fact);
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 (uri === modelUri) {
479
- ownBaseFacts.push(fact);
582
+ if (isOwn) {
583
+ ownBaseFactsByKey.set(key, fact);
480
584
  }
481
- }
482
- for (const q of store.getQuads(null, null, null, graphs.entailments)) {
483
- const fact = this.asFact(q);
484
- const key = this.factKey(fact);
485
- facts.set(key, fact);
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: this.uniqueFacts(ownBaseFacts),
600
+ ownBaseFacts: [...ownBaseFactsByKey.values()],
497
601
  baseKeys,
498
602
  importEntKeys,
499
603
  ownEntKeys,
500
604
  facts,
605
+ index,
501
606
  };
502
607
  }
503
- buildIndex(facts) {
504
- const typesBySubject = new Map();
505
- const subjectsByType = new Map();
506
- const edgeFactsBySubjectProperty = new Map();
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
- typesBySubject,
539
- subjectsByType,
540
- edgeFactsBySubjectProperty,
541
- edgeFactsByProperty,
542
- subjectsByPropertyObject,
543
- termByKey,
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
- insertOwnEntailedFact(scope, index, store, fact) {
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
- this.indexAdd(index, fact);
555
- store.addQuad(quad(this.asQuadSubject(fact.subject), fact.predicate, this.asQuadObject(fact.object), scope.ownEntailmentGraph));
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, store, fact) {
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
- this.indexRemove(index, fact);
566
- store.removeQuad(quad(this.asQuadSubject(fact.subject), fact.predicate, this.asQuadObject(fact.object), scope.ownEntailmentGraph));
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
- const edges = [];
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
- const edges = [];
590
- const suffix = `|${object.id}`;
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 edges;
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
- const poKey = `${fact.predicate.value}|${fact.object.id}`;
708
- const subjects = index.subjectsByPropertyObject.get(poKey) ?? new Set();
709
- subjects.add(fact.subject.id);
710
- index.subjectsByPropertyObject.set(poKey, subjects);
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 = bySp.filter((entry) => this.factKey(entry) !== this.factKey(fact));
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 = byProperty.filter((entry) => this.factKey(entry) !== this.factKey(fact));
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
- const poKey = `${fact.predicate.value}|${fact.object.id}`;
749
- const subjects = index.subjectsByPropertyObject.get(poKey);
750
- if (!subjects) {
751
- return;
752
- }
753
- const stillHas = nextBySp.some((entry) => entry.object.id === fact.object.id);
754
- if (!stillHas) {
755
- subjects.delete(fact.subject.id);
756
- }
757
- if (subjects.size === 0) {
758
- index.subjectsByPropertyObject.delete(poKey);
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 = _index.edgeFactsByProperty.get(propertyIri) ?? [];
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 = _index.edgeFactsByProperty.get(propertyIri) ?? [];
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