@comunica/actor-query-operation-from-quad
Version:
A from query-operation actor
211 lines • 10.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ActorQueryOperationFromQuad = void 0;
const bus_query_operation_1 = require("@comunica/bus-query-operation");
const context_entries_1 = require("@comunica/context-entries");
const core_1 = require("@comunica/core");
const sparqlalgebrajs_1 = require("sparqlalgebrajs");
/**
* A comunica From Query Operation Actor.
*/
class ActorQueryOperationFromQuad extends bus_query_operation_1.ActorQueryOperationTypedMediated {
constructor(args) {
super(args, 'from');
}
/**
* Create a deep copy of the given operation.
* @param {Operation} operation An operation.
* @param {(subOperation: Operation) => Operation} recursiveCb A callback for recursive operation calls.
* @return {Operation} The copied operation.
*/
static copyOperation(operation, recursiveCb) {
const copiedOperation = {};
for (const key of Object.keys(operation)) {
if (Array.isArray(operation[key]) && key !== 'template') {
// We exclude the 'template' entry, as we don't want to modify the template value of construct operations
if (key === 'variables') {
copiedOperation[key] = operation[key];
}
else {
copiedOperation[key] = operation[key].map(recursiveCb);
}
}
else if (ActorQueryOperationFromQuad.ALGEBRA_TYPES.includes(operation[key].type)) {
copiedOperation[key] = recursiveCb(operation[key]);
}
else {
copiedOperation[key] = operation[key];
}
}
return copiedOperation;
}
/**
* Recursively transform the given operation to use the given graphs as default graph
* This will (possibly) create a new operation and not modify the given operation.
* @package
* @param algebraFactory The algebra factory.
* @param {Operation} operation An operation.
* @param {RDF.Term[]} defaultGraphs Graph terms.
* @return {Operation} A new operation.
*/
static applyOperationDefaultGraph(algebraFactory, operation, defaultGraphs) {
// If the operation is a BGP or Path, change the graph.
if ((operation.type === 'bgp' && operation.patterns.length > 0) ||
operation.type === 'path' ||
operation.type === 'pattern') {
if (operation.type === 'bgp') {
return ActorQueryOperationFromQuad
.joinOperations(algebraFactory, operation.patterns.map((pattern) => {
if (pattern.graph.termType !== 'DefaultGraph') {
return algebraFactory.createBgp([pattern]);
}
const bgps = defaultGraphs.map((graph) => algebraFactory.createBgp([Object.assign(algebraFactory
.createPattern(pattern.subject, pattern.predicate, pattern.object, graph), { metadata: pattern.metadata })]));
return ActorQueryOperationFromQuad.unionOperations(algebraFactory, bgps);
}));
}
if (operation.graph.termType !== 'DefaultGraph') {
return operation;
}
const paths = defaultGraphs.map((graph) => {
if (operation.type === 'path') {
return algebraFactory
.createPath(operation.subject, operation.predicate, operation.object, graph);
}
return Object.assign(algebraFactory
.createPattern(operation.subject, operation.predicate, operation.object, graph), { metadata: operation.metadata });
});
return ActorQueryOperationFromQuad.unionOperations(algebraFactory, paths);
}
return ActorQueryOperationFromQuad.copyOperation(operation, (subOperation) => this.applyOperationDefaultGraph(algebraFactory, subOperation, defaultGraphs));
}
/**
* Recursively transform the given operation to use the given graphs as named graph
* This will (possibly) create a new operation and not modify the given operation.
* @package
* @param algebraFactory The algebra factory.
* @param {Operation} operation An operation.
* @param {RDF.Term[]} namedGraphs Graph terms.
* @param {RDF.Term[]} defaultGraphs Default graph terms.
* @return {Operation} A new operation.
*/
static applyOperationNamedGraph(algebraFactory, operation, namedGraphs, defaultGraphs) {
// If the operation is a BGP or Path, change the graph.
if ((operation.type === 'bgp' && operation.patterns.length > 0) ||
operation.type === 'path' ||
operation.type === 'pattern') {
const patternGraph = operation.type === 'bgp' ? operation.patterns[0].graph : operation.graph;
if (patternGraph.termType === 'DefaultGraph') {
// SPARQL spec (8.2) describes that when FROM NAMED's are used without a FROM, the default graph must be empty.
// The FROMs are transformed before this step to a named node, so this will not apply to this case anymore.
return { type: sparqlalgebrajs_1.Algebra.types.BGP, patterns: [] };
}
if (patternGraph.termType === 'Variable') {
if (namedGraphs.length === 1) {
const graph = namedGraphs[0];
// If the pattern graph is a variable, replace the graph and bind the variable using VALUES
const bindings = {};
bindings[`?${patternGraph.value}`] = graph;
const values = algebraFactory
.createValues([patternGraph], [bindings]);
let pattern;
if (operation.type === 'bgp') {
pattern = algebraFactory
.createBgp(operation.patterns.map((pat) => algebraFactory
.createPattern(pat.subject, pat.predicate, pat.object, graph)));
}
else if (operation.type === 'path') {
pattern = algebraFactory
.createPath(operation.subject, operation.predicate, operation.object, graph);
}
else {
pattern = algebraFactory
.createPattern(operation.subject, operation.predicate, operation.object, graph);
}
return algebraFactory.createJoin([values, pattern]);
}
// If the pattern graph is a variable, take the union of the pattern applied to each available named graph
return ActorQueryOperationFromQuad.unionOperations(algebraFactory, namedGraphs.map((graph) => ActorQueryOperationFromQuad.applyOperationNamedGraph(algebraFactory, operation, [graph], defaultGraphs)));
}
// The pattern's graph is defined (including the default graphs)
const isNamedGraphAvailable = [...namedGraphs, ...defaultGraphs].some((namedGraph) => namedGraph.equals(patternGraph));
if (isNamedGraphAvailable) {
// Return the pattern as-is if the pattern's graph was selected in a FROM NAMED
return operation;
}
// No-op if the pattern's graph was not selected in a FROM NAMED.
return { type: sparqlalgebrajs_1.Algebra.types.BGP, patterns: [] };
}
return ActorQueryOperationFromQuad.copyOperation(operation, (subOperation) => this
.applyOperationNamedGraph(algebraFactory, subOperation, namedGraphs, defaultGraphs));
}
/**
* Transform the given array of operations into a join operation.
* @package
* @param algebraFactory The algebra factory.
* @param {Operation[]} operations An array of operations, must contain at least one operation.
* @return {Join} A join operation.
*/
static joinOperations(algebraFactory, operations) {
if (operations.length === 1) {
return operations[0];
}
if (operations.length > 1) {
return algebraFactory.createJoin(operations);
}
throw new Error('A join can only be applied on at least one operation');
}
/**
* Transform the given array of operations into a union operation.
* @package
* @param algebraFactory The algebra factory.
* @param {Operation[]} operations An array of operations, must contain at least one operation.
* @return {Union} A union operation.
*/
static unionOperations(algebraFactory, operations) {
if (operations.length === 1) {
return operations[0];
}
if (operations.length > 1) {
return algebraFactory.createUnion(operations);
}
throw new Error('A union can only be applied on at least one operation');
}
/**
* Transform an operation based on the default and named graphs in the pattern.
*
* FROM sets the default graph.
* If multiple are available, take the union of the operation for all of them at quad-pattern level.
*
* FROM NAMED indicates which named graphs are available.
* This will rewrite the query so that only triples from the given named graphs can be selected.
*
* @package
* @param algebraFactory The algebra factory.
* @param {From} pattern A from operation.
* @return {Operation} The transformed operation.
*/
static createOperation(algebraFactory, pattern) {
let operation = pattern.input;
if (pattern.default.length > 0) {
operation = ActorQueryOperationFromQuad.applyOperationDefaultGraph(algebraFactory, operation, pattern.default);
}
if (pattern.named.length > 0 || pattern.default.length > 0) {
operation = ActorQueryOperationFromQuad
.applyOperationNamedGraph(algebraFactory, operation, pattern.named, pattern.default);
}
return operation;
}
async testOperation(_operation, _context) {
return (0, core_1.passTestVoid)();
}
async runOperation(operationOriginal, context) {
const dataFactory = context.getSafe(context_entries_1.KeysInitQuery.dataFactory);
const algebraFactory = new sparqlalgebrajs_1.Factory(dataFactory);
const operation = ActorQueryOperationFromQuad.createOperation(algebraFactory, operationOriginal);
return this.mediatorQueryOperation.mediate({ operation, context });
}
}
exports.ActorQueryOperationFromQuad = ActorQueryOperationFromQuad;
ActorQueryOperationFromQuad.ALGEBRA_TYPES = Object.keys(sparqlalgebrajs_1.Algebra.types).map(key => sparqlalgebrajs_1.Algebra.types[key]);
//# sourceMappingURL=ActorQueryOperationFromQuad.js.map