@jahed/sparql-engine
Version:
SPARQL query engine for servers and web browsers.
217 lines • 7.01 kB
JavaScript
// SPDX-License-Identifier: MIT
import { chunk, flatMap, flatten, slice } from "lodash-es";
import { createObserver, createSubscription, PipelineEngine, } from "./pipeline-engine.js";
/**
* A PipelineStage which materializes all intermediate results in main memory.
*/
export class VectorStage {
// We need to use Promise to store the stage content,
// as some computations can require asynchronous computations.
// For example, the RDF graph can send HTTP requests to evaluate triple patterns.
_content;
constructor(content) {
this._content = content;
}
getContent() {
return this._content;
}
subscribe(observerOrNext) {
const observer = createObserver(observerOrNext);
try {
this._content
.then((c) => {
if (observer.next)
c.forEach(observer.next);
if (observer.complete)
observer.complete();
})
.catch(observer.error);
}
catch (e) {
if (observer.error)
observer.error(e);
}
return createSubscription();
}
forEach(cb) {
this._content
.then((c) => {
c.forEach(cb);
})
.catch((err) => {
throw err;
});
}
pipe(fn) {
return fn(this);
}
async *[Symbol.asyncIterator]() {
for (const v of await this._content) {
yield v;
}
}
}
export class VectorStreamInput {
_resolve;
_reject;
_content;
constructor(resolve, reject) {
this._resolve = resolve;
this._reject = reject;
this._content = [];
}
next(value) {
this._content.push(value);
}
error(err) {
this._reject(err);
}
complete() {
this._resolve(this._content);
}
}
/**
* A pipeline implemented using {@link VectorStage}, *i.e.*, all intermediate results are materialized in main memory. This approach is often called **vectorized approach**.
* This pipeline is more efficient CPU-wise than {@link RxjsPipeline}, but it also consumes much more memory, as it materializes evey stage of the pipeline before moving to the next.
* It should only be used when SPARQL queries generate few intermediate results.
* @see P. A. Boncz, S. Manegold, and M. L. Kersten. "Database architecture evolution: Mammals flourished long before dinosaurs became extinct". PVLDB, (2009)
*/
export default class VectorPipeline extends PipelineEngine {
empty() {
return new VectorStage(Promise.resolve([]));
}
of(...values) {
return new VectorStage(Promise.resolve(values));
}
async getContent(x) {
if ("getContent" in x) {
return x.getContent();
}
else if (Array.isArray(x)) {
return Promise.resolve(x);
}
else if (x instanceof Promise) {
const v = await x;
return [v];
}
else if (Symbol.iterator in x) {
return Promise.resolve(Array.from(x));
}
throw new Error("Invalid argument: " + x);
}
from(x) {
return new VectorStage(this.getContent(x));
}
fromAsync(cb) {
return new VectorStage(new Promise((resolve, reject) => {
cb(new VectorStreamInput(resolve, reject));
}));
}
clone(stage) {
return new VectorStage(stage.getContent().then((c) => c.slice(0)));
}
catch(input, handler) {
return new VectorStage(new Promise((resolve, reject) => {
input
.getContent()
.then((c) => resolve(c.slice(0)))
.catch((err) => {
if (handler === undefined) {
reject(err);
}
else {
handler(err)
.getContent()
.then((c) => resolve(c.slice(0)))
.catch((err) => {
throw err;
});
}
});
}));
}
merge(...inputs) {
return new VectorStage(Promise.all(inputs.map((input) => this.getContent(input))).then((contents) => flatten(contents)));
}
map(input, mapper) {
return new VectorStage(input.getContent().then((c) => c.map(mapper)));
}
flatMap(input, mapper) {
return new VectorStage(input.getContent().then((c) => flatMap(c, mapper)));
}
mergeMap(input, mapper) {
return new VectorStage(input.getContent().then(async (content) => {
let result = [];
for (const item of content) {
const mapped = mapper(item);
result = result.concat(await mapped.getContent());
}
return result;
}));
}
mergeMapAsync(input, mapper) {
return new VectorStage(input.getContent().then(async (content) => {
let result = [];
for (const item of content) {
const mapped = await mapper(item);
result = result.concat(await mapped.getContent());
}
return result;
}));
}
filter(input, predicate) {
return new VectorStage(input.getContent().then((c) => c.filter(predicate)));
}
filterAsync(input, predicate) {
return new VectorStage(input.getContent().then(async (c) => {
const results = [];
for (const item of c) {
if (await predicate(item)) {
results.push(item);
}
}
return results;
}));
}
finalize(input, callback) {
return new VectorStage(input.getContent().then((c) => {
callback();
return c;
}));
}
reduce(input, reducer, initial) {
return new VectorStage(input.getContent().then((c) => [c.reduce(reducer, initial)]));
}
limit(input, stopAfter) {
return new VectorStage(input.getContent().then((c) => slice(c, 0, stopAfter)));
}
skip(input, toSkip) {
return new VectorStage(input.getContent().then((c) => slice(c, toSkip)));
}
defaultValues(input, ...values) {
return new VectorStage(input.getContent().then((content) => {
if (content.length > 0) {
return content.slice(0);
}
return values;
}));
}
bufferCount(input, count) {
return new VectorStage(input.getContent().then((c) => chunk(c, count)));
}
forEach(input, cb) {
input.forEach(cb);
}
first(input) {
return new VectorStage(input.getContent().then((content) => {
if (content.length < 1) {
return [];
}
return [content[0]];
}));
}
collect(input) {
return new VectorStage(input.getContent().then((c) => [c]));
}
}
//# sourceMappingURL=vector-pipeline.js.map