The sitemap: ontology that defines application structure

Happy 2015 everyone!

We decided to kick off the year by finally posting more about the
current status of Graphity, as the implementation has matured and is
pretty stable.

We want to start with showing an example of the core notion of the
declarative LD application approach -- the sitemap, which is an
ontology consisting of resource templates that define the application
structure.

The following is the sitemap of a simple blogging application with 2
main concepts: posts and categories. Both have collection (container)
and item pages, and categories can be attached to posts.


<#> a owl:Ontology ;
    owl:imports gcs:, void:, foaf:, dqc: ;
    rdfs:label "Blog sitemap" ;
    dct:created "2014-10-21T01:53:00+01:00"^^xsd:dateTime .

# RESOURCE CLASSES (GRAPHITY TEMPLATES)

<#Site> a owl:Class, gp:Template ;
     rdfs:subClassOf gp:Space, foaf:Document, sioc:Space ;
     gp:uriTemplate "/" ;
     spin:query gp:DescribeWithChildren ;
     gp:limit 20 ;
     gc:defaultMode gc:ListMode ;
     # gc:mode gc:ReadMode, gc:ListMode, gc:TableMode, gc:ThumbnailMode ;
     rdfs:label "Homepage" ;
     rdfs:isDefinedBy <#> .

# posts

<#Forum> a owl:Class, gp:Template ;
    rdfs:subClassOf foaf:Document, gp:Container, sioc:Forum ;
    gp:uriTemplate "/posts" ;
    gp:limit 20 ;
    gp:orderBy "title"^^xsd:string ;
    gc:defaultMode gc:ReadMode ;
    gc:mode gc:ReadMode, gc:ListMode, gc:TableMode, gc:ThumbnailMode,
gc:CreateMode ;
    spin:query gp:DescribeWithChildren ;
    rdfs:label "Post container" ;
    rdfs:isDefinedBy <#> .

<#Post> a rdfs:Class, owl:Class, gp:Template ;
    rdfs:subClassOf foaf:Document, gp:Item, sioc:Post ,
        [ a owl:Restriction ;
            owl:onProperty sioc:has_container ;
            owl:allValuesFrom <#Forum>
        ] ;
    gp:uriTemplate "/posts/{slug}" ;
    gp:skolemTemplate "/{slug}" ;
    gc:mode gc:ReadMode, gc:EditMode ;
    gc:defaultMode gc:ReadMode ;
    gp:cacheControl "public, max-age=3600" ;
    spin:query gp:Describe ;
    spin:update gp:Delete ;
    spin:constructor
              [ a       sp:Construct ;
                sp:text """
                    PREFIX blog:    <http://graphity.org/blog#>
                    PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
                    PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>
                    PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
                    PREFIX sioc:    <http://rdfs.org/sioc/ns#>
                    PREFIX gp:      <http://graphity.org/gp#>
                    PREFIX gc:      <http://graphity.org/gc#>
                    PREFIX dct:     <http://purl.org/dc/terms/>

                    CONSTRUCT {
                        ?this a foaf:Document, blog:Post, sioc:Post,
gp:Item, sioc:Item ;
                            dct:title " " ;
                            sioc:content " " ;
                            dct:subject _:category ;
                            gp:slug " "^^xsd:string .
                    }
                    WHERE {}"""
              ] ;
    spin:constraint [ a dqc:MissingProperties ;
   sp:arg1 <#Post> ;
   sp:arg2 gp:slug
] ,
        [ a dqc:MissingProperties ;
   sp:arg1 <#Post> ;
   sp:arg2 dct:title
] ,
        [ a dqc:MissingProperties ;
   sp:arg1 <#Post> ;
   sp:arg2 sioc:content
] ;
    rdfs:label "Post document" ;
    rdfs:isDefinedBy <#> .

# categories

<#CategoryContainer> a owl:Class, gp:Template ;
    rdfs:subClassOf foaf:Document, gp:Container, sioc:Container ;
    gp:uriTemplate "/categories" ;
    gp:limit 20 ;
    gp:orderBy "prefLabel"^^xsd:string ;
    gc:defaultMode gc:ReadMode ;
    gc:mode gc:ReadMode, gc:ListMode, gc:TableMode, gc:ThumbnailMode,
gc:CreateMode ;
    spin:query gp:DescribeWithChildren ;
    rdfs:label "Category container" ;
    rdfs:isDefinedBy <#> .

<#CategoryItem> a rdfs:Class, owl:Class, gp:Template ;
    rdfs:subClassOf foaf:Document, gp:Item, sioc:Item ,
        [ a owl:Restriction ;
            owl:onProperty sioc:has_container ;
            owl:allValuesFrom <#CategoryContainer>
        ] ;
    gp:uriTemplate "/categories/{slug}" ;
    gp:skolemTemplate "/{slug}" ;
    gc:mode gc:ReadMode, gc:EditMode ;
    gc:defaultMode gc:ReadMode ;
    gp:cacheControl "public, max-age=3600" ;
    spin:query [ a       sp:Describe ;
                sp:text """
                    PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>
                    PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
                    PREFIX dct:     <http://purl.org/dc/terms/>

                    DESCRIBE ?this ?category ?post {
                        ?category foaf:isPrimaryTopicOf ?this
                        OPTIONAL {
                            SELECT ?post ?category
                            {
                                ?post dct:subject ?category
                            }
                        }
                    }"""
              ] ;
    spin:update gp:DeleteWithTopic ;
    spin:constructor
              [ a       sp:Construct ;
                sp:text """
                    PREFIX blog:    <http://graphity.org/blog#>
                    PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
                    PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>
                    PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
                    PREFIX sioc:    <http://rdfs.org/sioc/ns#>
                    PREFIX gp:      <http://graphity.org/gp#>
                    PREFIX skos:    <http://www.w3.org/2004/02/skos/core#>

                    CONSTRUCT {
                        ?this a foaf:Document, blog:CategoryItem,
gp:Item, sioc:Item ;
                            foaf:primaryTopic _:category ;
                            gp:slug " "^^xsd:string .
                        _:category a blog:Category, skos:Concept ;
                            foaf:isPrimaryTopicOf ?this ;
                            skos:prefLabel " " .
                    }
                    WHERE {}"""
              ] ;
    spin:constraint [ a dqc:MissingProperties ;
   sp:arg1 <#CategoryItem> ;
   sp:arg2 gp:slug
] ;
    rdfs:label "Category document" ;
    rdfs:isDefinedBy <#> .

<#Category> a rdfs:Class, owl:Class ;
    rdfs:subClassOf skos:Concept,
        [ a owl:Restriction ;
            owl:onProperty foaf:isPrimaryTopicOf ;
            owl:allValuesFrom <#CategoryItem>
] ;
    gp:skolemTemplate "/{isPrimaryTopicOf.slug}" ;
    spin:constraint [ a dqc:MissingProperties ;
   sp:arg1 <#Category> ;
   sp:arg2 skos:prefLabel
] ;
    rdfs:label "Category" ;
    rdfs:isDefinedBy <#> .


I have skipped the namespaces here, but you can find the full file on GitHub:
https://github.com/Graphity/blog-app/blob/master/src/main/resources/org/graphity/blog/vocabulary/blog.ttl

If you check the source code in the repository, you will see that
there are only 2 Java classes intended as extension points. Those
could also be removed, making the application fully declarative,
consisting only of the sitemap and the XSLT stylesheet (about which we
will post separately). Processing model embedded in Graphity makes
this possible. We have also implemented access control declaratively,
but it is not included for this application.

To see for yourself that the application works and is read-write, you
can visit a demo deployed at http://linkeddatahub.com/blog-app.

Looking at the sitemap, you can see a number of components:
- each container and and item gets its own class, as they have their
own URI templates, as indicated by gp:uriTemplate
- spin:query is used for resource description retrieval, spin:update for removal
- OWL restrictions indicate the structural relationships between
classes, which are based on SIOC vocabulary
- container classes have offset/limit/orderBy/desc modifiers that are
used for pagination
- item classes have skolemization URI templates, which are used to
build URIs from blank nodes in request RDF payload
- constraints indicate mandatory properties of class instances
- constructors define a template of a class instance
- cache control and layout modes can be specified per resource class

We plan to post in more detail about the more important aspects. If
you think some parts are unclear or interesting, or simply have
questions, please let us know and we will focus on that.


Martynas
graphityhq.com

Received on Friday, 2 January 2015 14:52:09 UTC