| sidebar_position | 2 |
|---|
Reactodia defines a contract (DataProvider interface) to query a subset of data from external source (data graph) to provide means for incremental data loading when exploring the graph.
Reactodia uses RDF (Resource Description Framework) as a representation format for the graph data. The core concepts of RDF are:
- IRI (Internationalized Resource Identifier) — basically a URI but not limited to ASCII and may contain most unicode characters.
- resource — a graph node (element) represented by an IRI (in which case it is a named node) or a anonymous dataset-local identifier (it which case it is a blank node).
- literal — a simple value represented by a string with a datatype or a language tag.
- triple — an expressions of the form subject–predicate–object to represent a graph edge of type predicate (link type) between source resource and target resource or literal.
- quad — a triple with an additional associated graph IRI.
For interoperability with other RDF-based libraries for JavaScript, the property values for entities and relations are stored as either named node or literal values using commonly used RDF/JS representation.
To provide improved type-safety with TypeScript when dealing with various kinds of IRIs from the data graph, the library uses the following branded string types:
| Type | Description |
|---|---|
ElementIri |
IRI of a entity (resource). |
ElementTypeIri |
IRI of a entity type (resource). |
LinkTypeIri |
IRI of a link type, i.e. triple predicate when the object is a resource (the predicate is always a named node). |
PropertyTypeIri |
IRI of a property type, i.e. triple predicate when the object is a literal (the predicate is always a named node). |
The library provides a number of built-in DataProvider interface implementations for various scenarios:
| Provider | Description |
|---|---|
EmptyDataProvider |
An empty provider which returns nothing from all query methods. |
RdfDataProvider |
Provides graph data from an in-memory RDF/JS-compatible graph dataset. |
SparqlDataProvider |
Provides graph data by requesting it from a SPARQL endpoint. |
CompositeDataProvider |
Provides graph data by combining results from multiple other data providers. |
DecoratedDataProvider |
Generically wraps over another provider to modify how the requests are made or alter the results. |
IndexedDbCachedProvider |
Caches graph data returned from another data provider using browser's built-in IndexedDB storage. |
:::tip
It is recommended to extend EmptyDataProvider when implementing a data provider: this way methods can be implemented one-by-one as needed and no changes will be necessary if DataProvider will gain additional methods in the future.
:::
In this example Reactodia is initialized with RdfDataProvider which is provisioned with graph data in JSON Graph Format.
As a first step, the data in converted into RDF graph (triples), next the graph is added to the provider, finally all the nodes are added tot the diagram:
function ExampleRdfProviderProvisionFromJGF() {
const {defaultLayout} = Reactodia.useWorker(Layouts);
const {onMount} = Reactodia.useLoadedWorkspace(async ({context, signal}) => {
const {model, performLayout} = context;
// Example graph data based on JSON graph documentation:
const jsonGraph = {
"graph": {
"nodes": {
"alice": {
"label": "Alice",
"metadata": {
"type": "Person",
"birthDate": "1990-01-01"
}
},
"bob": {
"label": "Bob",
"metadata": {
"type": "Person",
"birthDate": "1990-02-02"
}
}
},
"edges": [
{
"source": "alice",
"relation": "isFriendOf",
"target": "bob",
"metadata": {
"since": "2000-03-03"
}
}
]
}
} as const;
const factory = Reactodia.Rdf.DefaultDataFactory;
const hasType = factory.namedNode(Reactodia.rdf.type);
const hasLabel = factory.namedNode(Reactodia.rdfs.label);
const triples: Reactodia.Rdf.Quad[] = [];
for (const [id, node] of Object.entries(jsonGraph.graph.nodes)) {
const iri = factory.namedNode(`graph:node:${id}`);
const {type, ...otherProperties} = node.metadata;
triples.push(
factory.quad(iri, hasType, factory.namedNode(`graph:type:${type}`)),
factory.quad(iri, hasLabel, factory.literal(node.label))
);
for (const [property, value] of Object.entries(otherProperties)) {
const propertyIri = factory.namedNode(`graph:property:${property}`);
triples.push(factory.quad(iri, propertyIri, factory.literal(value)));
}
}
for (const edge of jsonGraph.graph.edges) {
const source = factory.namedNode(`graph:node:${edge.source}`);
const target = factory.namedNode(`graph:node:${edge.target}`);
const predicate = factory.namedNode(`graph:node:${edge.relation}`);
const edgeTriple = factory.quad(source, predicate, target);
triples.push(edgeTriple);
for (const [property, value] of Object.entries(edge.metadata)) {
const propertyIri = factory.namedNode(`graph:property:${property}`);
triples.push(factory.quad(edgeTriple, propertyIri, factory.literal(value)));
}
}
const dataProvider = new Reactodia.RdfDataProvider();
dataProvider.addGraph(triples);
await model.createNewDiagram({dataProvider, signal});
for (const {element} of await dataProvider.lookup({elementTypeId: 'graph:type:Person'})) {
model.createElement(element.id);
}
await model.requestData();
await performLayout({signal});
}, []);
return (
<div className='reactodia-live-editor'>
<Reactodia.Workspace ref={onMount}
defaultLayout={defaultLayout}>
<Reactodia.DefaultWorkspace />
</Reactodia.Workspace>
</div>
);
}