UNPKG

@jahed/sparql-engine

Version:

SPARQL query engine for servers and web browsers.

179 lines 7.15 kB
// SPDX-License-Identifier: MIT import { identity, isUndefined, uniqBy } from "lodash-es"; export function createSubscription() { return { [Symbol.dispose]() { }, }; } export function createObserver(observerOrNext) { return observerOrNext ? typeof observerOrNext === "object" ? observerOrNext : { next: observerOrNext } : {}; } /** * Abstract representation used to apply transformations on a pipeline of iterators. * Concrete subclasses are used by the framework to build the query execution pipeline. * @abstract */ export class PipelineEngine { /** * Maps each source value to an array of values which is merged in the output PipelineStage. * @param input - Input PipelineStage * @param mapper - Transformation function * @return Output PipelineStage */ flatMap(input, mapper) { return this.mergeMap(input, (value) => this.of(...mapper(value))); } /** * Flatten the output of a pipeline stage that emits array of values into single values. * @param input - Input PipelineStage * @return Output PipelineStage */ flatten(input) { return this.flatMap(input, (v) => v); } /** * Returns a PipelineStage that emits all items emitted by the source PipelineStage that are distinct by comparison from previous items. * @param input - Input PipelineStage * @param selector - Optional function to select which value you want to check as distinct. * @return A PipelineStage that emits items from the source PipelineStage with distinct values. */ distinct(input, selector) { if (isUndefined(selector)) { selector = identity; } return this.flatMap(this.collect(input), (values) => uniqBy(values, selector)); } /** * Emits only the first value (or the first value that meets some condition) emitted by the source PipelineStage. * @param input - Input PipelineStage * @return A PipelineStage of the first item that matches the condition. */ first(input) { return this.limit(input, 1); } /** * Returns a PipelineStage that emits the items you specify as arguments after it finishes emitting items emitted by the source PipelineStage. * @param input - Input PipelineStage * @param values - Values to append * @return A PipelineStage that emits the items emitted by the source PipelineStage and then emits the additional values. */ endWith(input, values) { return this.merge(input, this.from(values)); } /** * Perform a side effect for every emission on the source PipelineStage, but return a PipelineStage that is identical to the source. * @param input - Input PipelineStage * @param cb - Callback invoked on each item * @return A PipelineStage identical to the source, but runs the specified PipelineStage or callback(s) for each item. */ tap(input, cb) { return this.map(input, (value) => { cb(value); return value; }); } /** * Find the smallest value produced by a pipeline of iterators. * It takes a ranking function as input, which is invoked with (x, y) * and must returns True if x < y and False otherwise. * Warning: this function needs to materialize all values of the pipeline. * @param input - Input PipelineStage * @param comparator - (optional) Ranking function * @return A pipeline stage that emits the lowest value found */ min(input, ranking) { if (isUndefined(ranking)) { ranking = (x, y) => x < y; } return this.map(this.collect(input), (values) => { let minValue = values[0]; for (let i = 1; i < values.length - 1; i++) { if (ranking(values[i], minValue)) { minValue = values[i]; } } return minValue; }); } /** * Find the smallest value produced by a pipeline of iterators. * It takes a ranking function as input, which is invoked with (x, y) * and must returns True if x > y and False otherwise. * Warning: this function needs to materialize all values of the pipeline. * @param input - Input PipelineStage * @param comparator - (optional) Ranking function * @return A pipeline stage that emits the highest value found */ max(input, ranking) { if (isUndefined(ranking)) { ranking = (x, y) => x > y; } return this.map(this.collect(input), (values) => { let maxValue = values[0]; for (let i = 1; i < values.length - 1; i++) { if (ranking(values[i], maxValue)) { maxValue = values[i]; } } return maxValue; }); } /** * Groups the items produced by a pipeline according to a specified criterion, * and emits the resulting groups * @param input - Input PipelineStage * @param keySelector - A function that extracts the grouping key for each item * @param elementSelector - (optional) A function that transforms items before inserting them in a group */ groupBy(input, keySelector, elementSelector) { if (isUndefined(elementSelector)) { elementSelector = identity; } const groups = new Map(); let stage = this.map(input, (value) => { return { key: keySelector(value), value: elementSelector(value), }; }); return this.mergeMap(this.collect(stage), (subgroups) => { // build groups subgroups.forEach((g) => { if (!groups.has(g.key)) { groups.set(g.key, [g.value]); } else { groups.set(g.key, groups.get(g.key).concat([g.value])); } }); // inject groups into the pipeline return this.fromAsync((input) => { groups.forEach((value, key) => input.next([key, value])); }); }); } /** * Peek values from the input pipeline stage, and use them to decide * between two candidate pipeline stages to continue the pipeline. * @param input - Input pipeline stage * @param count - How many items to peek from the input? * @param predicate - Predicate function invoked with the values * @param ifCase - Callback invoked if the predicate function evaluates to True * @param elseCase - Callback invoked if the predicate function evaluates to False * @return A pipeline stage */ peekIf(input, count, predicate, ifCase, elseCase) { const peekable = this.limit(this.clone(input), count); return this.mergeMapAsync(this.collect(peekable), (values) => { if (predicate(values)) { return ifCase(values); } return elseCase(values); }); } } //# sourceMappingURL=pipeline-engine.js.map