graph-explorer
Version:
Graph Explorer can be used to explore and RDF graphs in SPARQL endpoints or on the web.
272 lines (250 loc) • 7.1 kB
text/typescript
import { createElement, ClassAttributes } from "react";
import * as ReactDOM from "react-dom";
import * as SparqlJS from "sparqljs";
const SparqlGenerator = SparqlJS.Generator;
import SparqlClient from "sparql-http-client";
const RDFS_PREFIX = "http://www.w3.org/2000/01/rdf-schema#";
const RDF_PREFIX = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
import {
Workspace,
WorkspaceProps,
ElementTypeIri,
LinkTypeIri,
LocalizedString,
ElementModel,
LinkModel,
SparqlDataProvider,
SparqlQueryMethod,
OWLRDFSSettings,
AuthoringState,
} from "../src/graph-explorer/index";
import { ExampleMetadataApi } from "./resources/exampleMetadataApi";
import {
onPageLoad,
tryLoadLayoutFromLocalStorage,
saveLayoutToLocalStorage,
} from "./common";
const rdfs = {
subClassOf: (RDFS_PREFIX + "subClassOf") as LinkTypeIri,
label: (RDFS_PREFIX + "label") as LinkTypeIri,
subPropertyOf: (RDFS_PREFIX + "subPropertyOf") as LinkTypeIri,
};
const rdf = {
class: (RDF_PREFIX + "Class") as ElementTypeIri,
a: (RDF_PREFIX + "type") as LinkTypeIri,
};
function onWorkspaceMounted(workspace: Workspace) {
if (!workspace) {
return;
}
const SparqlDialect = OWLRDFSSettings;
SparqlDialect.fullTextSearch = {
prefix: "",
queryPattern: `
?inst rdfs:label ?searchLabel.
(?searchLabel ?score) <tag:stardog:api:property:textMatch> "\${text}".
`,
};
SparqlDialect.filterTypePattern = "?inst a ?class";
const sparqlDataProvider = new SparqlDataProvider(
{
endpointUrl: "/sparql",
queryMethod: SparqlQueryMethod.GET,
},
SparqlDialect
);
const diagram = tryLoadLayoutFromLocalStorage();
workspace.getModel().importLayout({
diagram,
validateLinks: true,
dataProvider: sparqlDataProvider,
});
}
const props: WorkspaceProps & ClassAttributes<Workspace> = {
ref: onWorkspaceMounted,
onSaveDiagram: (workspace) => {
const diagram = workspace.getModel().exportLayout();
window.location.hash = saveLayoutToLocalStorage(diagram);
window.location.reload();
},
onPersistChanges: generateSparqlUpdate,
metadataApi: new ExampleMetadataApi(),
elementTemplateResolver: (_types) => {
return undefined;
},
};
onPageLoad((container) => {
ReactDOM.render(createElement(Workspace, props), container);
});
function generateSparqlUpdate(workspace: Workspace) {
const state = workspace.getEditor().authoringState;
const model = workspace.getModel();
const elementUpdateStructure = {
type: "update" as const,
updates: [] as any,
prefixes: {},
};
const deletedElements: string[] = [];
const deletedLinks: string[] = [];
state.elements.forEach((element) => {
if (element.deleted) {
deletedElements.push(element.after.id);
elementUpdateStructure.updates.push({
updateType: "delete",
delete: [
{ type: "bgp", triples: triplesFromElementState(element.after) },
],
});
} else if (!element.before) {
// simple insert data
elementUpdateStructure.updates.push({
updateType: "insert",
insert: [
{ type: "bgp", triples: triplesFromElementState(element.after) },
],
});
} else {
elementUpdateStructure.updates.push({
updateType: "insertdelete",
delete: [
{ type: "bgp", triples: triplesFromElementState(element.before) },
],
where: [
{ type: "bgp", triples: triplesFromElementState(element.before) },
],
insert: [
{ type: "bgp", triples: triplesFromElementState(element.after) },
],
});
}
});
state.links.forEach((link) => {
if (link.deleted) {
deletedLinks.push(
model.findLink(
link.after.linkTypeId,
model.elements.find((element) => element.iri == link.after.sourceId)
.id,
model.elements.find((element) => element.iri == link.after.targetId)
.id
).id
);
elementUpdateStructure.updates.push({
updateType: "delete",
delete: [{ type: "bgp", triples: triplesFromLinkState(link.after) }],
});
} else {
elementUpdateStructure.updates.push({
updateType: "insert",
insert: [{ type: "bgp", triples: triplesFromLinkState(link.after) }],
});
}
});
const parser = new SparqlGenerator({ newline: "" });
if (elementUpdateStructure.updates.length > 0) {
const updateQuery = parser.stringify(elementUpdateStructure);
const client = new SparqlClient({
endpointUrl: window.location.origin + "/query",
updateUrl: window.location.origin + "/update",
});
client.query.update(updateQuery);
workspace.getEditor().setAuthoringState(AuthoringState.empty);
deletedElements.forEach((iri) => {
console.log(model.elements.find((element) => element.iri == iri).id);
model.removeElement(
model.elements.find((element) => element.iri == iri).id
);
});
deletedLinks.forEach((id) => {
model.removeLink(id);
});
}
}
/**
*
* @param elementState the state of an element, either before or after
* @returns a arrary of triples in BGP format for SparqlJS
*/
function triplesFromElementState(elementState: ElementModel) {
const triples = [] as any;
// grab types
elementState.types.forEach((type) => {
triples.push({
subject: {
termType: "NamedNode",
value: elementState.id,
},
predicate: {
termType: "NamedNode",
value: rdf.a,
},
object: {
termType: "NamedNode",
value: type,
},
});
});
// grab label
elementState.label.values.forEach((label) => {
triples.push({
subject: {
termType: "NamedNode",
value: elementState.id,
},
predicate: {
termType: "NamedNode",
value: rdfs.label,
},
object: {
termType: "Literal",
value: label.value,
language: label.language,
},
});
});
// grab all properties
for (const [key, property] of Object.entries(elementState.properties)) {
property.values.forEach((value) => {
const v = value as LocalizedString;
triples.push({
subject: {
termType: "NamedNode",
value: elementState.id,
},
predicate: {
termType: "NamedNode",
value: key,
},
object: {
termType: "Literal",
value: v.value,
language: v.language,
},
});
});
}
return triples;
}
/**
*
* @param linkState the state of a link, either before or after
* @returns a array of triples in BGP format for SparqlJS
*/
function triplesFromLinkState(linkState: LinkModel) {
const triples = [];
triples.push({
subject: {
termType: "NamedNode",
value: linkState.sourceId,
},
predicate: {
termType: "NamedNode",
value: linkState.linkTypeId,
},
object: {
termType: "NamedNode",
value: linkState.targetId,
},
});
return triples;
}