SPIN provides a representation for rules and constraints using SPARQL. The full SPIN implementation allows customers to:

AllegroGraph provides partial support for SPIN functions and magic-properties when using the sparql-1.1 SPARQL query engine. We will be continuing to expand our support in subsequent releases.

The SPIN API allows you to define a function in terms of a SPARQL query and then call that function in other SPARQL queries. These SPIN functions can appear in FILTERs and can also be used to compute values in assignment and select expressions.

Secondly, the API lets you associate a URI with a SPARQL query. You can use the URI in other queries as a magic property which can introduce additional bindings into the result set. These magic properties can implement complex rules.

Note that these SPIN associations are defined in the context of a triple-store session and that they do not persist when a triple-store is closed. This means that you currently need to re-define them each time you re-open a store.

HTTP API

Other clients

SPIN APIs for AllegroGraph's other clients are in progress. See the Javadocs for a description or the Java API.

Lisp API

The functions in Lisp API are all exported from the db.agraph.spin package.

SPIN Functions

register-spin-function uri  query  argument-names  &key  db
function

Create a spin-function structure and associate it with uri.

This creates a new spin-function structure whose query is query and whose arguments are named argument-names. The structure is then associated with uri.

For example:

(ag.spin:register-spin-function  
    (resource "age" "ex")  
    "prefix kennedy: <http://www.example.com/kennedy#>  
     select ( (2011 - xs:int(?birthYear)) as ?age ) {  
       ?who kennedy:birth-year ?birthYear .  
     }"  
    '(?who)) 

creates a function to compute the age of a subject (assuming that it is 2011!) and links it to ex:age. I can then use this in another query. For example:

select ?first ?last ?age ?birthYear {  
  ?person kennedy:first-name ?first .  
  ?person kennedy:last-name ?last .  
  ?person kennedy:birth-year ?birthYear .  
  bind( ex:age( ?person ) as ?age ) .  
} order by ?age limit 5 
unregister-spin-function uri  &key  db
function

Removes the association between uri and its SPIN function.

This signals a warning if uri is not currently associated with a SPIN function.

spin-functions &key  db
function

Returns a list of SPIN functions.

Each item in the list will be a sublist with two items: the uri (in N-Triples format) and its spin-function structure.

lookup-spin-function uri  &key  db
function

Find the SPIN function associated with uri.

Returns the spin-function structure for uri or nil if there is no associations.

SPIN magic properties

register-spin-magic-property uri  query  argument-names  &key  db
function

Create a spin-magic-property structure and associate it with uri.

This creates a new spin-magic-property structure whose query is query and whose arguments are named argument-names. The structure is then associated with uri.

For example:

;; my uncle is a male person who has the same parent as my parent  
(ag.spin:register-spin-magic-property  
  (resource "uncle" "ex")  
  "  
  prefix kennedy: <http://www.example.com/kennedy#>  
  select ?uncle {  
    ?parent kennedy:has-child ?child .  
    ?grand kennedy:has-child ?parent .  
    ?grand kennedy:has-child ?uncle .  
    filter( ?parent != ?uncle )  
    ?uncle kennedy:sex kennedy:male .  
  }")  
  '(?child)) 

creates a query to generate bindings for uncle from child (or from child to uncle) and links it with ex:uncle.

unregister-spin-magic-property uri  &key  db
function

Removes the association between uri and its SPIN magic property.

This signals a warning if uri is not currently associated with a SPIN magic property.

spin-magic-properties &key  db
function

Returns a list of SPIN magic properties.

Each item in the list will be a sublist with two items: the uri (in N-Triples format) and its spin-magic-property structure.

lookup-spin-magic-property uri  &key  db
function

Find the SPIN magic property associated with uri.

Returns the spin-magic property structure for uri or nil if there is no associations.

Example SPIN session

This example uses the Lisp API with the kennedy dataset included in the tutorial sources. We will define a SPIN function and some SPIN magic properties and then use them in some example queries. First, some setup:

(enable-!-reader)  
(enable-print-decoded t)  
(create-triple-store "kennedy")  
(load-ntriples "kennedy.ntriples")  
(register-namespace "kennedy" "http://www.franz.com/simple#")  
(register-namespace "ex" "http://franz.examples#") 

SPIN functions

A SPIN function allows you to use a SPARQL query to compute a value based on the values of other variables in your query. We will use a very contrived example to illustrate the mechanics.

Suppose we want a query that returns the age of the youngest five people in our triplestore. If we had function like age, then this would be easy:

(sparql:run-sparql "  
    prefix ex: <http://franz.examples#>  
    prefix kennedy: <http://www.franz.com/simple#>  
    select ?first ?last ?age ?birthYear {  
      ?person kennedy:first-name ?first .  
      ?person kennedy:last-name ?last .  
      ?person kennedy:birth-year ?birthYear .  
      bind( ex:age( ?person ) as ?age ) .  
    } order by ?age limit 5  
" :results-format :lists) 

Trying this now, however, leads to an error message because we have not provided any association between ex:age and a function. We can ask AllegroGraph to define an age function and to implement it with a SPARQL query using register-spin-function:

(ag.spin:register-spin-function  
    !ex:age  
    "prefix kennedy: <http://www.franz.com/simple#>  
     prefix xs: <http://www.w3.org/2001/XMLSchema#>  
     select ( (2011 - xs:int(?birthYear)) as ?age ) {  
       ?who kennedy:birth-year ?birthYear .  
     }"  
    '(?who)) 

Which connects the first argument (ex:age) to the query in the second argument and uses ?who as the variable 1 . When we call the function in our query, AllegroGraph will use the value of ?person (from the query) as a variable binding for ?who and evalute the SPIN function's SPARQL query. It will return the value computed for the first row selected (as if the function's query had LIMIT 1). If we now re-evaluate our first query, we will get results:

(({Kym} {Smith} {39} {1972})  
 ({Molly} {Stark} {43} {1968})  
 ({Rory} {Kennedy} {43} {1968})  
 ({Douglas} {Kennedy} {44} {1967})  
 ({Mark} {Bailey} {44} {1967})) 

SPIN Magic Properties

Suppose we now want explore the relationships amongst the people in the kennedy dataset. In particular, we would like to know who is the uncle of whom. For example, we might want to know the uncles of all of the children of person1:

(sparql:run-sparql  
  "  
  prefix ex: <http://franz.examples#>  
  prefix kennedy: <http://www.franz.com/simple#>  
  select distinct ?child ?uncle {  
    kennedy:person1 kennedy:has-child ?child .  
    ?uncle ex:uncle ?child .  
  }"  
  :results-format :lists) 

This query will return no results since uncle isn't defined as part of our dataset. To remedy this, we will need to do some computations. SPIN lets us do these computations using SPARQL. We can define someone's uncle as the brother of one of their parents.

(ag.spin:register-spin-magic-property  
  !ex:uncle  
  "  
  prefix ex: <http://franz.examples#>  
  prefix kennedy: <http://www.franz.com/simple#>  
  select ?uncle {  
    ?parent kennedy:has-child ?child .  
    ?parent ex:brother ?uncle .  
  }"  
  '(?child)) 

Since ex:brother is also not defined in our base dataset, we need an additional rule. A person's brother is a male child that shares a parent and is not the same person:

(ag.spin:register-spin-magic-property  
  !ex:brother  
  "  
  prefix ex: <http://franz.examples#>  
  prefix kennedy: <http://www.franz.com/simple#>  
  select ?brother {  
    ?parent kennedy:has-child ?child .  
    ?parent kennedy:has-child ?brother .  
    filter( ?child != ?brother )  
    ?brother kennedy:sex kennedy:male .  
  }"  
  '(?child))           

In both cases, we use register-spin-magic-property to associate a URI (like !ex:uncle) with a SPARQL query string that contains placeholders (like ?child) that will vary when we invoke the property. Once we register the magic properties, we can ask our query again and now we will get results:

(sparql:run-sparql  
  "  
  prefix ex: <http://franz.examples#>  
  prefix kennedy: <http://www.franz.com/simple#>  
  select distinct ?child ?uncle {  
    kennedy:person1 kennedy:has-child ?child .  
    ?uncle ex:uncle ?child .  
  }"  
  :results-format :lists)  
 
(({person3} {person24}) ({person3} {person22}) ({person3} {person20})  
 ({person3} {person33}) ({person3} {person31}) ({person3} {person28})  
 ({person3} {person26}) ({person3} {person25}) ({person3} {person41})  
 ({person3} {person39}) ...)     

Note that to answer this query, AllegroGraph is first finding the children of kennedy:person1. Then for each child, it evaluates the query that looks for the that child's uncle. To do this, it must evaluate the query that find's the child's parent's brothers.

limitations

There are several limitations in this first implementation. To wit:

Note that this initial implementation of SPIN functions and magic properties may fail to operate correctly in all circumstances. This will be corrected in subsequent release. We welcome your bug reports and feedback.


Footnotes

  1. This function has several problems in its own right in that it only works during 2011 and does not take the month of the year into account. It does, however, show how SPIN functions can be used.