Skip to content

Commit

Permalink
perf: perform difference using internals (#451)
Browse files Browse the repository at this point in the history
* perf: perform difference using internals

* chore: improve test coverage for #difference

* chore: fix tests

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: update docs

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
jeswr and coderabbitai[bot] authored Sep 21, 2024
1 parent 48cb127 commit 04d55c5
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/N3Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,40 @@ function intersect(s1, s2, depth = 4) {
return target;
}

/**
* Determines the difference of the `_graphs` index s1 and s2.
* s1 and s2 *must* belong to Stores that share an `_entityIndex`.
*
* False is returned when there is no difference; this should
* *not* be set as the value for an index.
*/
function difference(s1, s2, depth = 4) {
let target = false;

for (const key in s1) {
// When the key is not in the index, then none of the triples defined by s1[key] are
// in s2 and so we want to copy them over to the resultant store.
if (!(key in s2)) {
target = target || Object.create(null);
target[key] = depth === 0 ? null : merge({}, s1[key], depth - 1);
}
else if (depth !== 0) {
const diff = difference(s1[key], s2[key], depth - 1);
if (diff !== false) {
target = target || Object.create(null);
target[key] = diff;
}
// Depth 3 is the 'subjects', 'predicates' and 'objects' keys.
// If the 'subjects' index is empty, so will the 'predicates' and 'objects' index.
else if (depth === 3) {
return false;
}
}
}

return target;
}

// ## Constructor
export class N3EntityIndex {
constructor(options = {}) {
Expand Down Expand Up @@ -896,6 +930,16 @@ export default class N3Store {
if (other === this)
return new N3Store({ entityIndex: this._entityIndex });

if ((other instanceof N3Store) && other._entityIndex === this._entityIndex) {
const store = new N3Store({ entityIndex: this._entityIndex });
const graphs = difference(this._graphs, other._graphs);
if (graphs) {
store._graphs = graphs;
store._size = null;
}
return store;
}

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

Expand Down
19 changes: 19 additions & 0 deletions test/N3Store-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,25 @@ describe('Store', () => {

expect(store.difference(store).size).toEqual(0);
expect(store2.difference(store2).size).toEqual(0);
expect(store.difference(new Store([...store])).size).toEqual(0);
expect(store2.difference(new Store([...store2])).size).toEqual(0);

const stores = [store, store1, store2, store3, store4, storeb, storeg, empty];
for (const s1 of stores) {
for (const s2 of stores) {
expect(s1.difference(s2).size).toBeLessThanOrEqual(s1.size);
expect(s1.difference(s2)._graphs).toBeTruthy();
expect(s1.union(s2).difference(s1).equals(s2.difference(s1))).toBe(true);
expect(s1.difference(s2).union(s1).equals(s1)).toBe(true);
expect(new Store([...s1.union(s2).difference(s1)]).equals(new Store([...s2.difference(s1)]))).toBe(true);
expect(new Store([...s1.difference(s2).union(s1)]).equals(new Store([...s1]))).toBe(true);

const newStore = s1.difference(s2);
const size = newStore.size;
newStore.add(new Quad(new NamedNode('mys1'), new NamedNode('myp1'), new NamedNode('myo1')));
expect(newStore.size).toBe(size + 1);
}
}
});
});

Expand Down

0 comments on commit 04d55c5

Please sign in to comment.