Skip to content

Commit d38dc45

Browse files
jeswrRubenVerborgh
andauthored
feat: asynchronously yield results (#275)
* feat: asyncronously yeild results * chore add _yieldQuads documentation * chore: optimize has method and iterator on main store * chore: rename variable in comment * chore remove unecessary return * Extend has to quad patterns. * Simplify some, and thereby _findInIndex signature. * Make readQuads a public method. Co-authored-by: Ruben Verborgh <ruben@verborgh.org>
1 parent d5f15e6 commit d38dc45

3 files changed

Lines changed: 50 additions & 91 deletions

File tree

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,12 @@ store.addQuad(
293293
namedNode('http://ex.org/Mouse')
294294
);
295295

296-
const mickey = store.getQuads(namedNode('http://ex.org/Mickey'), null, null)[0];
297-
console.log(mickey);
296+
// Retrieve all quads
297+
for (const quad of store)
298+
console.log(quad);
299+
// Retrieve Mickey's quads
300+
for (const quad of store.readQuads(namedNode('http://ex.org/Mickey'), null, null))
301+
console.log(quad);
298302
```
299303

300304
### Addition and deletion of quads
@@ -312,6 +316,7 @@ The store provides the following manipulation methods
312316
### Searching quads or entities
313317
The store provides the following search methods
314318
([documentation](http://rdfjs.github.io/N3.js/docs/N3Store.html)):
319+
- `readQuads` returns a generator of quads matching the given pattern
315320
- `getQuads` returns an array of quads matching the given pattern
316321
- `match` returns a stream of quads matching the given pattern
317322
- `countQuads` counts the number of quads matching the given pattern

src/N3Store.js

Lines changed: 34 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,13 @@ export default class N3Store {
8484
// `name0`, `name1`, and `name2` are the names of the keys at each level,
8585
// used when reconstructing the resulting quad
8686
// (for instance: _subject_, _predicate_, and _object_).
87-
// Finally, `graph` will be the graph of the created quads.
88-
// If `callback` is given, each result is passed through it
89-
// and iteration halts when it returns truthy for any quad.
90-
// If instead `array` is given, each result is added to the array.
91-
_findInIndex(index0, key0, key1, key2, name0, name1, name2, graph, callback, array) {
87+
// Finally, `graphId` will be the graph of the created quads.
88+
*_findInIndex(index0, key0, key1, key2, name0, name1, name2, graphId) {
9289
let tmp, index1, index2;
9390
// Depending on the number of variables, keys or reverse index are faster
9491
const varCount = !key0 + !key1 + !key2,
9592
entityKeys = varCount > 1 ? Object.keys(this._ids) : this._entities;
93+
const graph = termFromId(graphId, this._factory);
9694

9795
// If a key is specified, use only that part of index 0.
9896
if (key0) (tmp = index0, index0 = {})[key0] = tmp[key0];
@@ -114,18 +112,12 @@ export default class N3Store {
114112
parts[name0] = termFromId(entity0, this._factory);
115113
parts[name1] = termFromId(entity1, this._factory);
116114
parts[name2] = termFromId(entityKeys[values[l]], this._factory);
117-
const quad = this._factory.quad(
118-
parts.subject, parts.predicate, parts.object, termFromId(graph, this._factory));
119-
if (array)
120-
array.push(quad);
121-
else if (callback(quad))
122-
return true;
115+
yield this._factory.quad(parts.subject, parts.predicate, parts.object, graph);
123116
}
124117
}
125118
}
126119
}
127120
}
128-
return array;
129121
}
130122

131123
// ### `_loop` executes the callback on all keys of index 0
@@ -274,11 +266,11 @@ export default class N3Store {
274266
return this;
275267
}
276268

277-
// ### `has` determines whether a dataset includes a certain quad.
278-
// Returns true or false as appropriate.
279-
has(quad) {
280-
const quads = this.getQuads(quad.subject, quad.predicate, quad.object, quad.graph);
281-
return quads.length !== 0;
269+
// ### `has` determines whether a dataset includes a certain quad or quad pattern.
270+
has(subjectOrQuad, predicate, object, graph) {
271+
if (subjectOrQuad && subjectOrQuad.subject)
272+
({ subject: subjectOrQuad, predicate, object, graph } = subjectOrQuad);
273+
return !this.readQuads(subjectOrQuad, predicate, object, graph).next().done;
282274
}
283275

284276
// ### `import` adds a stream of quads to the store
@@ -341,7 +333,7 @@ export default class N3Store {
341333
const stream = new Readable({ objectMode: true });
342334

343335
stream._read = () => {
344-
for (const quad of this.getQuads(subject, predicate, object, graph))
336+
for (const quad of this.readQuads(subject, predicate, object, graph))
345337
stream.push(quad);
346338
stream.push(null);
347339
};
@@ -357,20 +349,26 @@ export default class N3Store {
357349
// ### `getQuads` returns an array of quads matching a pattern.
358350
// Setting any field to `undefined` or `null` indicates a wildcard.
359351
getQuads(subject, predicate, object, graph) {
352+
return [...this.readQuads(subject, predicate, object, graph)];
353+
}
354+
355+
// ### `readQuads` returns an generator of quads matching a pattern.
356+
// Setting any field to `undefined` or `null` indicates a wildcard.
357+
*readQuads(subject, predicate, object, graph) {
360358
// Convert terms to internal string representation
361359
subject = subject && termToId(subject);
362360
predicate = predicate && termToId(predicate);
363361
object = object && termToId(object);
364362
graph = graph && termToId(graph);
365363

366-
const quads = [], graphs = this._getGraphs(graph), ids = this._ids;
364+
const graphs = this._getGraphs(graph), ids = this._ids;
367365
let content, subjectId, predicateId, objectId;
368366

369367
// Translate IRIs to internal index keys.
370368
if (isString(subject) && !(subjectId = ids[subject]) ||
371369
isString(predicate) && !(predicateId = ids[predicate]) ||
372370
isString(object) && !(objectId = ids[object]))
373-
return quads;
371+
return;
374372

375373
for (const graphId in graphs) {
376374
// Only if the specified graph contains triples, there can be results
@@ -379,28 +377,27 @@ export default class N3Store {
379377
if (subjectId) {
380378
if (objectId)
381379
// If subject and object are given, the object index will be the fastest
382-
this._findInIndex(content.objects, objectId, subjectId, predicateId,
383-
'object', 'subject', 'predicate', graphId, null, quads);
380+
yield* this._findInIndex(content.objects, objectId, subjectId, predicateId,
381+
'object', 'subject', 'predicate', graphId, null, true);
384382
else
385383
// If only subject and possibly predicate are given, the subject index will be the fastest
386-
this._findInIndex(content.subjects, subjectId, predicateId, null,
387-
'subject', 'predicate', 'object', graphId, null, quads);
384+
yield* this._findInIndex(content.subjects, subjectId, predicateId, null,
385+
'subject', 'predicate', 'object', graphId, null, true);
388386
}
389387
else if (predicateId)
390388
// If only predicate and possibly object are given, the predicate index will be the fastest
391-
this._findInIndex(content.predicates, predicateId, objectId, null,
392-
'predicate', 'object', 'subject', graphId, null, quads);
389+
yield* this._findInIndex(content.predicates, predicateId, objectId, null,
390+
'predicate', 'object', 'subject', graphId, null, true);
393391
else if (objectId)
394392
// If only object is given, the object index will be the fastest
395-
this._findInIndex(content.objects, objectId, null, null,
396-
'object', 'subject', 'predicate', graphId, null, quads);
393+
yield* this._findInIndex(content.objects, objectId, null, null,
394+
'object', 'subject', 'predicate', graphId, null, true);
397395
else
398396
// If nothing is given, iterate subjects and predicates first
399-
this._findInIndex(content.subjects, null, null, null,
400-
'subject', 'predicate', 'object', graphId, null, quads);
397+
yield* this._findInIndex(content.subjects, null, null, null,
398+
'subject', 'predicate', 'object', graphId, null, true);
401399
}
402400
}
403-
return quads;
404401
}
405402

406403
// ### `match` returns a new dataset that is comprised of all quads in the current instance matching the given arguments.
@@ -481,60 +478,9 @@ export default class N3Store {
481478
// and returns `true` if it returns truthy for any of them.
482479
// Setting any field to `undefined` or `null` indicates a wildcard.
483480
some(callback, subject, predicate, object, graph) {
484-
// Convert terms to internal string representation
485-
subject = subject && termToId(subject);
486-
predicate = predicate && termToId(predicate);
487-
object = object && termToId(object);
488-
graph = graph && termToId(graph);
489-
490-
const graphs = this._getGraphs(graph), ids = this._ids;
491-
let content, subjectId, predicateId, objectId;
492-
493-
// Translate IRIs to internal index keys.
494-
if (isString(subject) && !(subjectId = ids[subject]) ||
495-
isString(predicate) && !(predicateId = ids[predicate]) ||
496-
isString(object) && !(objectId = ids[object]))
497-
return false;
498-
499-
for (const graphId in graphs) {
500-
// Only if the specified graph contains triples, there can be results
501-
if (content = graphs[graphId]) {
502-
// Choose the optimal index, based on what fields are present
503-
if (subjectId) {
504-
if (objectId) {
505-
// If subject and object are given, the object index will be the fastest
506-
if (this._findInIndex(content.objects, objectId, subjectId, predicateId,
507-
'object', 'subject', 'predicate', graphId, callback, null))
508-
return true;
509-
}
510-
else
511-
// If only subject and possibly predicate are given, the subject index will be the fastest
512-
if (this._findInIndex(content.subjects, subjectId, predicateId, null,
513-
'subject', 'predicate', 'object', graphId, callback, null))
514-
return true;
515-
}
516-
else if (predicateId) {
517-
// If only predicate and possibly object are given, the predicate index will be the fastest
518-
if (this._findInIndex(content.predicates, predicateId, objectId, null,
519-
'predicate', 'object', 'subject', graphId, callback, null)) {
520-
return true;
521-
}
522-
}
523-
else if (objectId) {
524-
// If only object is given, the object index will be the fastest
525-
if (this._findInIndex(content.objects, objectId, null, null,
526-
'object', 'subject', 'predicate', graphId, callback, null)) {
527-
return true;
528-
}
529-
}
530-
else
531-
// If nothing is given, iterate subjects and predicates first
532-
if (this._findInIndex(content.subjects, null, null, null,
533-
'subject', 'predicate', 'object', graphId, callback, null)) {
534-
return true;
535-
}
536-
}
537-
}
481+
for (const quad of this.readQuads(subject, predicate, object, graph))
482+
if (callback(quad))
483+
return true;
538484
return false;
539485
}
540486

@@ -820,7 +766,7 @@ export default class N3Store {
820766
// Can be used where iterables are expected: for...of loops, array spread operator,
821767
// `yield*`, and destructuring assignment (order is not guaranteed).
822768
*[Symbol.iterator]() {
823-
yield* this.getQuads();
769+
yield* this.readQuads();
824770
}
825771
}
826772

@@ -851,7 +797,7 @@ class DatasetCoreAndReadableStream extends Readable {
851797
}
852798

853799
_read() {
854-
for (const quad of this.filtered.getQuads())
800+
for (const quad of this)
855801
this.push(quad);
856802
this.push(null);
857803
}
@@ -873,6 +819,6 @@ class DatasetCoreAndReadableStream extends Readable {
873819
}
874820

875821
*[Symbol.iterator]() {
876-
yield* this.filtered.getQuads();
822+
yield* this._filtered || this.n3Store.readQuads(this.subject, this.predicate, this.object, this.graph);
877823
}
878824
}

test/N3Store-test.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,16 @@ describe('Store', () => {
175175

176176
describe('adding a triple that did not exist yet', () => {
177177
it('should return true', () => {
178+
store.has(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o4'))).should.be.false;
179+
store.has(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o4')).should.be.false;
180+
store.has(null, null, new NamedNode('o4')).should.be.false;
181+
178182
store.addQuad('s1', 'p1', 'o4').should.be.true;
183+
184+
store.has(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o4'))).should.be.true;
185+
store.has(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o4')).should.be.true;
186+
store.has(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o4'), new DefaultGraph()).should.be.true;
187+
store.has(null, null, new NamedNode('o4')).should.be.true;
179188
});
180189

181190
it('should increase the size', () => {
@@ -192,7 +201,6 @@ describe('Store', () => {
192201

193202
it('should return self', () => {
194203
should.equal(store.add(new Quad(new NamedNode('s2'), new NamedNode('p2'), new NamedNode('o2'))), store);
195-
store.has(new Quad(new NamedNode('s2'), new NamedNode('p2'), new NamedNode('o2')));
196204
});
197205

198206
it('should increase the size', () => {

0 commit comments

Comments
 (0)