Skip to main content

Data Provider

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.

IRI and RDF

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 subjectpredicateobject 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:

TypeDescription
ElementIriIRI of a entity (resource).
ElementTypeIriIRI of a entity type (resource).
LinkTypeIriIRI of a link type, i.e. triple predicate when the object is a resource (the predicate is always a named node).
PropertyTypeIriIRI of a property type, i.e. triple predicate when the object is a literal (the predicate is always a named node).

Data Providers

The library provides a number of built-in DataProvider interface implementations for various scenarios:

ProviderDescription
EmptyDataProviderAn empty provider which returns nothing from all query methods.
RdfDataProviderProvides graph data from an in-memory RDF/JS-compatible graph dataset.
SparqlDataProviderProvides graph data by requesting it from a SPARQL endpoint.
CompositeDataProviderProvides graph data by combining results from multiple other data providers.
DecoratedDataProviderGenerically wraps over another provider to modify how the requests are made or alter the results.
IndexedDbCachedProviderCaches 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.

Example: provisioning an RdfDataProvider from a graph data in JSON Graph Format

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:

Result
Loading...
Live Editor
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('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
    const hasLabel = factory.namedNode('http://www.w3.org/2000/01/rdf-schema#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});

    const elementIris: Reactodia.ElementIri[] = [];
    for (const {element} of await dataProvider.lookup({elementTypeId: 'graph:type:Person'})) {
      elementIris.push(model.createElement(element).iri);
    }

    await model.requestElementData(elementIris);
    await model.requestLinks();
    await performLayout({signal});
  }, []);

  return (
    <div className='reactodia-live-editor'>
      <Reactodia.Workspace ref={onMount}
        defaultLayout={defaultLayout}>
        <Reactodia.DefaultWorkspace />
      </Reactodia.Workspace>
    </div>
  );
}