0.4.1 • Published 5 years ago

ram-shapes v0.4.1

Weekly downloads
3
License
MIT
Repository
github
Last release
5 years ago

RAM shapes: declarative RDF ↔ ADT mapping npm version

Home page | Specification draft | Playground

RAM is a type construction language, specification and an implementation of mapping operations between RDF graphs and structured data types.

Features

RAM introduces a language based on RDF which allows to describe a runtime object interface with so-called "shapes". The shapes are basically types augumented with metadata to map them into RDF graph. Usage of such shapes allows to:

  • Map RDF graph data into JS objects.
  • Generate RDF quad/triple data from JS objects.
  • Construct SPARQL queries to fetch necessary data for given shapes.
  • (In the future) Validate that runtime object structure matches specified shape.

Installation

Install with npm install --save ram-shapes

Usage

Try out on the interactive playground.

import * as Ram from 'ram-shapes';
import * as N3 from 'n3';
import * as SparqlJs from 'sparqljs';

// get graph triples (source data)
const dataset = Ram.Rdf.dataset(new N3.Parser().parse(`
    @prefix ex: <http://example.com/schema/>.
    @prefix : <http://example.com/data/>.

    :anno1 a ex:Annotation;
        ex:start :point1;
        ex:end ("1" "2").

    :point1 a ex:Point;
        ex:position 42.
`));

// define custom RAM shapes using Turtle syntax
const shapes = Ram.frameShapes(Ram.Rdf.dataset(new N3.Parser().parse(`
    @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
    @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
    @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
    @prefix : <http://ram-shapes.github.io/schema#>.
    @prefix ex: <http://example.com/schema/>.

    ex:Annotation a :ObjectShape;
        :typeProperty [
            :name "type";
            :path ([ :predicate rdf:type ]);
            :shape [ a :ResourceShape; :termValue ex:Annotation ]
        ];
        :property [
            :name "id";
            :path ();
            :shape [ a :ResourceShape ]
        ];
        :property [
            :name "start";
            :path ([ :predicate ex:start ]);
            :shape ex:Selector
        ];
        :property [
            :name "end";
            :path ([ :predicate ex:end ]);
            :shape [ a :OptionalShape; :item ex:Selector ]
        ].

    ex:Selector a :UnionShape;
        :variant ex:Point, ex:Path.

    ex:Point a :ObjectShape;
        :typeProperty [
            :name "type";
            :path ([ :predicate rdf:type ]);
            :shape [ a :ResourceShape; :termValue ex:Point ]
        ];
        :property [
            :name "position";
            :path ([ :predicate ex:position ]);
            :shape [ a :LiteralShape; :termDatatype xsd:integer ]
        ].

    ex:Path a :ListShape;
        :item [ a :LiteralShape; :termDatatype xsd:string ].
`)));

// choose entry point shape
const rootShape = Ram.Rdf.namedNode(
  'http://example.com/schema#Annotation'
);
// (optionally) specify prefixes for Turtle and SPARQL
const prefixes = {
  rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
  rdfs: "http://www.w3.org/2000/01/rdf-schema#",
  xsd: "http://www.w3.org/2001/XMLSchema#",
  ex: "http://example.com/schema/",
  "": "http://example.com/data/",
};

// use defined shapes to lower RDF graph into JS objects...
const matches = Ram.frame({shapes, rootShape, dataset}));
for (const match of matches) {
  /* match.value object has ex:Annotation shape, e.g.:
    {
      "type": "http://example.com/schema/Annotation",
      "id": "http://example.com/data/anno1",
      "start": {
        "type": "http://example.com/schema/Point",
        "position": 42
      },
      "end": ["1", "2"]
    }
  */

  // ... and lift JS object back into an RDF graph
  const quads = Ram.flatten({
    shapes,
    rootShape,
    value: match.value,
    prefixes,
  });
  /* quads is Iterable<Ram.Rdf.Quad>, e.g.:

    :anno1 a ex:Annotation;
        ex:start _:object_044916_1.
    _:object_044916_1 a ex:Point;
        ex:position "42"^^xsd:integer.
    :anno1 ex:end _:list_044916_2.
    _:list_044916_2 rdf:first "1";
        rdf:rest _:list_044916_3.
    _:list_044916_3 rdf:first "2";
        rdf:rest rdf:nil.
  */
}

// another application of defined shapes is to generate a CONSTRUCT query
// to get necessary graph data for framing
const query = Ram.generateQuery({shapes, rootShape, prefixes});
const queryString = new SparqlJs.Generator().stringify(query);
/* query is a CONSTRUCT query in SPARQL.js runtime format:

  PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
  PREFIX ex: <http://example.com/schema/>
  CONSTRUCT {
    ?object_1 rdf:type ex:Annotation.
    ?object_1 ex:start ?object_4.
    ?object_4 rdf:type ex:Point.
    ?object_4 ex:position ?literal_5.
    ?object_1 ex:start ?list_6.
    ?listNode_7 rdf:rest ?nextNode_8.
    ?listNode_7 rdf:first ?literal_9.
    ?object_1 ex:end ?object_11.
    ?object_11 rdf:type ex:Point.
    ?object_11 ex:position ?literal_12.
    ?object_1 ex:end ?list_13.
    ?listNode_14 rdf:rest ?nextNode_15.
    ?listNode_14 rdf:first ?literal_16.
  }
  WHERE {
    ?object_1 rdf:type ex:Annotation.
    {
      ?object_1 ex:start ?object_4.
      ?object_4 rdf:type ex:Point.
      ?object_4 ex:position ?literal_5.
    }
    UNION
    {
      ?object_1 ex:start ?list_6.
      ?list_6 (rdf:rest*) ?listNode_7.
      ?listNode_7 rdf:rest ?nextNode_8.
      ?listNode_7 rdf:first ?literal_9.
    }
    OPTIONAL {
      {
        ?object_1 ex:end ?object_11.
        ?object_11 rdf:type ex:Point.
        ?object_11 ex:position ?literal_12.
      }
      UNION
      {
        ?object_1 ex:end ?list_13.
        ?list_13 (rdf:rest*) ?listNode_14.
        ?listNode_14 rdf:rest ?nextNode_15.
        ?listNode_14 rdf:first ?literal_16.
      }
    }
  }
 */

References

A publication which describes this work is currently under review at MTSR Conference 2019.

0.4.1

5 years ago

0.4.0

5 years ago

0.3.0

5 years ago