0.1.2 • Published 6 years ago

ontology-store v0.1.2

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

ontology-store.js

A fully persistent, immutable RDF store with an OWL API. Check out the docs here (WIP)

ontology-store.js is browser-compatible (non-SPARQL compliant) RDF store specialized for reading and editing the Web Ontology Language (or OWL).

ontology-store.js understands RDF and OWL so you don't have to.

This library:

  1. Reads in an RDF graph (either from an RDF serialization or a JS structure) and stores it into a fully-persistent data structure.
  2. Provides an OWL API to read and write from the data structure using OWL constructs.
  3. Writes the modified graph or sub-graph to a valid RDF serialization (provided by N3.js) or a JS structure.

DISCLAIMER

This library is just in its beginning state. There are performance issues and it has yet to be fully tested. Feel free to contribute and open issues discussing features you'd like to see.

Thank you!

Quick example

// simple example for use within node
const OntologyStore = require('ontology-store');

const ab = 'http://ontology-store.js.org/tests#';
const rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
const rdfs = 'http://www.w3.org/2000/01/rdf-schema#';
const owl = 'http://www.w3.org/2002/07/owl#';

const prefixes = `
@prefix ab:   <${ab}> .
@prefix rdf:  <${rdf}> .
@prefix rdfs: <${rdfs}> .
@prefix owl: <${owl}> .`;

const example = `
  ${prefixes}

  # ontology definition
  ab:ExampleOntology
    a owl:Ontology ;
    rdfs:label "Example Ontology" ;
    rdfs:comment "this is an example ontology" .

  ab:BusinessEntity
    a owl:Class ;
    rdfs:comment "something that belongs to a business or firm" ;
    rdfs:label "Business Entity" .

  # simple class with owl:Class assertion
  ab:Person
    a owl:Class ;
    rdfs:label "Person" ;
    rdfs:isDefinedBy ab:ExampleOntology .

  # subclass
  ab:Employee
    a owl:Class ;
    rdfs:subClassOf ab:Person ;
    rdfs:subClassOf ab:BusinessEntity ;
    rdfs:label "Employee" ;
    rdfs:isDefinedBy ab:ExampleOntology .

  ab:Contractor
    # purposely omit, "a owl:Class"
    rdfs:subClassOf ab:Employee ;
    rdfs:label "Contractor" ;
    rdfs:comment "a contractor is a subclass of an employee" ;
    rdfs:isDefinedBy ab:ExampleOntology .

  ab:HourlyEmployee
    a owl:Class ;
    rdfs:isDefinedBy ab:ExampleOntology ;
    rdfs:subClassOf ab:Employee ;
    rdfs:label "Hourly Employee" .

  ab:SalariedEmployee
    a rdfs:Class ;
    rdfs:subClassOf ab:Employee ;
    rdfs:isDefinedBy ab:ExampleOntology ;
    rdfs:label "Salaried Employee" .

  # simple class with rdfs:Class assertion
  ab:Product
    a rdfs:Class ;
    rdfs:label "Product" ;
    rdfs:isDefinedBy ab:ExampleOntology .
  
  # object property defined with owl:ObjectProperty and rdf:Property
  ab:worksOn
    a owl:ObjectProperty ;
    a rdf:Property ;
    rdfs:domain ab:Employee ;
    rdfs:range ab:Product ;
    rdfs:label "works on" ;
    rdfs:isDefinedBy ab:ExampleOntology .

  # simple rdf:Property
  ab:additionalComments
    a rdf:Property ;
    rdfs:label "additional comments" ;
    rdfs:isDefinedBy ab:ExampleOntology .

  ab:workedOnBy
    a owl:ObjectProperty ;
    rdfs:domain ab:Product ;
    rdfs:range ab:Employee ;
    rdfs:inverseOf ab:worksOn ;
    rdfs:label "worked on" ;
    rdfs:isDefinedBy ab:ExampleOntology .

  # owl datatype property defined with owl:DatatypeProperty
  ab:startDate
    a owl:DatatypeProperty ;
    rdfs:domain ab:Employee ;
    rdfs:label "start date" ;
    rdfs:isDefinedBy ab:ExampleOntology .
`;

OntologyStore.parse(example).then(store => {
  // ===============================================================================================
  // READ ONTOLOGY
  // ===============================================================================================
  /** the first class in the set of all classes */
  const employee = store.classes.get(`${ab}Employee`);
  console.log(employee.label); // logs `Employee`

  // get a list of subclass labels
  const employeeSubclasses = employee.subClasses.seq().map(subclass => subclass.label).toArray();
  console.log(employeeSubclasses.join(', ')) // logs `Contractor, HourlyEmployee, SalariedEmployee`

  // serialize the subset
  employee.subClasses.serialize().then(result => console.log(result));
  /* #result is this:
  @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
  @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
  @prefix owl: <http://www.w3.org/2002/07/owl#>.

  <http://ontology-store.js.org/tests#Contractor> rdfs:subClassOf <http://ontology-store.js.org/tests#Employee>;
      rdfs:label "Contractor";
      rdfs:comment "a contractor is a subclass of an employee";
      rdfs:isDefinedBy <http://ontology-store.js.org/tests#ExampleOntology>.
  <http://ontology-store.js.org/tests#HourlyEmployee> a owl:Class;
      rdfs:isDefinedBy <http://ontology-store.js.org/tests#ExampleOntology>;
      rdfs:subClassOf <http://ontology-store.js.org/tests#Employee>;
      rdfs:label "Hourly Employee".
  <http://ontology-store.js.org/tests#SalariedEmployee> a rdfs:Class;
      rdfs:subClassOf <http://ontology-store.js.org/tests#Employee>;
      rdfs:isDefinedBy <http://ontology-store.js.org/tests#ExampleOntology>;
      rdfs:label "Salaried Employee".
  */

  // ===============================================================================================
  // MODIFY ONTOLOGY
  // ===============================================================================================
  
  // changes are done immutably
  const newStore = employee.subClasses.create({
    label: 'New Sub Employee',
    comment: 'represents a sub employee',
    definedBy: ontologies => ontologies.seq().first(),
    thenConsume: newSubClass => {
      newSubClass.serialize().then(result => console.log(result));

      /* # results in:
      @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
      @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
      @prefix owl: <http://www.w3.org/2002/07/owl#>.

      <http://ontology-store.js.org/tests#ExampleOntologyf8affcf7-412c-4c42-945e-6edd50166ff6-New%20Sub%20Employee> rdfs:
      isDefinedBy <http://ontology-store.js.org/tests#ExampleOntology>;
          a owl:Class, rdfs:Class;
          rdfs:subClassOf <http://ontology-store.js.org/tests#Employee>;
          rdfs:label "New Sub Employee";
          rdfs:comment "represents a sub employee".
      */
    }
  });

  // do something with `newStore`
  // `store` remains unchanged
});

Install

This library is built using the UMD module definition to allow maximum compatibly.

Please ensure that you have your proxy settings correct before installing.

If you're not using a module system, the library will be available via the global OntologyStore.

npm and/or node

npm install --save ontology-store

then use like so:

const OntologyStore = require('ontology-store');

bower

bower install --save ontology-store.js

then you can use it in a script tag like so:

<script src="./bower_components/ontology-store/docs/ontology-store.js"></script>

or just download the minified JS

Here you go!

Motivation

Supporting an ontology editor user interface requires us to create, edit, and store a valid RDF graph that is aware of supported OWL constructs.

This RDF store is tailored towards building ontologies (metadata) written in OWL and not storing any arbitrary RDF graph (e.g. instances). This RDF store exposes an OWL-aware API to allow for easy access and manipulation of the RDF graph with respect to OWL constructs. Additionally, this RDF store has the ability to both parse and serialize to and from a valid RDF serialization.

So overall, the purpose of this code is to:

  1. provide a backend to parse, store, and serialize an RDF graph of OWL terms and to
  2. provide an API to manipulate an OWL/RDF graph

Design goals (desired features)

  1. Parse and serialize any RDF graph. This does not mean the has to be able to edit/visualize every triple but it should be able to read, store, and re-write an RDF graph without deleting triples. This also requires bnode support.
  2. Store as a fully persistent RDF graph to allow for arbitrary undos and redos. This undo/redo requirement causes a lot of positive and negative trade-offs that will be (TODO) discussed later in this doc.
  3. Allow manipulation of the RDF graph through 'resource sets'. A 'resource set' is an arbitrary set of resources (a resource is simply a node in the RDF graph identified by a URI) that can be manipulated by adding or removing individual resources. An example of a resource set could be the subclasses of "Animal" that could include the resources "Dog" and "Cat". The API should then allow the user to add another resource (e.g. "Bird") or remove the existing resources (i.e. "Dog" and "Cat").
  4. Support for importing other RDF graphs. Terms from other graphs should be reusable in the current graph. Importing isn't simply copying all the terms from one graph into the current graph but bring external terms into the scope of the current graph. The classes and properties of the imported graph don't necessarily have to be completely readonly because assertions made in the current graph will have a different namespace than the external graph therefore eliminating conflicts. Additionally, terms from other graphs should not be copied into the serialization of the current ontology but referenced. Import will be (TODO) discussed throughly later in this doc.

Supported OWL constructs

These are the only current supported OWL constructs. It may be a small subset, but this subset it enough to define mappable vocabulary.

  • RDF: rdf:type, rdf:Property
  • RDFS: rdfs:Class, rdfs:subClassOf, rdfs:subPropertyOf, rdfs:domain, rdfs:range, rdfs:label, rdfs:comment, rdfs:isDefinedBy
  • OWL: owl:Class, owl:ObjectProperty, owl:DatatypeProperty, owl:Ontology, owl:imports, owl:versionInfo

Test cases

The current features of the library have been tested to be stable. Please check the tests output here.