From a9c1a796ea6eab1ef690444c52a3c8a4df8c863f Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:55:41 +1000 Subject: [PATCH 1/3] WIP: set --- src/N3Store.js | 36 +++++++++++++++++++++--------------- test.mjs | 2 ++ test/N3Store-test.js | 9 +++++++++ 3 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 test.mjs diff --git a/src/N3Store.js b/src/N3Store.js index 46236cba..36943d9f 100644 --- a/src/N3Store.js +++ b/src/N3Store.js @@ -8,11 +8,17 @@ import N3Writer from './N3Writer'; const ITERATOR = Symbol('iter'); function merge(target, source, depth = 4) { - if (depth === 0) - return Object.assign(target, source); + if (depth === 0) { + target ||= new Set(); + for (const key of source) + target.add(key) + return target; + } + + target ||= Object.create(null); for (const key in source) - target[key] = merge(target[key] || Object.create(null), source[key], depth - 1); + target[key] = merge(target[key], source[key], depth - 1); return target; } @@ -132,7 +138,7 @@ export default class N3Store { for (const graphKey in graphs) for (const subjectKey in (subjects = graphs[graphKey].subjects)) for (const predicateKey in (subject = subjects[subjectKey])) - size += Object.keys(subject[predicateKey]).length; + size += subject[predicateKey].size; return this._size = size; } @@ -143,11 +149,11 @@ export default class N3Store { _addToIndex(index0, key0, key1, key2) { // Create layers as necessary const index1 = index0[key0] || (index0[key0] = {}); - const index2 = index1[key1] || (index1[key1] = {}); + const index2 = index1[key1] || (index1[key1] = new Set()); // Setting the key to _any_ value signals the presence of the quad - const existed = key2 in index2; + const existed = index2.has(key2); if (!existed) - index2[key2] = null; + index2.add(key2); return !existed; } @@ -155,10 +161,10 @@ export default class N3Store { _removeFromIndex(index0, key0, key1, key2) { // Remove the quad from the index const index1 = index0[key0], index2 = index1[key1]; - delete index2[key2]; + index2.delete(key2); // Remove intermediary index layers if they are empty - for (const key in index2) return; + if (index2.size > 0) return; delete index1[key1]; for (const key in index1) return; delete index0[key0]; @@ -188,10 +194,10 @@ export default class N3Store { if (index2 = index1[value1]) { parts[name1] = this._termFromId(entityKeys[value1]); // If a key is specified, use only that part of index 2, if it exists. - const values = key2 ? (key2 in index2 ? [key2] : []) : Object.keys(index2); + const values = key2 ? (index2.has(key2) ? [key2] : []) : index2; // Create quads for all items found in index 2. - for (let l = 0; l < values.length; l++) { - parts[name2] = this._termFromId(entityKeys[values[l]]); + for (const o of values) { + parts[name2] = this._termFromId(entityKeys[o]); yield this._factory.quad(parts.subject, parts.predicate, parts.object, graph); } } @@ -229,7 +235,7 @@ export default class N3Store { _loopBy2Keys(index0, key0, key1, callback) { let index1, index2, key2; if ((index1 = index0[key0]) && (index2 = index1[key1])) { - for (key2 in index2) + for (key2 of index2) callback(key2); } } @@ -249,9 +255,9 @@ export default class N3Store { for (const value1 in index1) { if (index2 = index1[value1]) { // If a key is specified, count the quad if it exists - if (key2) (key2 in index2) && count++; + if (key2) (index2.has(key2)) && count++; // Otherwise, count all quads - else count += Object.keys(index2).length; + else count += index2.size; } } } diff --git a/test.mjs b/test.mjs new file mode 100644 index 00000000..7ac5ccaa --- /dev/null +++ b/test.mjs @@ -0,0 +1,2 @@ +const set = new Set([1]) +console.log(set.has(1)) diff --git a/test/N3Store-test.js b/test/N3Store-test.js index 3fb3aaf9..322fac04 100644 --- a/test/N3Store-test.js +++ b/test/N3Store-test.js @@ -14,6 +14,15 @@ import { Readable } from 'readable-stream'; import arrayifyStream from 'arrayify-stream'; describe('Store', () => { + + describe('A store w. one elemnt', () => { + it('should have size 0', () => { + const store = new Store(); + expect(store.addQuad(new Quad(new NamedNode('s1'), new NamedNode('p2'), new NamedNode('o2')))).toBe(true); + expect(store.size).toEqual(1); + }); + }); + describe('The Store export', () => { it('should be a function', () => { expect(Store).toBeInstanceOf(Function); From 08bf9c17eb4c3b5492ae23470e0ef55e9622b079 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:24:02 +1000 Subject: [PATCH 2/3] feat: working use of set instead of obj --- src/N3Reasoner.js | 2 +- src/N3Store.js | 9 ++-- test.mjs | 2 - test/N3Reasoner-test.js | 2 +- test/N3Store-test.js | 94 +++++++++++++++++++++-------------------- 5 files changed, 53 insertions(+), 56 deletions(-) delete mode 100644 test.mjs diff --git a/src/N3Reasoner.js b/src/N3Reasoner.js index c3483e0c..d663b8c0 100644 --- a/src/N3Reasoner.js +++ b/src/N3Reasoner.js @@ -39,7 +39,7 @@ export default class N3Reasoner { if (index2 = index1[value]) { if (v1) val1.value = Number(value); v2 = !(value = val2.value); - for (value in v2 ? index2 : { [value]: index2[value] }) { + for (value of (v2 ? index2 : [value])) { if (v2) val2.value = Number(value); if (i === rule.premise.length - 1) diff --git a/src/N3Store.js b/src/N3Store.js index 36943d9f..b3f51043 100644 --- a/src/N3Store.js +++ b/src/N3Store.js @@ -374,7 +374,7 @@ export default class N3Store { !(object = object && this._termToNumericId(object)) || !(graphItem = graphs[graph]) || !(subjects = graphItem.subjects[subject]) || !(predicates = subjects[predicate]) || - !(object in predicates)) + !predicates.has(object)) return false; // Remove it from all indexes @@ -840,12 +840,9 @@ export default class N3Store { s1 = s1.subjects; for (const subject in (s2 = g2[graph].subjects)) { if (!(p1 = s1[subject])) return false; - for (const predicate in (p2 = s2[subject])) { - if (!(o1 = p1[predicate])) return false; - for (const object in p2[predicate]) - if (!(object in o1)) return false; + for (const predicate in (p2 = s2[subject])) + if (!(o1 = p1[predicate]) || !p2[predicate].isSubsetOf(o1)) return false; } - } } return true; } diff --git a/test.mjs b/test.mjs deleted file mode 100644 index 7ac5ccaa..00000000 --- a/test.mjs +++ /dev/null @@ -1,2 +0,0 @@ -const set = new Set([1]) -console.log(set.has(1)) diff --git a/test/N3Reasoner-test.js b/test/N3Reasoner-test.js index 29b716be..7d0b74a4 100644 --- a/test/N3Reasoner-test.js +++ b/test/N3Reasoner-test.js @@ -302,7 +302,7 @@ describe('Reasoner', () => { it('Should correctly apply the deep taxonomy benchmark', async () => { for (let i = 0; i < 5; i++) { - const store = generateDeepTaxonomy(10 ** i); + const store = new Store([...generateDeepTaxonomy(10 ** i)]); new Reasoner(store).reason(SUBCLASS_RULE); diff --git a/test/N3Store-test.js b/test/N3Store-test.js index 322fac04..ad65a6cd 100644 --- a/test/N3Store-test.js +++ b/test/N3Store-test.js @@ -14,7 +14,6 @@ import { Readable } from 'readable-stream'; import arrayifyStream from 'arrayify-stream'; describe('Store', () => { - describe('A store w. one elemnt', () => { it('should have size 0', () => { const store = new Store(); @@ -2030,6 +2029,9 @@ describe('Store', () => { store.add(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o2'))); store.add(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o4'))); expect(m.next().value).toEqual(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o3'))); + expect(m.next().value).toEqual(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o0'))); + expect(m.next().value).toEqual(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o2'))); + expect(m.next().value).toEqual(new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o4'))); expect(m.next().done).toBe(true); expect([...store.match(null, null, null, null)]).toHaveLength(5); }, @@ -2307,51 +2309,51 @@ describe('Store', () => { }); }); -describe('EntityIndex', () => { - let entityIndex; - beforeEach(() => { - entityIndex = new EntityIndex(); - }); - - it('should be a constructor', () => { - expect(entityIndex).toBeInstanceOf(EntityIndex); - }); - - it('custom index should be used when instantiated with store', () => { - const index = { - '': 1, - 's1': 2, - 'p1': 3, - 'o0': 4, - 's2': 5, - 'p2': 6, - 'o2': 7, - }; - - const store = new Store([ - new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o0')), - ], { entityIndex }); - expect(store.size).toBe(1); - expect(entityIndex._id).toEqual(4); - - const substore = store.match(); - substore.add(new Quad(new NamedNode('s2'), new NamedNode('p2'), new NamedNode('o2'))); - expect(store.size).toBe(1); - expect(substore.size).toBe(2); - expect(entityIndex._id).toEqual(7); - expect(entityIndex._ids).toEqual(index); - - const store2 = new Store([ - new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o5')), - ], { entityIndex }); - expect(store2.size).toBe(1); - expect(entityIndex._id).toEqual(8); - expect(entityIndex._ids).toEqual({ - ...index, - o5: 8, - }); - }); -}); +// describe('EntityIndex', () => { +// let entityIndex; +// beforeEach(() => { +// entityIndex = new EntityIndex(); +// }); + +// it('should be a constructor', () => { +// expect(entityIndex).toBeInstanceOf(EntityIndex); +// }); + +// it('custom index should be used when instantiated with store', () => { +// const index = { +// '': 1, +// 's1': 2, +// 'p1': 3, +// 'o0': 4, +// 's2': 5, +// 'p2': 6, +// 'o2': 7, +// }; + +// const store = new Store([ +// new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o0')), +// ], { entityIndex }); +// expect(store.size).toBe(1); +// expect(entityIndex._id).toEqual(4); + +// const substore = store.match(); +// substore.add(new Quad(new NamedNode('s2'), new NamedNode('p2'), new NamedNode('o2'))); +// expect(store.size).toBe(1); +// expect(substore.size).toBe(2); +// expect(entityIndex._id).toEqual(7); +// expect(entityIndex._ids).toEqual(index); + +// const store2 = new Store([ +// new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o5')), +// ], { entityIndex }); +// expect(store2.size).toBe(1); +// expect(entityIndex._id).toEqual(8); +// expect(entityIndex._ids).toEqual({ +// ...index, +// o5: 8, +// }); +// }); +// }); function alwaysTrue() { return true; } function alwaysFalse() { return false; } From f7871c8bbfab861529247e66e73a594b94190950 Mon Sep 17 00:00:00 2001 From: Jesse Wright <63333554+jeswr@users.noreply.github.com> Date: Mon, 5 Aug 2024 11:28:55 +1000 Subject: [PATCH 3/3] chore: re-enable entity index tests --- test/N3Store-test.js | 90 ++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/test/N3Store-test.js b/test/N3Store-test.js index ad65a6cd..40480374 100644 --- a/test/N3Store-test.js +++ b/test/N3Store-test.js @@ -2309,51 +2309,51 @@ describe('Store', () => { }); }); -// describe('EntityIndex', () => { -// let entityIndex; -// beforeEach(() => { -// entityIndex = new EntityIndex(); -// }); - -// it('should be a constructor', () => { -// expect(entityIndex).toBeInstanceOf(EntityIndex); -// }); - -// it('custom index should be used when instantiated with store', () => { -// const index = { -// '': 1, -// 's1': 2, -// 'p1': 3, -// 'o0': 4, -// 's2': 5, -// 'p2': 6, -// 'o2': 7, -// }; - -// const store = new Store([ -// new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o0')), -// ], { entityIndex }); -// expect(store.size).toBe(1); -// expect(entityIndex._id).toEqual(4); - -// const substore = store.match(); -// substore.add(new Quad(new NamedNode('s2'), new NamedNode('p2'), new NamedNode('o2'))); -// expect(store.size).toBe(1); -// expect(substore.size).toBe(2); -// expect(entityIndex._id).toEqual(7); -// expect(entityIndex._ids).toEqual(index); - -// const store2 = new Store([ -// new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o5')), -// ], { entityIndex }); -// expect(store2.size).toBe(1); -// expect(entityIndex._id).toEqual(8); -// expect(entityIndex._ids).toEqual({ -// ...index, -// o5: 8, -// }); -// }); -// }); +describe('EntityIndex', () => { + let entityIndex; + beforeEach(() => { + entityIndex = new EntityIndex(); + }); + + it('should be a constructor', () => { + expect(entityIndex).toBeInstanceOf(EntityIndex); + }); + + it('custom index should be used when instantiated with store', () => { + const index = { + '': 1, + 's1': 2, + 'p1': 3, + 'o0': 4, + 's2': 5, + 'p2': 6, + 'o2': 7, + }; + + const store = new Store([ + new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o0')), + ], { entityIndex }); + expect(store.size).toBe(1); + expect(entityIndex._id).toEqual(4); + + const substore = store.match(); + substore.add(new Quad(new NamedNode('s2'), new NamedNode('p2'), new NamedNode('o2'))); + expect(store.size).toBe(1); + expect(substore.size).toBe(2); + expect(entityIndex._id).toEqual(7); + expect(entityIndex._ids).toEqual(index); + + const store2 = new Store([ + new Quad(new NamedNode('s1'), new NamedNode('p1'), new NamedNode('o5')), + ], { entityIndex }); + expect(store2.size).toBe(1); + expect(entityIndex._id).toEqual(8); + expect(entityIndex._ids).toEqual({ + ...index, + o5: 8, + }); + }); +}); function alwaysTrue() { return true; } function alwaysFalse() { return false; }