UNPKG

@jahed/sparql-engine

Version:

SPARQL query engine for servers and web browsers.

109 lines 5.41 kB
// SPDX-License-Identifier: MIT import ExecutionContext from "../../engine/context/execution-context.js"; import ContextSymbols from "../../engine/context/symbols.js"; import { Pipeline } from "../../engine/pipeline/pipeline.js"; import BGPStageBuilder from "../../engine/stages/bgp-stage-builder.js"; import Graph from "../../rdf/graph.js"; import { cacheEvalBGP } from "../../utils/evaluation.js"; import { RDF, isVariable } from "../../utils/rdf.js"; import rewritingOp from "./rewriting-op.js"; // The default size of the bucket of Basic Graph Patterns used by the Bound Join algorithm const BOUND_JOIN_BUFFER_SIZE = 15; /** * Rewrite a triple pattern using a rewriting key, * i.e., append "_key" to each SPARQL variable in the triple pattern * @param key - Rewriting key * @param tp - Triple pattern to rewrite * @return The rewritten triple pattern */ function rewriteTriple(triple, key) { const res = Object.assign({}, triple); if (isVariable(triple.subject)) { res.subject = RDF.variable(`${triple.subject.value}_${key}`); } if (isVariable(triple.predicate)) { res.predicate = RDF.variable(`${triple.predicate.value}_${key}`); } if (isVariable(triple.object)) { res.object = RDF.variable(`${triple.object.value}_${key}`); } return res; } /** * Join the set of bindings produced by a pipeline stage with a BGP using the Bound Join algorithm. * @param source - Source of bindings * @param bgp - Basic Pattern to join with * @param graph - Graphe queried * @param Context - Query execution context * @return A pipeline stage which evaluates the bound join */ export default function boundJoin(source, bgp, graph, builder, context) { let bufferSize = BOUND_JOIN_BUFFER_SIZE; if (context.hasProperty(ContextSymbols.BOUND_JOIN_BUFFER_SIZE)) { bufferSize = context.getProperty(ContextSymbols.BOUND_JOIN_BUFFER_SIZE); } return Pipeline.getInstance().mergeMapAsync(Pipeline.getInstance().bufferCount(source, bufferSize), async (bucket) => { // simple case: first join in the pipeline if (bucket.length === 1 && bucket[0].isEmpty) { if (context.cachingEnabled()) { return cacheEvalBGP(bgp, graph, context.cache, builder, context); } return graph.evalBGP(bgp, context); } else { // The bucket of rewritten basic graph patterns const bgpBucket = []; // The bindings of the bucket that cannot be evaluated with a bound join for this BGP const regularBindings = []; // A rewriting table dedicated to this instance of the bound join const rewritingTable = new Map(); // The rewriting key (a simple counter) for this instance of the bound join let key = 0; // Build the bucket of Basic Graph patterns bucket.map((binding) => { const boundedBGP = []; let nbBounded = 0; // build the bounded BGP using the current set of bindings bgp.forEach((triple) => { const boundedTriple = rewriteTriple(binding.bound(triple), key); boundedBGP.push(boundedTriple); // track the number of fully bounded triples, i.e., triple patterns without any SPARQL variables if (!isVariable(boundedTriple.subject) && !isVariable(boundedTriple.predicate) && !isVariable(boundedTriple.object)) { nbBounded++; } }); // if the whole BGP is bounded, then the current set of bindings cannot be processed // using a bound join and we must process it using a regular Index Join. // Otherwise, the partially bounded BGP is suitable for a bound join if (nbBounded === bgp.length) { regularBindings.push(binding); } else { // save the rewriting into the table rewritingTable.set(key, binding); bgpBucket.push(boundedBGP); } key++; }); let boundJoinStage = Pipeline.getInstance().empty(); let regularJoinStage = Pipeline.getInstance().empty(); // first, evaluates the bucket of partially bounded BGPs using a bound join if (bgpBucket.length > 0) { boundJoinStage = await rewritingOp(graph, bgpBucket, rewritingTable, builder, context); } // then, evaluates the remaining bindings using a bound join if (regularBindings.length > 0) { // otherwiwe, we create a new context to force the execution using Index Joins const newContext = context.clone(); newContext.setProperty(ContextSymbols.FORCE_INDEX_JOIN, true); // Invoke the BGPStageBuilder to evaluate the bucket regularJoinStage = builder._buildIterator(Pipeline.getInstance().of(...regularBindings), graph, bgp, newContext); } // merge the two pipeline stages to produce the join results return Pipeline.getInstance().merge(boundJoinStage, regularJoinStage); } }); } //# sourceMappingURL=bound-join.js.map