SPIN provides a representation for rules and constraints using SPARQL. The full SPIN implementation allows customers to:
- Encode SPARQL queries in RDF (i.e., as triples).
- Use SPARQL to define rules and constraints.
- Use SPARQL to define new functions that can be used as filters and in binding expressions.
- Store reusable SPARQL queries as templates,
- Use SPARQL to define magic properties that extend query pattern matching semantics beyond simple subgraph pattern matching.
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
- GET /spin/function/uri - returns the query associated with the function
- PUT /spin/function/uri?query?arguments - defines a spin function for URI
- DELETE /spin/function/uri - removes the function
GET /spin/function - returns a list of defined SPIN functions
GET /spin/magicproperty/uri - returns the query associated with the magic property
- PUT /spin/magicproperty/uri?query?arguments - defines a spin magic property and associates it with URI
- DELETE /spin/magicproperty/uri - removes the magic property
- GET /spin/magicproperty - returns a list of defined SPIN magic properties
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
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
Removes the association between uri
and its SPIN function.
This signals a warning if uri
is not currently associated with a SPIN 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.
Find the SPIN function associated with uri
.
Returns the spin-function
structure for uri
or nil
if there is no associations.
SPIN magic properties
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.
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.
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.
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:
there is room for much optimization and caching of query information.
AllegroGraph does not check for mutually recursive and non-terminating rules. I.e., if magic property A uses magic property B and magic propery B uses magic property A, then AllegroGraph may enter a cycle that will run until resources are exhausted.
only object "arguments" are supported. I.e., you can
?x ex:magic (?a ?b ?c) .
but not
(?x ?y) ex:magic ?a .
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
- 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. ↩