@rdfc/sparql-ingest-processor-ts
Version:
SPARQL Update function to be within RDF-Connect pipelines
143 lines (142 loc) • 5.95 kB
JavaScript
import { XSD } from "@treecg/types";
import { DataFactory } from "rdf-data-factory";
import { RdfStore } from "rdf-stores";
import { Writer as N3Writer } from "n3";
const df = new DataFactory();
export function getSubjects(store, predicate, object, graph) {
return store.getQuads(null, predicate, object, graph).map((quad) => {
return quad.subject;
});
}
export function getObjects(store, subject, predicate, graph) {
return store.getQuads(subject, predicate, null, graph).map((quad) => {
return quad.object;
});
}
export function splitStorePerNamedGraph(store) {
const stores = [];
const namedGraphs = new Set();
store.getQuads(null, null, null, null)
.forEach(q => namedGraphs.add(q.graph));
namedGraphs.forEach(ng => {
const subStore = RdfStore.createDefault();
const quads = store.getQuads(null, null, null, ng);
quads.forEach(q => subStore.addQuad(q));
stores.push({ graph: ng, store: subStore });
});
return stores;
}
export function splitStoreOnSize(store, threshold) {
const stores = [];
if (store.size < threshold) {
stores.push(store);
}
else {
const quads = store.getQuads();
const bnSet = new Set();
let subStore = RdfStore.createDefault();
for (let i = 0; i < quads.length; i++) {
if (bnSet.has(`${quads[i].subject.value}${quads[i].predicate.value}${quads[i].object.value}${quads[i].graph.value}`)) {
continue;
}
if (subStore.size >= threshold) {
stores.push(subStore);
subStore = RdfStore.createDefault();
}
if (quads[i].subject.termType === "BlankNode") {
const subjectQuads = store.getQuads(quads[i].subject);
const objectQuads = store.getQuads(null, null, quads[i].subject);
[...subjectQuads, ...objectQuads].forEach((q) => {
subStore.addQuad(q);
bnSet.add(`${q.subject.value}${q.predicate.value}${q.object.value}${q.graph.value}`);
});
}
if (quads[i].object.termType === "BlankNode") {
const subjectQuads = store.getQuads(quads[i].object);
const objectQuads = store.getQuads(null, null, quads[i].object);
[...subjectQuads, ...objectQuads].forEach((q) => {
subStore.addQuad(q);
bnSet.add(`${q.subject.value}${q.predicate.value}${q.object.value}${q.graph.value}`);
});
}
subStore.addQuad(quads[i]);
}
stores.push(subStore);
}
return stores;
}
export function sanitizeQuads(store) {
for (const q of store.getQuads()) {
if (q.object.termType === "Literal" && q.object.datatype.value === XSD.integer) {
if (/\+\d+/.test(q.object.value) && q.object.value.startsWith("+")) {
store.removeQuad(q);
store.addQuad(df.quad(q.subject, q.predicate, df.literal(q.object.value.substring(1), df.namedNode(XSD.integer)), q.graph));
}
}
}
}
export async function doSPARQLRequest(query, config, dispatcher, logger) {
try {
const timeout = config.measurePerformance?.queryTimeout || 1800;
if (query.length > 0 && typeof query[0] !== 'string') {
const quads = query;
let hasQuads = config.targetNamedGraph !== undefined;
if (quads.some(q => !q.graph.equals(df.defaultGraph()))) {
hasQuads = true;
}
const writer = new N3Writer({ format: hasQuads ? "N-Quads" : "N-Triples" });
const serialized = writer.quadsToString(quads);
const url = new URL(config.graphStoreUrl);
if (config.accessToken) {
url.searchParams.append("access-token", config.accessToken);
}
logger.verbose(`[doSPARQLRequest] Executing SPARQL Graph Store request (POST) with ${quads.length} quads.`);
logger.debug(`[doSPARQLRequest] POSTing the following RDF quads:\n${serialized}`);
const res = await fetch(url.toString(), {
method: "POST",
headers: {
'Content-Type': hasQuads ? 'application/n-quads' : 'application/n-triples',
},
body: serialized,
dispatcher,
});
if (!res.ok) {
throw new Error(`HTTP request failed with code ${res.status} and message: \n${await res.text()}`);
}
await res.text();
return;
}
let queries = [];
const jointQuery = query.join(";\n");
if (config.forVirtuoso && Buffer.byteLength(jointQuery, 'utf8') > 1e6) {
queries = query;
}
else {
queries.push(jointQuery);
}
for (const q of queries) {
logger.debug(`[doSPARQLRequest] Executing SPARQL query: \n${q}`);
const res = await fetch(config.graphStoreUrl, {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
body: `update=${fixedEncodeURIComponent(q)}${config.accessToken ? `&access-token=${config.accessToken}` : ''}`,
dispatcher,
});
if (!res.ok) {
throw new Error(`HTTP request failed with code ${res.status} and message: \n${await res.text()}`);
}
await res.text();
}
}
catch (err) {
logger.error(`Error while executing SPARQL request: ${err.message} - ${err.cause}`);
throw err;
}
}
function fixedEncodeURIComponent(str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
return '%' + c.charCodeAt(0).toString(16);
});
}