n3 2.7.11 → 2.8.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/README.md +33 -0
- package/browser/n3.esm.min.js +11 -11
- package/browser/n3.min.js +11 -11
- package/lib/N3Lexer.js +102 -25
- package/lib/N3Reasoner.js +13 -4
- package/lib/N3Store.js +385 -27
- package/package.json +1 -1
- package/src/N3Lexer.js +90 -27
- package/src/N3Reasoner.js +14 -4
- package/src/N3Store.js +418 -32
package/lib/N3Store.js
CHANGED
|
@@ -15,6 +15,17 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
15
15
|
|
|
16
16
|
const ITERATOR = Symbol('iter');
|
|
17
17
|
const SIZE = Symbol('size');
|
|
18
|
+
|
|
19
|
+
// Neither a registration nor its cleanup record may keep a view or store alive.
|
|
20
|
+
// Initialize lazily so importing N3 and using lazy views needs no weak-reference APIs.
|
|
21
|
+
let observerRegistry;
|
|
22
|
+
function finalizeObserver({
|
|
23
|
+
store,
|
|
24
|
+
observer
|
|
25
|
+
}) {
|
|
26
|
+
const target = store.deref();
|
|
27
|
+
if (target) target._removeObserver(observer);
|
|
28
|
+
}
|
|
18
29
|
function hasInIndex(index0, key0, key1, key2) {
|
|
19
30
|
const index1 = index0 && index0[key0];
|
|
20
31
|
const index2 = index1 && index1[key1];
|
|
@@ -169,11 +180,14 @@ class N3Store {
|
|
|
169
180
|
this._size = 0;
|
|
170
181
|
// `_graphs` contains subject, predicate, and object indexes per graph
|
|
171
182
|
this._graphs = Object.create(null);
|
|
183
|
+
// `_observers` contains weak references to views notified before every mutation
|
|
184
|
+
this._observers = null;
|
|
172
185
|
|
|
173
186
|
// Shift parameters if `quads` is not given
|
|
174
187
|
if (!options && quads && !quads[0] && !(typeof quads.match === 'function')) options = quads, quads = null;
|
|
175
188
|
options = options || {};
|
|
176
189
|
this._factory = options.factory || _N3DataFactory.default;
|
|
190
|
+
this._matchSemantics = validateMatchSemantics(options.matchSemantics);
|
|
177
191
|
this._entityIndex = options.entityIndex || new N3EntityIndex({
|
|
178
192
|
factory: this._factory
|
|
179
193
|
});
|
|
@@ -373,6 +387,38 @@ class N3Store {
|
|
|
373
387
|
};
|
|
374
388
|
}
|
|
375
389
|
|
|
390
|
+
// ### `_addObserver` registers a mutation observer.
|
|
391
|
+
_addObserver(view) {
|
|
392
|
+
const observer = new WeakRef(view);
|
|
393
|
+
const registry = observerRegistry || (observerRegistry = new FinalizationRegistry(finalizeObserver));
|
|
394
|
+
registry.register(view, {
|
|
395
|
+
store: new WeakRef(this),
|
|
396
|
+
observer
|
|
397
|
+
}, observer);
|
|
398
|
+
(this._observers || (this._observers = new Set())).add(observer);
|
|
399
|
+
return observer;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ### `_removeObserver` unregisters a mutation observer.
|
|
403
|
+
_removeObserver(observer) {
|
|
404
|
+
observerRegistry.unregister(observer);
|
|
405
|
+
if (this._observers) {
|
|
406
|
+
this._observers.delete(observer);
|
|
407
|
+
if (this._observers.size === 0) this._observers = null;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// ### `_notifyObservers` notifies observers of a mutation.
|
|
412
|
+
_notifyObservers(subjectId, predicateId, objectId, graphId, added) {
|
|
413
|
+
// Observers can remove themselves during Set iteration
|
|
414
|
+
for (const observer of this._observers) {
|
|
415
|
+
const view = observer.deref();
|
|
416
|
+
if (view) view._onParentMutation(subjectId, predicateId, objectId, graphId, added);else
|
|
417
|
+
// Finalization may be delayed; also prune collected views during notification.
|
|
418
|
+
this._removeObserver(observer);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
376
422
|
// ### `_uniqueEntities` returns a function that accepts an entity ID
|
|
377
423
|
// and passes the corresponding entity to callback if it hasn't occurred before.
|
|
378
424
|
_uniqueEntities(callback) {
|
|
@@ -404,6 +450,15 @@ class N3Store {
|
|
|
404
450
|
// Convert terms to internal string representation
|
|
405
451
|
graph = graph ? this._termToNewNumericId(graph) : 1;
|
|
406
452
|
|
|
453
|
+
// Map long IRIs to shared numeric identifiers before updating the indexes.
|
|
454
|
+
subject = this._termToNewNumericId(subject);
|
|
455
|
+
predicate = this._termToNewNumericId(predicate);
|
|
456
|
+
object = this._termToNewNumericId(object);
|
|
457
|
+
return this._addQuad(subject, predicate, object, graph);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// ### `_addQuad` adds a quad using identifiers from this store's entity index.
|
|
461
|
+
_addQuad(subject, predicate, object, graph) {
|
|
407
462
|
// Find the graph that will contain the triple
|
|
408
463
|
let graphItem = this._graphs[graph];
|
|
409
464
|
// Create the graph if it doesn't exist yet
|
|
@@ -424,12 +479,11 @@ class N3Store {
|
|
|
424
479
|
Object.freeze(graphItem);
|
|
425
480
|
}
|
|
426
481
|
|
|
427
|
-
//
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
object = this._termToNewNumericId(object);
|
|
482
|
+
// Notify observers before inserting a new quad so snapshots retain their prior contents
|
|
483
|
+
if (this._observers !== null) {
|
|
484
|
+
if (hasInIndex(graphItem.subjects, subject, predicate, object)) return false;
|
|
485
|
+
this._notifyObservers(subject, predicate, object, graph, true);
|
|
486
|
+
}
|
|
433
487
|
if (!this._addToIndex(graphItem.subjects, subject, predicate, object)) return false;
|
|
434
488
|
this._addToIndex(graphItem.predicates, predicate, object, subject);
|
|
435
489
|
this._addToIndex(graphItem.objects, object, subject, predicate);
|
|
@@ -490,12 +544,19 @@ class N3Store {
|
|
|
490
544
|
} = subject);
|
|
491
545
|
// Convert terms to internal string representation
|
|
492
546
|
graph = graph ? this._termToNumericId(graph) : 1;
|
|
547
|
+
if (!(subject = subject && this._termToNumericId(subject)) || !(predicate = predicate && this._termToNumericId(predicate)) || !(object = object && this._termToNumericId(object))) return false;
|
|
548
|
+
return this._removeQuad(subject, predicate, object, graph);
|
|
549
|
+
}
|
|
493
550
|
|
|
494
|
-
|
|
495
|
-
|
|
551
|
+
// ### `_removeQuad` removes a quad using identifiers from this store's entity index.
|
|
552
|
+
_removeQuad(subject, predicate, object, graph) {
|
|
553
|
+
// Verify the quad exists before notifying observers or changing any indexes.
|
|
496
554
|
const graphs = this._graphs;
|
|
497
|
-
|
|
498
|
-
if (!
|
|
555
|
+
const graphItem = graphs[graph];
|
|
556
|
+
if (!graphItem || !hasInIndex(graphItem.subjects, subject, predicate, object)) return false;
|
|
557
|
+
|
|
558
|
+
// Notify observers before the mutation
|
|
559
|
+
if (this._observers !== null) this._notifyObservers(subject, predicate, object, graph, false);
|
|
499
560
|
|
|
500
561
|
// Remove it from all indexes
|
|
501
562
|
this._removeFromIndex(graphItem.subjects, subject, predicate, object);
|
|
@@ -593,9 +654,11 @@ class N3Store {
|
|
|
593
654
|
// Note: Since a DatasetCore is an unordered set, the order of the quads within the returned sequence is arbitrary.
|
|
594
655
|
// Setting any field to `undefined` or `null` indicates a wildcard.
|
|
595
656
|
// For backwards compatibility, the object return also implements the Readable stream interface.
|
|
596
|
-
|
|
657
|
+
// `options.matchSemantics` controls how the view reacts to later mutations.
|
|
658
|
+
match(subject, predicate, object, graph, options = null) {
|
|
597
659
|
return new DatasetCoreAndReadableStream(this, subject, predicate, object, graph, {
|
|
598
|
-
entityIndex: this._entityIndex
|
|
660
|
+
entityIndex: this._entityIndex,
|
|
661
|
+
matchSemantics: options && options.matchSemantics !== undefined ? options.matchSemantics : this._matchSemantics
|
|
599
662
|
});
|
|
600
663
|
}
|
|
601
664
|
|
|
@@ -891,7 +954,9 @@ class N3Store {
|
|
|
891
954
|
*/
|
|
892
955
|
addAll(quads) {
|
|
893
956
|
if (quads instanceof DatasetCoreAndReadableStream) quads = quads.filtered;
|
|
894
|
-
if (Array.isArray(quads)) this.addQuads(quads);
|
|
957
|
+
if (Array.isArray(quads)) this.addQuads(quads);
|
|
958
|
+
// Index merging bypasses observer notifications
|
|
959
|
+
else if (this._observers === null && quads instanceof N3Store && quads._entityIndex === this._entityIndex) {
|
|
895
960
|
if (quads._size !== 0) {
|
|
896
961
|
this._graphs = merge(this._graphs, quads._graphs);
|
|
897
962
|
this._size = null; // Invalidate the cached size
|
|
@@ -941,7 +1006,9 @@ class N3Store {
|
|
|
941
1006
|
* @param graph The optional exact graph to match.
|
|
942
1007
|
*/
|
|
943
1008
|
deleteMatches(subject, predicate, object, graph) {
|
|
944
|
-
for (const quad of this.match(subject, predicate, object, graph
|
|
1009
|
+
for (const quad of this.match(subject, predicate, object, graph, {
|
|
1010
|
+
matchSemantics: 'lazy'
|
|
1011
|
+
})) this.removeQuad(quad);
|
|
945
1012
|
return this;
|
|
946
1013
|
}
|
|
947
1014
|
|
|
@@ -1065,7 +1132,9 @@ class N3Store {
|
|
|
1065
1132
|
* Returns a stream that contains all quads of the dataset.
|
|
1066
1133
|
*/
|
|
1067
1134
|
toStream() {
|
|
1068
|
-
return this.match(
|
|
1135
|
+
return this.match(null, null, null, null, {
|
|
1136
|
+
matchSemantics: 'lazy'
|
|
1137
|
+
});
|
|
1069
1138
|
}
|
|
1070
1139
|
|
|
1071
1140
|
/**
|
|
@@ -1126,6 +1195,97 @@ function indexMatch(index, ids, depth = 0) {
|
|
|
1126
1195
|
return target;
|
|
1127
1196
|
}
|
|
1128
1197
|
|
|
1198
|
+
// Capture a read in the same index order as readQuads, without constructing RDF terms.
|
|
1199
|
+
// A flat list avoids allocating a Quad (and its terms) for every unread result.
|
|
1200
|
+
function snapshotMatch(store, subject, predicate, object, graph) {
|
|
1201
|
+
const snapshot = {
|
|
1202
|
+
store,
|
|
1203
|
+
ids: []
|
|
1204
|
+
};
|
|
1205
|
+
const subjectId = subject && store._termToNumericId(subject);
|
|
1206
|
+
const predicateId = predicate && store._termToNumericId(predicate);
|
|
1207
|
+
const objectId = object && store._termToNumericId(object);
|
|
1208
|
+
// Only active reads are frozen: bound terms and graphs have already resolved,
|
|
1209
|
+
// and notifications run before deletion. Entity IDs are never removed.
|
|
1210
|
+
|
|
1211
|
+
// Keep this choice aligned with readQuads: changing order would repeat or skip
|
|
1212
|
+
// results when an iterator resumes partway through its snapshot.
|
|
1213
|
+
let indexName, key0, key1, key2, positions;
|
|
1214
|
+
if (objectId && (subjectId || !predicateId)) {
|
|
1215
|
+
indexName = 'objects';
|
|
1216
|
+
[key0, key1, key2] = [objectId, subjectId, predicateId];
|
|
1217
|
+
positions = [2, 0, 1];
|
|
1218
|
+
} else if (!subjectId && predicateId) {
|
|
1219
|
+
indexName = 'predicates';
|
|
1220
|
+
[key0, key1, key2] = [predicateId, objectId, subjectId];
|
|
1221
|
+
positions = [1, 2, 0];
|
|
1222
|
+
} else {
|
|
1223
|
+
indexName = 'subjects';
|
|
1224
|
+
[key0, key1, key2] = [subjectId, predicateId, objectId];
|
|
1225
|
+
positions = [0, 1, 2];
|
|
1226
|
+
}
|
|
1227
|
+
const graphs = store._getGraphs(graph),
|
|
1228
|
+
parts = [];
|
|
1229
|
+
for (const graphId in graphs) {
|
|
1230
|
+
const index = graphs[graphId][indexName];
|
|
1231
|
+
const graphKey = Number(graphId);
|
|
1232
|
+
for (const value0 in key0 ? {
|
|
1233
|
+
[key0]: index[key0]
|
|
1234
|
+
} : index) {
|
|
1235
|
+
const index1 = index[value0];
|
|
1236
|
+
if (!index1) continue; // eslint-disable-line no-continue
|
|
1237
|
+
parts[positions[0]] = Number(value0);
|
|
1238
|
+
for (const value1 in key1 ? {
|
|
1239
|
+
[key1]: index1[key1]
|
|
1240
|
+
} : index1) {
|
|
1241
|
+
const index2 = index1[value1];
|
|
1242
|
+
if (!index2) continue; // eslint-disable-line no-continue
|
|
1243
|
+
parts[positions[1]] = Number(value1);
|
|
1244
|
+
for (const value2 in key2 ? key2 in index2 ? {
|
|
1245
|
+
[key2]: null
|
|
1246
|
+
} : {} : index2) {
|
|
1247
|
+
parts[positions[2]] = Number(value2);
|
|
1248
|
+
snapshot.ids.push(parts[0], parts[1], parts[2], graphKey);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return snapshot;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// Skip already-yielded IDs in constant time; only materialize the requested tail.
|
|
1257
|
+
function* iterateSnapshot({
|
|
1258
|
+
store,
|
|
1259
|
+
ids
|
|
1260
|
+
}, offset) {
|
|
1261
|
+
const entities = store._entities;
|
|
1262
|
+
let subject, predicate, object, graph;
|
|
1263
|
+
let subjectId, predicateId, objectId, graphId;
|
|
1264
|
+
for (let i = offset * 4; i < ids.length; i += 4) {
|
|
1265
|
+
if (subjectId !== ids[i]) subject = store._termFromId(entities[subjectId = ids[i]]);
|
|
1266
|
+
if (predicateId !== ids[i + 1]) predicate = store._termFromId(entities[predicateId = ids[i + 1]]);
|
|
1267
|
+
if (objectId !== ids[i + 2]) object = store._termFromId(entities[objectId = ids[i + 2]]);
|
|
1268
|
+
if (graphId !== ids[i + 3]) graph = store._termFromId(entities[graphId = ids[i + 3]]);
|
|
1269
|
+
yield store._factory.quad(subject, predicate, object, graph);
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
function validateMatchSemantics(semantics = 'lazy') {
|
|
1273
|
+
if (semantics !== 'lazy' && semantics !== 'snapshot' && semantics !== 'forwarded') throw new Error(`Unknown matchSemantics: ${semantics}`);
|
|
1274
|
+
if (semantics !== 'lazy' && (typeof WeakRef !== 'function' || typeof FinalizationRegistry !== 'function')) throw new Error('Non-lazy matchSemantics requires WeakRef and FinalizationRegistry support');
|
|
1275
|
+
return semantics;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
// Returns the intersection of two quad patterns, or false if they conflict.
|
|
1279
|
+
function intersectMatchPatterns(left, right) {
|
|
1280
|
+
const result = new Array(4);
|
|
1281
|
+
for (let i = 0; i < 4; i++) {
|
|
1282
|
+
const leftTerm = left[i],
|
|
1283
|
+
rightTerm = right[i];
|
|
1284
|
+
if (leftTerm === null || leftTerm === undefined) result[i] = rightTerm;else if (rightTerm === null || rightTerm === undefined || (0, _N3DataFactory.termToId)(leftTerm) === (0, _N3DataFactory.termToId)(rightTerm)) result[i] = leftTerm;else return false;
|
|
1285
|
+
}
|
|
1286
|
+
return result;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1129
1289
|
/**
|
|
1130
1290
|
* A class that implements both DatasetCore and Readable.
|
|
1131
1291
|
*/
|
|
@@ -1142,6 +1302,86 @@ class DatasetCoreAndReadableStream extends _readableStream.Readable {
|
|
|
1142
1302
|
graph,
|
|
1143
1303
|
options
|
|
1144
1304
|
});
|
|
1305
|
+
const semantics = this._semantics = validateMatchSemantics(options.matchSemantics);
|
|
1306
|
+
if (options.matchesNothing) {
|
|
1307
|
+
this._matchesNothing = true;
|
|
1308
|
+
this._filtered = new N3Store({
|
|
1309
|
+
factory: n3Store._factory,
|
|
1310
|
+
entityIndex: options.entityIndex
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
if (semantics !== 'lazy') {
|
|
1314
|
+
// Active reads share a snapshot only when their source changes.
|
|
1315
|
+
this._readers = null;
|
|
1316
|
+
// Cache pattern ids on first use
|
|
1317
|
+
this._subjectId = this._predicateId = this._objectId = this._graphId = undefined;
|
|
1318
|
+
if (!this._matchesNothing) {
|
|
1319
|
+
this._observer = n3Store._addObserver(this);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// ### `_matchesPattern` tests quad ids against this view.
|
|
1325
|
+
_matchesPattern(subjectId, predicateId, objectId, graphId) {
|
|
1326
|
+
const {
|
|
1327
|
+
subject,
|
|
1328
|
+
predicate,
|
|
1329
|
+
object,
|
|
1330
|
+
graph,
|
|
1331
|
+
n3Store
|
|
1332
|
+
} = this;
|
|
1333
|
+
if (subject && subjectId !== (this._subjectId || (this._subjectId = n3Store._termToNumericId(subject)))) return false;
|
|
1334
|
+
if (predicate && predicateId !== (this._predicateId || (this._predicateId = n3Store._termToNumericId(predicate)))) return false;
|
|
1335
|
+
if (object && objectId !== (this._objectId || (this._objectId = n3Store._termToNumericId(object)))) return false;
|
|
1336
|
+
return graph === null || graph === undefined || graphId === (this._graphId || (this._graphId = graph === '' || (0, _N3Util.isDefaultGraph)(graph) ? 1 : n3Store._termToNumericId(graph)));
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// ### `_matchesQuad` tests a Quad against this view.
|
|
1340
|
+
_matchesQuad(quad) {
|
|
1341
|
+
const {
|
|
1342
|
+
subject,
|
|
1343
|
+
predicate,
|
|
1344
|
+
object,
|
|
1345
|
+
graph
|
|
1346
|
+
} = this;
|
|
1347
|
+
return !this._matchesNothing && (subject === null || subject === undefined || (0, _N3DataFactory.termToId)(subject) === (0, _N3DataFactory.termToId)(quad.subject)) && (predicate === null || predicate === undefined || (0, _N3DataFactory.termToId)(predicate) === (0, _N3DataFactory.termToId)(quad.predicate)) && (object === null || object === undefined || (0, _N3DataFactory.termToId)(object) === (0, _N3DataFactory.termToId)(quad.object)) && (graph === null || graph === undefined || (0, _N3DataFactory.termToId)(graph) === (0, _N3DataFactory.termToId)(quad.graph));
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// ### `_assertMatchesPattern` rejects a Quad outside this view.
|
|
1351
|
+
_assertMatchesPattern(quad) {
|
|
1352
|
+
if (!this._matchesQuad(quad)) throw new Error('Quad does not match the forwarded view pattern');
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
// ### `_sourceIterator` returns an iterator over the current backing store.
|
|
1356
|
+
_sourceIterator() {
|
|
1357
|
+
return this._filtered ? this._filtered[Symbol.iterator]() : this.n3Store.readQuads(this.subject, this.predicate, this.object, this.graph);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// ### `_freezeCurrentIterators` freezes only readers still using the live source.
|
|
1361
|
+
_freezeCurrentIterators() {
|
|
1362
|
+
if (this._readers) {
|
|
1363
|
+
this._readers.snapshot = this._filtered ? snapshotMatch(this._filtered) : snapshotMatch(this.n3Store, this.subject, this.predicate, this.object, this.graph);
|
|
1364
|
+
// Each iterator retains its own group. New reads must see the new source;
|
|
1365
|
+
// the view must not retain snapshots belonging to suspended or abandoned reads.
|
|
1366
|
+
this._readers = null;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// ### `_onParentMutation` applies a parent mutation to this view.
|
|
1371
|
+
_onParentMutation(subjectId, predicateId, objectId, graphId, added) {
|
|
1372
|
+
if (!this._matchesPattern(subjectId, predicateId, objectId, graphId)) return;
|
|
1373
|
+
this._freezeCurrentIterators();
|
|
1374
|
+
|
|
1375
|
+
// Keep using the parent until materialization
|
|
1376
|
+
if (this._semantics === 'forwarded') {
|
|
1377
|
+
if (this._filtered) {
|
|
1378
|
+
if (added) this._filtered._addQuad(subjectId, predicateId, objectId, graphId);else this._filtered._removeQuad(subjectId, predicateId, objectId, graphId);
|
|
1379
|
+
}
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// Capture the pre-mutation snapshot
|
|
1384
|
+
this._filtered = this.filtered;
|
|
1145
1385
|
}
|
|
1146
1386
|
get filtered() {
|
|
1147
1387
|
if (!this._filtered) {
|
|
@@ -1152,10 +1392,12 @@ class DatasetCoreAndReadableStream extends _readableStream.Readable {
|
|
|
1152
1392
|
predicate,
|
|
1153
1393
|
subject
|
|
1154
1394
|
} = this;
|
|
1395
|
+
if (this._semantics !== 'lazy') this._freezeCurrentIterators();
|
|
1155
1396
|
const newStore = this._filtered = new N3Store({
|
|
1156
1397
|
factory: n3Store._factory,
|
|
1157
1398
|
entityIndex: this.options.entityIndex
|
|
1158
1399
|
});
|
|
1400
|
+
if (this._semantics === 'snapshot') this._detachObserver();
|
|
1159
1401
|
let subjectId, predicateId, objectId;
|
|
1160
1402
|
|
|
1161
1403
|
// Translate IRIs to internal index keys.
|
|
@@ -1207,13 +1449,55 @@ class DatasetCoreAndReadableStream extends _readableStream.Readable {
|
|
|
1207
1449
|
this.push(value);
|
|
1208
1450
|
}
|
|
1209
1451
|
}
|
|
1452
|
+
|
|
1453
|
+
// ### `_destroy` closes the cached iterator.
|
|
1454
|
+
_destroy(error, callback) {
|
|
1455
|
+
if (this[ITERATOR]) {
|
|
1456
|
+
this[ITERATOR].return();
|
|
1457
|
+
this[ITERATOR] = null;
|
|
1458
|
+
}
|
|
1459
|
+
callback(error);
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// ### `_detachObserver` stops observing the parent store.
|
|
1463
|
+
_detachObserver() {
|
|
1464
|
+
if (this._observer) {
|
|
1465
|
+
this.n3Store._removeObserver(this._observer);
|
|
1466
|
+
this._observer = null;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
// ### `_detach` freezes the view and stops observing the parent.
|
|
1471
|
+
_detach() {
|
|
1472
|
+
this._filtered = this.filtered;
|
|
1473
|
+
this._detachObserver();
|
|
1474
|
+
// Lazy views deferred this state
|
|
1475
|
+
if (this._semantics === 'lazy') this._readers = null;
|
|
1476
|
+
this._semantics = 'snapshot';
|
|
1477
|
+
return this;
|
|
1478
|
+
}
|
|
1210
1479
|
addAll(quads) {
|
|
1480
|
+
if (this._semantics === 'forwarded') {
|
|
1481
|
+
for (const quad of quads) {
|
|
1482
|
+
this._assertMatchesPattern(quad);
|
|
1483
|
+
this.n3Store.addQuad(quad);
|
|
1484
|
+
}
|
|
1485
|
+
return this;
|
|
1486
|
+
}
|
|
1211
1487
|
return this.filtered.addAll(quads);
|
|
1212
1488
|
}
|
|
1213
1489
|
contains(other) {
|
|
1214
1490
|
return this.filtered.contains(other);
|
|
1215
1491
|
}
|
|
1216
1492
|
deleteMatches(subject, predicate, object, graph) {
|
|
1493
|
+
if (this._semantics === 'forwarded') {
|
|
1494
|
+
// No deletion pattern can match a view with conflicting ancestor patterns.
|
|
1495
|
+
if (this._matchesNothing) throw new Error('Cannot delete from a conflicting forwarded view pattern');
|
|
1496
|
+
const pattern = intersectMatchPatterns([this.subject, this.predicate, this.object, this.graph], [subject, predicate, object, graph]);
|
|
1497
|
+
if (!pattern) throw new Error('Deletion pattern does not match the forwarded view pattern');
|
|
1498
|
+
this.n3Store.deleteMatches(...pattern);
|
|
1499
|
+
return this;
|
|
1500
|
+
}
|
|
1217
1501
|
return this.filtered.deleteMatches(subject, predicate, object, graph);
|
|
1218
1502
|
}
|
|
1219
1503
|
difference(other) {
|
|
@@ -1223,57 +1507,131 @@ class DatasetCoreAndReadableStream extends _readableStream.Readable {
|
|
|
1223
1507
|
return this.filtered.equals(other);
|
|
1224
1508
|
}
|
|
1225
1509
|
every(callback, subject, predicate, object, graph) {
|
|
1226
|
-
return this.filtered.every(callback, subject, predicate, object, graph);
|
|
1510
|
+
return this.filtered.every(this._semantics === 'forwarded' ? quad => callback(quad, this) : callback, subject, predicate, object, graph);
|
|
1227
1511
|
}
|
|
1228
1512
|
filter(iteratee) {
|
|
1229
|
-
return this.filtered.filter(iteratee);
|
|
1513
|
+
return this.filtered.filter(this._semantics === 'forwarded' ? quad => iteratee(quad, this) : iteratee);
|
|
1230
1514
|
}
|
|
1231
1515
|
forEach(callback, subject, predicate, object, graph) {
|
|
1232
|
-
return this.filtered.forEach(callback, subject, predicate, object, graph);
|
|
1516
|
+
return this.filtered.forEach(this._semantics === 'forwarded' ? quad => callback(quad, this) : callback, subject, predicate, object, graph);
|
|
1233
1517
|
}
|
|
1234
1518
|
import(stream) {
|
|
1235
|
-
return this.filtered.import(stream);
|
|
1519
|
+
if (this._semantics !== 'forwarded') return this.filtered.import(stream);
|
|
1520
|
+
const view = this;
|
|
1521
|
+
function onData(quad) {
|
|
1522
|
+
try {
|
|
1523
|
+
view._assertMatchesPattern(quad);
|
|
1524
|
+
view.n3Store.addQuad(quad);
|
|
1525
|
+
} catch (error) {
|
|
1526
|
+
// Stop this import; RDF/JS streams need not implement destroy()
|
|
1527
|
+
stream.removeListener('data', onData);
|
|
1528
|
+
if (typeof stream.destroy === 'function') stream.destroy(error);else stream.emit('error', error);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
stream.on('data', onData);
|
|
1532
|
+
return stream;
|
|
1236
1533
|
}
|
|
1237
1534
|
intersection(other) {
|
|
1238
1535
|
return this.filtered.intersection(other);
|
|
1239
1536
|
}
|
|
1240
1537
|
map(iteratee) {
|
|
1241
|
-
return this.filtered.map(iteratee);
|
|
1538
|
+
return this.filtered.map(this._semantics === 'forwarded' ? quad => iteratee(quad, this) : iteratee);
|
|
1242
1539
|
}
|
|
1243
1540
|
some(callback, subject, predicate, object, graph) {
|
|
1244
|
-
return this.filtered.some(callback, subject, predicate, object, graph);
|
|
1541
|
+
return this.filtered.some(this._semantics === 'forwarded' ? quad => callback(quad, this) : callback, subject, predicate, object, graph);
|
|
1245
1542
|
}
|
|
1246
1543
|
toCanonical() {
|
|
1247
1544
|
return this.filtered.toCanonical();
|
|
1248
1545
|
}
|
|
1249
1546
|
toStream() {
|
|
1250
|
-
|
|
1547
|
+
if (this._semantics !== 'lazy')
|
|
1548
|
+
// Use a fresh sync iterator instead of consuming this view's own readable stream.
|
|
1549
|
+
return _readableStream.Readable.from(this[Symbol.iterator]());
|
|
1550
|
+
return this._filtered ? this._filtered.toStream() : this.n3Store.match(this.subject, this.predicate, this.object, this.graph, {
|
|
1551
|
+
matchSemantics: 'lazy'
|
|
1552
|
+
});
|
|
1251
1553
|
}
|
|
1252
1554
|
union(quads) {
|
|
1253
|
-
return this._filtered ? this._filtered.union(quads) : this.n3Store.match(this.subject, this.predicate, this.object, this.graph
|
|
1555
|
+
return this._filtered ? this._filtered.union(quads) : this.n3Store.match(this.subject, this.predicate, this.object, this.graph, {
|
|
1556
|
+
matchSemantics: 'lazy'
|
|
1557
|
+
}).addAll(quads);
|
|
1254
1558
|
}
|
|
1255
1559
|
toArray() {
|
|
1560
|
+
if (this._semantics !== 'lazy') return [...this];
|
|
1256
1561
|
return this._filtered ? this._filtered.toArray() : this.n3Store.getQuads(this.subject, this.predicate, this.object, this.graph);
|
|
1257
1562
|
}
|
|
1258
1563
|
reduce(callback, initialValue) {
|
|
1259
|
-
return this.filtered.reduce(callback, initialValue);
|
|
1564
|
+
return this.filtered.reduce(this._semantics === 'forwarded' ? (accumulator, quad) => callback(accumulator, quad, this) : callback, initialValue);
|
|
1260
1565
|
}
|
|
1261
1566
|
toString() {
|
|
1262
1567
|
return new _N3Writer.default().quadsToString(this);
|
|
1263
1568
|
}
|
|
1264
1569
|
add(quad) {
|
|
1570
|
+
if (this._semantics === 'forwarded') {
|
|
1571
|
+
this._assertMatchesPattern(quad);
|
|
1572
|
+
this.n3Store.addQuad(quad);
|
|
1573
|
+
return this;
|
|
1574
|
+
}
|
|
1265
1575
|
return this.filtered.add(quad);
|
|
1266
1576
|
}
|
|
1267
1577
|
delete(quad) {
|
|
1578
|
+
if (this._semantics === 'forwarded') {
|
|
1579
|
+
this._assertMatchesPattern(quad);
|
|
1580
|
+
this.n3Store.removeQuad(quad);
|
|
1581
|
+
return this;
|
|
1582
|
+
}
|
|
1268
1583
|
return this.filtered.delete(quad);
|
|
1269
1584
|
}
|
|
1270
1585
|
has(quad) {
|
|
1271
1586
|
return this.filtered.has(quad);
|
|
1272
1587
|
}
|
|
1273
|
-
match(subject, predicate, object, graph) {
|
|
1274
|
-
|
|
1588
|
+
match(subject, predicate, object, graph, options = null) {
|
|
1589
|
+
const requestedSemantics = options && options.matchSemantics;
|
|
1590
|
+
if (options && requestedSemantics !== undefined) {
|
|
1591
|
+
validateMatchSemantics(requestedSemantics);
|
|
1592
|
+
if (requestedSemantics !== this._semantics) throw new Error(`Cannot override matchSemantics on a view: inherited "${this._semantics}", received "${requestedSemantics}"`);
|
|
1593
|
+
}
|
|
1594
|
+
if (this._semantics !== 'forwarded') return new DatasetCoreAndReadableStream(this.filtered, subject, predicate, object, graph, {
|
|
1595
|
+
entityIndex: this.options.entityIndex,
|
|
1596
|
+
matchSemantics: this._semantics
|
|
1597
|
+
});
|
|
1598
|
+
const pattern = !this._matchesNothing && intersectMatchPatterns([this.subject, this.predicate, this.object, this.graph], [subject, predicate, object, graph]);
|
|
1599
|
+
const [matchedSubject, matchedPredicate, matchedObject, matchedGraph] = pattern || [null, null, null, null];
|
|
1600
|
+
return new DatasetCoreAndReadableStream(this.n3Store, matchedSubject, matchedPredicate, matchedObject, matchedGraph, {
|
|
1601
|
+
entityIndex: this.options.entityIndex,
|
|
1602
|
+
matchSemantics: 'forwarded',
|
|
1603
|
+
matchesNothing: !pattern
|
|
1604
|
+
});
|
|
1275
1605
|
}
|
|
1276
1606
|
[Symbol.iterator]() {
|
|
1277
|
-
return this.
|
|
1607
|
+
return this._semantics === 'lazy' ? this._sourceIterator() : this._iterateStable();
|
|
1608
|
+
}
|
|
1609
|
+
*_iterateStable() {
|
|
1610
|
+
const readers = this._readers || (this._readers = {
|
|
1611
|
+
count: 0,
|
|
1612
|
+
snapshot: null
|
|
1613
|
+
});
|
|
1614
|
+
const source = this._sourceIterator();
|
|
1615
|
+
let yielded = 0;
|
|
1616
|
+
readers.count++;
|
|
1617
|
+
try {
|
|
1618
|
+
while (!readers.snapshot) {
|
|
1619
|
+
const {
|
|
1620
|
+
value,
|
|
1621
|
+
done
|
|
1622
|
+
} = source.next();
|
|
1623
|
+
// A custom factory can mutate the store inside next(), even on its first call.
|
|
1624
|
+
if (readers.snapshot || done) break;
|
|
1625
|
+
yielded++;
|
|
1626
|
+
yield value;
|
|
1627
|
+
}
|
|
1628
|
+
if (readers.snapshot) yield* iterateSnapshot(readers.snapshot, yielded);
|
|
1629
|
+
} finally {
|
|
1630
|
+
source.return();
|
|
1631
|
+
if (--readers.count === 0) {
|
|
1632
|
+
readers.snapshot = null;
|
|
1633
|
+
if (this._readers === readers) this._readers = null;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1278
1636
|
}
|
|
1279
1637
|
}
|