n3 2.13.3 → 2.13.5

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/src/N3Reasoner.js CHANGED
@@ -34,6 +34,7 @@ export default class N3Reasoner {
34
34
  if (!store._addToIndex(graphItem.subjects, subject, predicate, object)) return;
35
35
  store._addToIndex(graphItem.predicates, predicate, object, subject);
36
36
  store._addToIndex(graphItem.objects, object, subject, predicate);
37
+ this._indexed++;
37
38
  }
38
39
  // Count genuinely new derivations and fail past the budget. The check comes
39
40
  // after all three indexes are updated, so a caught error leaves the store
@@ -175,7 +176,7 @@ export default class N3Reasoner {
175
176
  }
176
177
 
177
178
  reason(rules) {
178
- this._derivations = 0;
179
+ this._derivations = this._indexed = 0;
179
180
  if (!Array.isArray(rules)) {
180
181
  rules = getRulesFromDataset(rules);
181
182
  }
@@ -242,9 +243,10 @@ export default class N3Reasoner {
242
243
  }
243
244
  }
244
245
  finally {
245
- // Invalidate the cached size even if a derivation budget was exceeded,
246
- // so a caught budget error leaves the store fully consistent.
247
- this._store._size = null;
246
+ // Count the quads added directly to the indexes, even if a derivation
247
+ // budget was exceeded, so a caught budget error leaves the store consistent
248
+ if (this._store._size !== null)
249
+ this._store._size += this._indexed;
248
250
  }
249
251
  }
250
252
  }
package/src/N3Store.js CHANGED
@@ -23,11 +23,15 @@ function hasInIndex(index0, key0, key1, key2) {
23
23
  return !!index2 && key2 in index2;
24
24
  }
25
25
 
26
+ // The number of index leaves that the last `merge` call added
27
+ let mergedLeaves = 0;
28
+
26
29
  function merge(target, source, depth = 4) {
27
30
  let size = target[SIZE] || 0;
28
31
  for (const key in source) {
29
32
  if (!(key in target)) {
30
33
  size++;
34
+ if (depth === 0) mergedLeaves++;
31
35
  target[key] = depth === 0 ? null : merge(Object.create(null), source[key], depth - 1);
32
36
  }
33
37
  // Merge into the existing object in place,
@@ -242,6 +246,12 @@ export class N3EntityIndex {
242
246
  }
243
247
  }
244
248
 
249
+ // Returns the size of a store from this copy of N3 if it is known without counting,
250
+ // or null otherwise
251
+ function knownSize(dataset) {
252
+ return dataset instanceof N3Store ? dataset._size : null;
253
+ }
254
+
245
255
  // ## Constructor
246
256
  export default class N3Store {
247
257
  constructor(quads, options) {
@@ -273,20 +283,11 @@ export default class N3Store {
273
283
 
274
284
  // ### `size` returns the number of quads in the store
275
285
  get size() {
276
- // Return the quad count if if was cached
277
- let size = this._size;
278
- if (size !== null)
279
- return size;
280
-
281
- // Calculate the number of quads by counting to the deepest level
282
- size = 0;
283
- const graphs = this._graphs;
284
- let subjects, subject;
285
- for (const graphKey in graphs)
286
- for (const subjectKey in (subjects = graphs[graphKey].subjects))
287
- for (const predicateKey in (subject = subjects[subjectKey]))
288
- size += subject[predicateKey][SIZE];
289
- return this._size = size;
286
+ // The quad count is kept up to date by every change,
287
+ // except by reasoners from earlier versions of N3, which set it to null
288
+ if (this._size === null)
289
+ this._size = this.countQuads();
290
+ return this._size;
290
291
  }
291
292
 
292
293
  // ## Private methods
@@ -568,8 +569,7 @@ export default class N3Store {
568
569
  this._addToIndex(graphItem.predicates, predicate, object, subject);
569
570
  this._addToIndex(graphItem.objects, object, subject, predicate);
570
571
 
571
- // The cached quad count is now invalid
572
- this._size = null;
572
+ if (this._size !== null) this._size++;
573
573
  return true;
574
574
  }
575
575
 
@@ -779,10 +779,14 @@ export default class N3Store {
779
779
  // If only predicate and possibly object are given, the predicate index will be the fastest
780
780
  count += this._countInIndex(content.predicates, predicateId, objectId, subjectId);
781
781
  }
782
- else {
783
- // If only object is possibly given, the object index will be the fastest
782
+ else if (object) {
783
+ // If only object is given, the object index will be the fastest
784
784
  count += this._countInIndex(content.objects, objectId, subjectId, predicateId);
785
785
  }
786
+ else {
787
+ // Without a pattern, the subject index sums the fewest leaf counts
788
+ count += this._countInIndex(content.subjects);
789
+ }
786
790
  }
787
791
  }
788
792
  return count;
@@ -1102,9 +1106,11 @@ export default class N3Store {
1102
1106
  this.addQuads(quads);
1103
1107
  // Index merging bypasses observer notifications
1104
1108
  else if (this._observers === null && quads instanceof N3Store && quads._entityIndex === this._entityIndex) {
1105
- if (quads._size !== 0) {
1109
+ if (quads.size !== 0) {
1110
+ // Each new quad adds one leaf to each of the three indexes
1111
+ mergedLeaves = 0;
1106
1112
  this._graphs = merge(this._graphs, quads._graphs);
1107
- this._size = null; // Invalidate the cached size
1113
+ if (this._size !== null) this._size += mergedLeaves / 3;
1108
1114
  }
1109
1115
  }
1110
1116
  else {
@@ -1127,19 +1133,26 @@ export default class N3Store {
1127
1133
  if (other === this)
1128
1134
  return true;
1129
1135
 
1130
- if (!(other instanceof N3Store) || this._entityIndex !== other._entityIndex)
1136
+ if (!(other instanceof N3Store) || this._entityIndex !== other._entityIndex) {
1137
+ // A larger set cannot be a subset, but only compare sizes that are known without counting
1138
+ const otherSize = knownSize(other), thisSize = this._size;
1139
+ if (otherSize !== null && thisSize !== null && otherSize > thisSize)
1140
+ return false;
1131
1141
  return other.every(quad => this.has(quad));
1142
+ }
1132
1143
 
1144
+ // At every level, the index of the subset cannot have more entries
1133
1145
  const g1 = this._graphs, g2 = other._graphs;
1134
- let s1, s2, p1, p2, o1;
1146
+ let s1, s2, p1, p2, o1, o2;
1135
1147
  for (const graph in g2) {
1136
1148
  if (!(s1 = g1[graph])) return false;
1137
1149
  s1 = s1.subjects;
1138
- for (const subject in (s2 = g2[graph].subjects)) {
1139
- if (!(p1 = s1[subject])) return false;
1140
- for (const predicate in (p2 = s2[subject])) {
1141
- if (!(o1 = p1[predicate])) return false;
1142
- for (const object in p2[predicate])
1150
+ if ((s2 = g2[graph].subjects)[SIZE] > s1[SIZE]) return false;
1151
+ for (const subject in s2) {
1152
+ if (!(p1 = s1[subject]) || (p2 = s2[subject])[SIZE] > p1[SIZE]) return false;
1153
+ for (const predicate in p2) {
1154
+ if (!(o1 = p1[predicate]) || (o2 = p2[predicate])[SIZE] > o1[SIZE]) return false;
1155
+ for (const object in o2)
1143
1156
  if (!(object in o1)) return false;
1144
1157
  }
1145
1158
  }
@@ -1222,7 +1235,7 @@ export default class N3Store {
1222
1235
  if (other === this) {
1223
1236
  const store = new N3Store({ entityIndex: this._entityIndex });
1224
1237
  store._graphs = merge(Object.create(null), this._graphs);
1225
- store._size = this._size;
1238
+ store._size = this.size;
1226
1239
  return store;
1227
1240
  }
1228
1241
  else if ((other instanceof N3Store) && this._entityIndex === other._entityIndex) {
@@ -1235,6 +1248,19 @@ export default class N3Store {
1235
1248
  return store;
1236
1249
  }
1237
1250
 
1251
+ // Test the quads of the smaller dataset against the larger one
1252
+ // when both sizes are known without counting
1253
+ const otherSize = knownSize(other), thisSize = this._size;
1254
+ if (otherSize !== null && thisSize !== null && otherSize < thisSize && typeof other[Symbol.iterator] === 'function' &&
1255
+ // unless it is a store whose custom factories may not create RDF/JS quads,
1256
+ // either for its quads or, through its entity index, for their terms
1257
+ other._factory === N3DataFactory && other._entityIndex._factory === N3DataFactory) {
1258
+ const store = new N3Store({ entityIndex: this._entityIndex });
1259
+ for (const quad of other)
1260
+ if (this.has(quad))
1261
+ store.add(quad);
1262
+ return store;
1263
+ }
1238
1264
  return this.filter(quad => other.has(quad));
1239
1265
  }
1240
1266
 
@@ -1306,7 +1332,7 @@ export default class N3Store {
1306
1332
  union(quads) {
1307
1333
  const store = new N3Store({ entityIndex: this._entityIndex });
1308
1334
  store._graphs = merge(Object.create(null), this._graphs);
1309
- store._size = this._size;
1335
+ store._size = this.size;
1310
1336
 
1311
1337
  store.addAll(quads);
1312
1338
  return store;