Skip to content

Commit

Permalink
perf: perform matching over indices (#443)
Browse files Browse the repository at this point in the history
* perf: perform matching over indices

* chore: update comments

* chore: add mutation tests
  • Loading branch information
jeswr authored Sep 17, 2024
1 parent 7dcb79c commit b0361a0
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 2 deletions.
48 changes: 46 additions & 2 deletions src/N3Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,31 @@ export default class N3Store {
}
}

/**
* Returns a subset of the `index` with that part of the index
* matching the `ids` array. `ids` contains 3 elements that are
* either numerical ids; or `null`.
*
* `false` is returned when there are no matching indices; this should
* *not* be set as the value for an index.
*/
function indexMatch(index, ids, depth = 0) {
const ind = ids[depth];
if (ind && !(ind in index))
return false;

let target = false;
for (const key in (ind ? { [ind]: index[ind] } : index)) {
const result = depth === 2 ? null : indexMatch(index[key], ids, depth + 1);

if (result !== false) {
target = target || Object.create(null);
target[key] = result;
}
}
return target;
}

/**
* A class that implements both DatasetCore and Readable.
*/
Expand All @@ -1039,8 +1064,27 @@ class DatasetCoreAndReadableStream extends Readable {
if (!this._filtered) {
const { n3Store, graph, object, predicate, subject } = this;
const newStore = this._filtered = new N3Store({ factory: n3Store._factory, entityIndex: this.options.entityIndex });
for (const quad of n3Store.readQuads(subject, predicate, object, graph))
newStore.addQuad(quad);
const graphs = n3Store._getGraphs(graph);

let subjectId, predicateId, objectId;

// Translate IRIs to internal index keys.
if (subject && !(subjectId = newStore._termToNumericId(subject)) ||
predicate && !(predicateId = newStore._termToNumericId(predicate)) ||
object && !(objectId = newStore._termToNumericId(object)))
return newStore;

for (const graph in graphs) {
const subjects = indexMatch(graphs[graph].subjects, [subjectId, predicateId, objectId]);
if (subjects) {
newStore._graphs[graph] = {
subjects,
predicates: indexMatch(graphs[graph].predicates, [predicateId, objectId, subjectId]),
objects: indexMatch(graphs[graph].objects, [objectId, subjectId, predicateId]),
};
}
}
newStore._size = null;
}
return this._filtered;
}
Expand Down
11 changes: 11 additions & 0 deletions test/N3Store-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Store,
termFromId, termToId,
EntityIndex,
DataFactory,
} from '../src';
import {
NamedNode,
Expand All @@ -13,6 +14,8 @@ import namespaces from '../src/IRIs';
import { Readable } from 'readable-stream';
import { arrayifyStream } from 'arrayify-stream';

const { namedNode } = DataFactory;

describe('Store', () => {
describe('The Store export', () => {
it('should be a function', () => {
Expand Down Expand Up @@ -695,6 +698,14 @@ describe('Store', () => {

expect(nextDataset.has(new Quad('s2', 'p1', 'o1'))).toBe(true);
expect(nextDataset.has(new Quad('s2', 'p2', 'o2'))).toBe(true);
expect(nextDataset.match(namedNode('s2'), null, null).has(new Quad('s2', 'p2', 'o2'))).toBe(true);
expect(nextDataset.match(null, namedNode('p2'), null).has(new Quad('s2', 'p2', 'o2'))).toBe(true);
expect(nextDataset.match(null, null, namedNode('o2')).has(new Quad('s2', 'p2', 'o2'))).toBe(true);
expect(nextDataset.size).toBe(2);
nextDataset.add(new Quad('s2', 'p1', 'onew'));
expect(nextDataset.size).toBe(3);
nextDataset.add(new Quad('s2', 'p1', 'onew'));
expect(nextDataset.size).toBe(3);

nextDataset.delete(new Quad('s2', 'p1', 'o1'));
nextDataset.delete(new Quad('s2', 'p2', 'o2'));
Expand Down

0 comments on commit b0361a0

Please sign in to comment.