UNPKG

@rdfc/sparql-ingest-processor-ts

Version:
173 lines (172 loc) 7.3 kB
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, splitStore } from "./Utils.js"; const df = new DataFactory(); export const CREATE = (store, forVirtuoso, namedGraph, multipleNamedGraphs) => { const stores = splitStore(store, forVirtuoso ? 500 : 50000); return stores.map((subStore, i) => { return ` INSERT DATA { ${namedGraph ? `GRAPH <${namedGraph}> {` : ""} ${new N3Writer().quadsToString(subStore.getQuads())} ${namedGraph ? `}` : ""} } ${i === stores.length - 1 ? "" : ";"} `; }); }; export const UPDATE = (store, forVirtuoso, namedGraph, multipleNamedGraphs) => { const formattedQuery = formatQuery(store); const stores = splitStore(store, forVirtuoso ? 500 : 50000); const queries = [ ` ${namedGraph ? `WITH <${namedGraph}>` : ""} DELETE { ${formattedQuery[0]} } WHERE { ${formattedQuery[0]} }; ` ]; stores.forEach((subStore, i) => { queries.push(` INSERT DATA { ${namedGraph ? `GRAPH <${namedGraph}> {` : ""} ${new N3Writer().quadsToString(subStore.getQuads())} ${namedGraph ? `}` : ""} } ${i === stores.length - 1 ? "" : ";"} `); }); return queries; }; export const DELETE = (store, memberIRIs, memberShapes, namedGraph, multipleNamedGraphs) => { const deleteBuilder = []; const whereBuilder = []; let indexStart = 0; for (const memberIRI of memberIRIs) { const formatted = formatQuery(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 formatQuery(memberStore, memberIRI, memberShapes, indexStart = 0) { const subjectSet = new Set(); const blankNodeMap = new Map(); const queryBuilder = []; const formattedQueries = []; let i = indexStart; if (!memberShapes || memberShapes.length === 0) { for (const quad of memberStore.getQuads()) { 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") { if (!blankNodeMap.has(quad.subject.value)) { blankNodeMap.set(quad.subject.value, `?bn_${i}`); } if (quad.object.termType === "BlankNode") { blankNodeMap.set(quad.object.value, `?bn_ref_${i}`); } queryBuilder.push(`${blankNodeMap.get(quad.subject.value)} <${quad.predicate.value}> ${quad.object.termType === "Literal" ? `"${quad.object.value}"^^<${quad.object.datatype.value}>` : quad.object.termType === "BlankNode" ? `${blankNodeMap.get(quad.object.value)} ` : `<${quad.object.value}>`}.`); queryBuilder.push(`${blankNodeMap.get(quad.subject.value)} ?p_${i} ?o_${i}.`); queryBuilder.push(`?s_ref_${i} ?p_ref_${i} ${blankNodeMap.get(quad.subject.value)}.`); } 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)[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"); } }