UNPKG

@jahed/sparql-engine

Version:

SPARQL query engine for servers and web browsers.

107 lines (101 loc) 3.36 kB
// SPDX-License-Identifier: MIT import { isBoolean } from "lodash-es"; import { termToString } from "rdf-string"; import type { PipelineStage, StreamPipelineInput, } from "../engine/pipeline/pipeline-engine.ts"; import { Pipeline } from "../engine/pipeline/pipeline.ts"; import type { QueryOutput } from "../engine/plan-builder.ts"; import { BindingBase, Bindings } from "../rdf/bindings.ts"; /** * Write the headers and generate an ordering * @private * @param bindings - Input bindings * @param separator - Separator to use * @param input - Output where to write results * @return The order of variables in the header */ function writeHead( bindings: Bindings, separator: string, input: StreamPipelineInput<string> ): string[] { const variables = Array.from(bindings.variables()).map((v) => v.startsWith("?") ? v.substring(1) : v ); input.next(variables.join(separator)); input.next("\n"); return variables; } /** * Write a set of bindings as CSV/TSV * @private * @param bindings - Input bindings * @param separator - Separator to use * @param input - Output where to write results */ function writeBindings( bindings: Bindings, separator: string, order: string[], input: StreamPipelineInput<string> ): void { let output: string[] = []; order.forEach((variable) => { if (bindings.has(variable)) { let value = bindings.get(variable)!; output.push(termToString(value)); } }); input.next(output.join(separator)); } /** * Create a function that formats query solutions in CSV/TSV using a separator * @param separator - Separator to use * @return A function that formats query results in a pipeline fashion */ function genericFormatter(separator: string) { return (source: PipelineStage<QueryOutput>): PipelineStage<string> => { return Pipeline.getInstance().fromAsync(async (input) => { try { let warmup = true; let isAsk = false; let ordering: string[] = []; for await (const b of source) { if (warmup && b instanceof BindingBase) { ordering = writeHead(b, separator, input); } else if (warmup && isBoolean(b)) { isAsk = true; input.next("boolean\n"); } warmup = false; // handle results (boolean for ASK queries, bindings for SELECT queries) if (isBoolean(b)) { input.next(b ? "true\n" : "false\n"); } else if (b instanceof BindingBase) { writeBindings(b, separator, ordering, input); input.next("\n"); } } input.complete(); } catch (error) { console.error(error); } }); }; } /** * Formats query solutions (bindings or booleans) from a PipelineStage in W3C SPARQL CSV format * @see https://www.w3.org/TR/2013/REC-sparql11-results-csv-tsv-20130321/ * @param source - Input pipeline * @return A pipeline that yields results in W3C SPARQL CSV format */ export const csvFormatter = genericFormatter(","); /** * Formats query solutions (bindings or booleans) from a PipelineStage in W3C SPARQL TSV format * @see https://www.w3.org/TR/2013/REC-sparql11-results-csv-tsv-20130321/ * @param source - Input pipeline * @return A pipeline that yields results in W3C SPARQL TSV format */ export const tsvFormatter = genericFormatter("\t");