SPIN in AllegroGraph | AllegroGraph 9.0.1
Author: Franz Incorporated

Copyright (c) 2005 - 2026 Franz, Incorporated
Last updated 27 May 2026 at 10:34

# SPIN in AllegroGraph

- Documentation Index
  - Release Notes
  - Quick Start
  - SSL/TLS Quick Start
  - Introduction
  - Setup and Configuration
    - Download
    - Server Installation
    - Server Configuration and Control
    - WebView
    - Database Upgrading
    - Managing Users
  - Server Management
    - Performance Tuning
    - Deleting Duplicate Triples
    - Purging Deleted Triples
    - Auditing
    - Managing Users
    - Replication
    - Point-in-Time Recovery
    - Transaction Log Archiving
    - Security Overview
    - Security Implementation
  - Tools
    - agtool
    - Data Loading
    - Data Export
    - Backup and Restore
    - Querying
    - RDFS++
    - Materializer
    - Event Scheduler
    - SHACL
  - Details
    - Triple Indices
    - Freetext Indices
    - Encoded IDs
    - Connection Pooling
    - N-dimensional Geospatial Overview
    - 2-D Geospatial
    - Temporal
    - Social Network
    - JavaScript
    - Datatypes
    - Data Conversion (Rapper)
    - Stored Procedures
    - Triple Attributes
    - Lineparse Format
  - Querying
    - SPARQL documentation summary
    - SPARQL Reference
    - SPIN
    - SPARQL Magic Properties
    - Prolog
  - 3rd-party tools
    - Solr text Indices
    - MongoDB integration
    - TopBraid Composer Plugin
    - Cloudera
  - Client APIs
    - Gruff
    - GraphTalker
    - REST/HTTP interface
    - HTTP reference
    - Javadocs (Sesame and Jena)
    - Python API
    - Lisp Reference
    - JavaScript
  - Tutorials
    - N-dimensional Geospatial
    - SPARQL Tutorial
    - Java Sesame
    - Java Jena
    - Python Sesame
    - Lisp
    - Prolog
    - More...
  - Other
    - Conferences and Seminars
    - Source Code on Github
    - Amazon EC2
    - Copyrights
  - Franz, Inc.

- HTTP API
- Other clients
- Lisp API
  - SPIN Functions
  - SPIN magic properties
- Example SPIN session
  - SPIN functions
  - SPIN Magic Properties
- limitations

Documentation Index

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.

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 of the Java API for SPIN magic properties and SPIN functions.

## Lisp API

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

### SPIN Functions

X 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

X 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.

X 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.

X 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

X 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.

X 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.

X 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.

X 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:

- 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

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. ↩