quadstore
Version:
Quadstore is a LevelDB-backed RDF graph database / triplestore for JavaScript runtimes (browsers, Node.js, Deno, Bun, ...) that implements the RDF/JS interfaces and supports SPARQL queries and querying across named graphs.
97 lines • 4.63 kB
JavaScript
import { ResultType } from '../types/index.js';
import { arrStartsWith } from '../utils/stuff.js';
import { emptyObject, separator } from '../utils/constants.js';
import { quadReader, twoStepsQuadWriter, writePattern } from '../serialization/index.js';
import { SortingIterator } from './sortingiterator.js';
import { LevelIterator } from './leveliterator.js';
const SORTING_KEY = Symbol();
const compareSortableQuadsReverse = (left, right) => {
return left[SORTING_KEY] > right[SORTING_KEY] ? -1 : 1;
};
const compareSortableQuads = (left, right) => {
return left[SORTING_KEY] > right[SORTING_KEY] ? 1 : -1;
};
const emitSortableQuad = (item) => item;
const getLevelQueryForIndex = (pattern, index, prefixes, opts) => {
const indexQuery = writePattern(pattern, index, prefixes);
if (indexQuery === null) {
return null;
}
const levelOpts = {
[indexQuery.gte ? 'gte' : 'gt']: indexQuery.gt,
[indexQuery.lte ? 'lte' : 'lt']: indexQuery.lt,
keys: true,
values: false,
keyEncoding: 'utf8',
};
if (typeof opts.limit === 'number') {
levelOpts.limit = opts.limit;
}
if (typeof opts.reverse === 'boolean') {
levelOpts.reverse = opts.reverse;
}
return { level: levelOpts, order: indexQuery.order, index: indexQuery.index };
};
const getLevelQuery = (pattern, indexes, prefixes, opts) => {
for (let i = 0, index; i < indexes.length; i += 1) {
index = indexes[i];
const levelQuery = getLevelQueryForIndex(pattern, index, prefixes, opts);
if (levelQuery !== null && (!opts.order || arrStartsWith(levelQuery.order, opts.order))) {
return levelQuery;
}
}
return null;
};
export const getStream = async (store, pattern, opts) => {
const { dataFactory, prefixes, indexes } = store;
const levelQueryFull = getLevelQuery(pattern, indexes, prefixes, opts);
if (levelQueryFull !== null) {
const { index, level, order } = levelQueryFull;
let iterator = new LevelIterator(store.db.iterator(level), ([key]) => quadReader.read(key, index.prefix.length, index.terms, dataFactory, prefixes), opts.maxBufferSize);
return { type: ResultType.QUADS, order, iterator, index: index.terms, resorted: false };
}
const levelQueryNoOpts = getLevelQuery(pattern, indexes, prefixes, emptyObject);
if (levelQueryNoOpts !== null) {
const { index, level, order } = levelQueryNoOpts;
let iterator = new LevelIterator(store.db.iterator(level), ([key]) => quadReader.read(key, index.prefix.length, index.terms, dataFactory, prefixes), opts.maxBufferSize);
if (typeof opts.order !== 'undefined' && !arrStartsWith(opts.order, order)) {
const digest = (item) => {
item[SORTING_KEY] = twoStepsQuadWriter.ingest(item, prefixes).write('', opts.order) + separator;
return item;
};
const compare = opts.reverse === true ? compareSortableQuadsReverse : compareSortableQuads;
iterator = new SortingIterator(iterator, compare, digest, emitSortableQuad);
if (typeof opts.limit !== 'undefined') {
const onEndOrError = function () {
this.removeListener('end', onEndOrError);
this.removeListener('error', onEndOrError);
this.destroy();
};
iterator = iterator.take(opts.limit)
.on('end', onEndOrError)
.on('error', onEndOrError);
}
}
return { type: ResultType.QUADS, order: opts.order || order, iterator, index: index.terms, resorted: true };
}
throw new Error(`No index compatible with pattern ${JSON.stringify(pattern)} and options ${JSON.stringify(opts)}`);
};
export const getApproximateSize = async (store, pattern, opts) => {
if (!store.db.approximateSize) {
return { type: ResultType.APPROXIMATE_SIZE, approximateSize: Infinity };
}
const { indexes, prefixes } = store;
const levelQuery = getLevelQuery(pattern, indexes, prefixes, opts);
if (levelQuery === null) {
throw new Error(`No index compatible with pattern ${JSON.stringify(pattern)} and options ${JSON.stringify(opts)}`);
}
const { level } = levelQuery;
const start = level.gte || level.gt;
const end = level.lte || level.lt;
const approximateSize = await store.db.approximateSize(start, end);
return {
type: ResultType.APPROXIMATE_SIZE,
approximateSize: Math.max(1, approximateSize),
};
};
//# sourceMappingURL=index.js.map