@rdfc/sparql-ingest-processor-ts
Version:
SPARQL Update function to be within RDF-Connect pipelines
149 lines (148 loc) • 6.1 kB
JavaScript
import { RDF, SHACL } from "@treecg/types";
import { Writer as N3Writer, Parser } from "n3";
import { RdfStore } from "rdf-stores";
import { DataFactory } from "rdf-data-factory";
import { getObjects, getSubjects } from "./Utils.js";
const df = new DataFactory();
export const CREATE = (store, namedGraph, multipleNamedGraphs) => {
const content = new N3Writer().quadsToString(store.getQuads(null, null, null, null));
return `
INSERT DATA {
${namedGraph ? `GRAPH <${namedGraph}> {${content}}` : `${content}`}
}
`;
};
export const UPDATE = (store, namedGraph, multipleNamedGraphs) => {
const formattedQuery = formatDeleteQuery(store);
const content = new N3Writer().quadsToString(store.getQuads(null, null, null, null));
return `
${namedGraph ? `WITH <${namedGraph}>` : ""}
DELETE {
${formattedQuery[0]}
}
INSERT {
${content}
}
WHERE {
${formattedQuery[0]}
}
`;
};
export const DELETE = (store, memberIRIs, memberShapes, namedGraph, multipleNamedGraphs) => {
const deleteBuilder = [];
const whereBuilder = [];
let indexStart = 0;
for (const memberIRI of memberIRIs) {
const formatted = formatDeleteQuery(store, memberIRI, memberShapes, indexStart);
deleteBuilder.push(formatted.length > 1 ? formatted[1] : formatted[0]);
whereBuilder.push(formatted[0]);
indexStart++;
}
return `
${namedGraph ? `WITH <${namedGraph}>` : ""}
DELETE {
${deleteBuilder.join("\n")}
} WHERE {
${whereBuilder.join("\n")}
}
`;
};
function formatDeleteQuery(memberStore, memberIRI, memberShapes, indexStart = 0) {
const subjectSet = new Set();
const queryBuilder = [];
const formattedQueries = [];
let i = indexStart;
if (!memberShapes || memberShapes.length === 0) {
for (const quad of memberStore.getQuads(null, null, null, null)) {
if (!subjectSet.has(quad.subject.value)) {
subjectSet.add(quad.subject.value);
if (quad.subject.termType === "NamedNode") {
queryBuilder.push(`<${quad.subject.value}> ?p_${i} ?o_${i}.`);
}
else if (quad.subject.termType === "BlankNode") {
const bnQ = memberStore.getQuads(null, null, quad.subject, null)[0];
queryBuilder.push(`<${bnQ.subject.value}> <${bnQ.predicate.value}> ?bn_${i}.`);
queryBuilder.push(`?bn_${i} ?p_${i} ?o${i}.`);
}
i++;
}
}
formattedQueries.push(queryBuilder.join("\n"));
}
else {
const shapeIndex = new Map();
memberShapes.forEach(msh => {
const shapeStore = RdfStore.createDefault();
new Parser().parse(msh).forEach(quad => shapeStore.addQuad(quad));
shapeIndex.set(extractMainTargetClass(shapeStore).value, shapeStore);
});
queryBuilder.push(`<${memberIRI}> ?p_${i} ?o_${i}.`);
const memberType = getObjects(memberStore, df.namedNode(memberIRI), RDF.terms.type, null)[0];
if (memberType) {
i++;
const mshStore = shapeIndex.get(memberType.value);
const propShapes = getObjects(mshStore, null, SHACL.terms.property, null);
for (const propSh of propShapes) {
const pred = getObjects(mshStore, propSh, SHACL.terms.path, null)[0];
queryBuilder.push(`<${memberIRI}> <${pred.value}> ?subEnt_${i}.`);
queryBuilder.push(`?subEnt_${i} ?p_${i} ?o_${i}.`);
i++;
}
formattedQueries.push(queryBuilder.join("\n"));
}
else {
const deleteQueryBuilder = [];
deleteQueryBuilder.push(`<${memberIRI}> ?p_${i} ?o_${i}.`);
i++;
shapeIndex.forEach(mshStore => {
const propShapes = getObjects(mshStore, null, SHACL.terms.property, null);
queryBuilder.push(" OPTIONAL { ");
for (const propSh of propShapes) {
const pred = getObjects(mshStore, propSh, SHACL.terms.path, null)[0];
queryBuilder.push(`<${memberIRI}> <${pred.value}> ?subEnt_${i}.`);
deleteQueryBuilder.push(`<${memberIRI}> <${pred.value}> ?subEnt_${i}.`);
queryBuilder.push(`?subEnt_${i} ?p_${i} ?o_${i}.`);
deleteQueryBuilder.push(`?subEnt_${i} ?p_${i} ?o_${i}.`);
i++;
}
queryBuilder.push(" }");
});
formattedQueries.push(queryBuilder.join("\n"));
formattedQueries.push(deleteQueryBuilder.join("\n"));
}
}
return formattedQueries;
}
function extractMainTargetClass(store) {
const nodeShapes = getSubjects(store, RDF.terms.type, SHACL.terms.NodeShape, null);
let mainNodeShape = null;
if (nodeShapes && nodeShapes.length > 0) {
for (const ns of nodeShapes) {
const isNotReferenced = getSubjects(store, null, ns, null).length === 0;
if (isNotReferenced) {
if (!mainNodeShape) {
mainNodeShape = ns;
}
else {
throw new Error("There are multiple main node shapes in a given shape."
+ " Unrelated shapes must be given as separate member shapes");
}
}
}
if (mainNodeShape) {
const tcq = getObjects(store, mainNodeShape, SHACL.terms.targetClass, null)[0];
if (tcq) {
return tcq;
}
else {
throw new Error("No target class found in main SHACL Node Shapes");
}
}
else {
throw new Error("No main SHACL Node Shapes found in given member shape");
}
}
else {
throw new Error("No SHACL Node Shapes found in given member shape");
}
}