Skip to content

Commit

Permalink
perf: performance improvements on dataset operations by operating dir…
Browse files Browse the repository at this point in the history
…ectly over graphs (#416)

* perf: performance improvements on dataset operations by operating directly over graphs

* chore: fix braces

* chore: remove double space
  • Loading branch information
jeswr authored Aug 1, 2024
1 parent 7ad4a72 commit 4868ab6
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
32 changes: 30 additions & 2 deletions src/N3Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ import N3Writer from './N3Writer';

const ITERATOR = Symbol('iter');

function merge(target, source, depth = 4) {
if (depth === 0)
return Object.assign(target, source);

for (const key in source)
target[key] = merge(target[key] || Object.create(null), source[key], depth - 1);

return target;
}

// ## Constructor
export class N3EntityIndex {
constructor(options = {}) {
Expand Down Expand Up @@ -792,21 +802,29 @@ export default class N3Store {
addAll(quads) {
if (Array.isArray(quads))
this.addQuads(quads);
else if (quads instanceof N3Store && quads._entityIndex === this._entityIndex) {
if (quads._size !== 0) {
this._graphs = merge(this._graphs, quads._graphs);
this._size = null; // Invalidate the cached size
}
}
else {
for (const quad of quads)
this.add(quad);
}
return this;
}


/**
* Returns `true` if the current dataset is a superset of the given dataset; in other words, returns `true` if
* the given dataset is a subset of, i.e., is contained within, the current dataset.
*
* Blank Nodes will be normalized.
*/
contains(other) {
if (other === this)
return true;

if (!(other instanceof N3Store) || this._entityIndex !== other._entityIndex)
return other.every(quad => this.has(quad));

Expand Down Expand Up @@ -848,6 +866,9 @@ export default class N3Store {
* Returns a new dataset that contains all quads from the current dataset that are not included in the given dataset.
*/
difference(other) {
if (other === this)
return new N3Store({ entityIndex: this._entityIndex });

return this.filter(quad => !other.has(quad));
}

Expand Down Expand Up @@ -877,6 +898,11 @@ export default class N3Store {
* Returns a new dataset containing all quads from the current dataset that are also included in the given dataset.
*/
intersection(other) {
if (other === this) {
const store = new N3Store({ entityIndex: this._entityIndex });
store._graphs = merge(Object.create(null), this._graphs);
store._size = this._size;
}
return this.filter(quad => other.has(quad));
}

Expand Down Expand Up @@ -947,7 +973,9 @@ export default class N3Store {
*/
union(quads) {
const store = new N3Store({ entityIndex: this._entityIndex });
store.addAll(this);
store._graphs = merge(Object.create(null), this._graphs);
store._size = this._size;

store.addAll(quads);
return store;
}
Expand Down
10 changes: 10 additions & 0 deletions test/N3Store-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2111,6 +2111,10 @@ describe('Store', () => {
expect(store1.size).toEqual(2);
expect(store2.size).toEqual(2);
expect(store.size).toEqual(3);

expect(store.union(store).size).toEqual(3);
expect(store.union(empty).size).toEqual(3);
expect(store1.union(empty).size).toEqual(2);
});
});

Expand All @@ -2120,6 +2124,9 @@ describe('Store', () => {
expect(store1.size).toEqual(2);
expect(store2.size).toEqual(2);
expect(store.size).toEqual(1);

expect(store.difference(store).size).toEqual(0);
expect(store2.difference(store2).size).toEqual(0);
});
});

Expand All @@ -2129,6 +2136,9 @@ describe('Store', () => {
expect(store1.size).toEqual(2);
expect(store2.size).toEqual(2);
expect(store.size).toEqual(1);

expect(store.intersection(store).size).toEqual(1);
expect(store2.intersection(store2).size).toEqual(2);
});
});

Expand Down

0 comments on commit 4868ab6

Please sign in to comment.