UNPKG

@jahed/sparql-engine

Version:

SPARQL query engine for servers and web browsers.

217 lines 7.01 kB
// 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