SPIN - Modeling Vocabulary

W3C Member Submission 22 February 2011, updated 18 June, 2013

W3C Member Submission:
http://www.w3.org/Submission/2011/SUBM-spin-modeling-20110222/
This version (almost identical to W3C Member Submission, but with generalized handling of sp:text and added spin:Columns):
http://spinrdf.org/spin.html
Authors:
Holger Knublauch <holger@topquadrant.com>

This document is available under the W3C Document License. See the W3C Intellectual Rights Notice and Legal Disclaimers for additional information.


Abstract

The SPIN Modeling Vocabulary is a light-weight collection of RDF properties and classes to support the use of SPARQL to specify rules and logical constraints. Based on an RDF representation of SPARQL queries, SPIN defines three class description properties: spin:constraint can be used to define conditions that all members of a class must fulfill. spin:rule can be used to specify inference rules using SPARQL CONSTRUCTs and DELETE/INSERTs. spin:constructor can be used to initialize new instances with default values. In addition to these class description properties, SPIN provides a powerful meta-modeling capability that can be used to build your own modeling language and SPARQL extensions. These meta-modeling features provide the ability to encapsulate reusable SPARQL queries into templates, and to derive new SPARQL functions as well as magic properties from other SPARQL queries and functions.

Status of This Document

SPIN has originally evolved as a specification developed by TopQuadrant. As of February 2011, SPIN has been published by W3C as a member submission. TopQuadrant may continue to improve SPIN in the future and make changes that will be published here. As of June 2013, this version is almost identical to the official W3C submission, except that we have lifted the requirement of using the SPIN RDF syntax for the definition of queries and update commands if (and only if) sp:text triples are present. Accordingly, we have changed the RDF source code of some examples to make them easier to read. Another change in June 2013 (with TopBraid 4.3) was the addition of the concept of (optional) column metadata attached to SPIN templates.


Table of Contents

1 Introduction

SPARQL is now firmly established as the standard query language for RDF models and Semantic Web data. Many RDF APIs and databases come with SPARQL support out-of-the-box. At the same time, SPARQL can be regarded as more than just a query language with a SELECT keyword: SPARQL also provides means to check whether certain conditions currently hold in an RDF model (ASK), to derive new triples from existing triples (CONSTRUCT), and to perform DELETE or INSERT operations. While these powerful elements are increasingly used by developers to solve application and modeling problems, SPIN defines a systematic framework on how to use SPARQL queries to drive applications. SPIN takes SPARQL a step further and uses it to enhance existing RDF models with formal, executable descriptions.

Based on the SPIN SPARQL Syntax, the SPIN Modeling Vocabulary (described in this document) defines a light- weight set of RDFS classes and properties that can be used to systematically embed SPARQL queries into RDF models so that they can be executed with well-defined semantics. The basic idea is to use specific RDF properties to link classes with SPARQL queries so that those SPARQL queries can be executed with a given context.

The following chapters describe how this approach can be used to describe classes (and their instances, Chapter 2), and how to build more complex modeling constructs out of reusable SPARQL queries (Chapter 3). Chapter 4 describes guidelines on how the SPIN vocabulary should be used in the context of the Semantic Web.

Note that the example RDF snippets in this document mostly use the human-readable notation of SPIN RDF, using the sp:text property. SPIN editing tools such as TopBraid typically generate SPIN RDF triples that are better suited for machines to handle because they contain proper references to URI resources, are independent of prefix declarations etc.

 

2 Class Description Vocabulary

The SPIN class description vocabulary defines several RDF properties that can be used to attach SPARQL queries to classes. The property spin:query is the common base property of spin:constraint, spin:rule and spin:constructor. SPIN-compliant tools can use these properties to execute SPARQL queries over the instances of the associated class(es) in response to events. For example, spin:rule can be used by SPIN reasoning engines to construct inferred RDF triples from the currently asserted information in the model.

The SPARQL queries referenced by the SPIN properties are interpreted in the context of the associated class. At run-time, the SPARQL variable ?this is (by default) pre-bound with instances of the class and its sub-classes. Typically, the query itself does not need to bind ?this to any value in the WHERE clause. The execution context (e.g., inference engine) will do this before the query is executed. However, if the query object has the value true for the property spin:thisUnbound then the system will not do this pre-binding. See separate section on details about spin:thisUnbound.

SPIN takes an object-oriented world view on Semantic Web models, in which SPARQL queries play a similar role to functions and methods. Inheritance (expressed using rdfs:subClassOf) is treated in the sense that any query defined for superclasses will also be applied to subclasses. In other words, SPIN class descriptors can only "narrow down" and further restrict what has been defined further up in the class hierarchy. In this spirit, global class descriptions are those that are attached to the root class rdfs:Resource or its OWL equivalent owl:Thing. Those global queries may not even mention ?this at all.

The following sections provide details on the three SPIN class description properties.

2.1 Constraints

The property spin:constraint can be used to link an rdfs:Class with SPARQL ASK or CONSTRUCT queries. Each ASK query defines a constraint that is assumed to evaluate to false for each instance of the associated class (and its subclasses). In other words, if an ASK constraint evaluates to true for one instance, then the instance violates the condition. Optionally, CONSTRUCT queries can create instances of a spin:ConstraintViolation class that provide details on a specific violation. At query execution time, the SPARQL variable ?this is bound to the current instance of the class.

Interactive tools such as editing forms may use the constraints to validate user input. Web services may check parameter ranges. Since SPIN queries are directly attached to class definitions in a machine-readable format, the intended "meaning" and scope of those classes can be clearly communicated to other agents.

2.1.1 Constraint checks using ASK

The following example defines a constraint on the example class ex:Parent so that each instance of parent must be at least 18 years of ex:age.

    ex:Parent
      a       rdfs:Class ;
      rdfs:label "Parent"^^xsd:string ;
      rdfs:subClassOf ex:Person ;
      spin:constraint
              [ a       sp:Ask ;
                sp:text """
                    # must be at least 18 years old
                    ASK WHERE {
                        ?this ex:age ?age .
                        FILTER (?age < 18) .
                    }"""
              ] .

2.1.2 Constraint checks using CONSTRUCT

The following example uses a SPARQL CONSTRUCT query, which is more complex than the ASK style but much more flexible. The CONSTRUCT is assumed to create instances of the system class spin:ConstraintViolation. These instances are usually blank nodes, and additional properties can be attached to violation instances so that editing tools can guide user input:

	spin:constraint
          [ a       sp:Construct ;
            sp:text """
                CONSTRUCT {
                    _:violation a spin:ConstraintViolation ;
                         spin:violationRoot ?this ;
                         spin:violationPath kennedys:spouse ;
                         rdfs:label "Same-sex marriage not permitted (in this model)"
                }
                WHERE {
                    ?this kennedys:spouse ?spouse .
                    ?this kennedys:gender ?gender .
                    ?spouse kennedys:gender ?spouseGender .
                    FILTER (?gender = ?spouseGender) .
                }"""
          ] .

SPIN constraints can be used for various purposes, but in particular to verify that an instance of a class maintains its expected state with regards to its property values and relationships with other resources. For example, simple SPIN constraints may only verify the range of some datatype literals, but other, more complex, constraints may walk adjacent nodes as well, starting a graph pattern matching at the context variable ?this. Anything that can be expressed in SPARQL can be used in SPIN constraints, including queries against other SPARQL end points, FILTER clauses and complex mathematical calculations.

Please note that the values of the spin:constraint property may also be SPIN template calls, assuming these templates wrap CONSTRUCT or ASK queries. Basically, SPIN allows you to use templates whenever a query is expected, as long as the query type matches the expectations.

2.1.3 Constraint violations that suggest fixes

Constraint checks of the CONSTRUCT form may also create extra information that can guide the user to fix the reported constraint violation. The spin:ConstraintViolation can have one or more values for spin:fix, pointing to a call (instance) of a spin:UpdateTemplate. The template call will typically have additional arguments that can also be constructed as part of the constraint check's CONSTRUCT clause.

The following example query is attached to a Person class using spin:constraint. If the Person (?this) has some spouse and is less than 18 years of age, then a violation will be reported. The spin:ConstraintViolation also points to a SPIN template of type :DeleteTriple, constructed as an anonymous node with the identifier _:fix.

    CONSTRUCT {
        _:violation a spin:ConstraintViolation ;
                rdfs:label "Underage marriage not allowed" ;
                spin:violationRoot ?this ;
                spin:violationPath :spouse ;
                spin:fix _:fix .
        _:fix a :DeleteTriple ;
                rdf:subject ?this ;
                rdf:predicate :spouse ;
                rdf:object ?spouse .
    }
    WHERE {
        ?this :spouse ?spouse .
        ?this :age ?age .
        FILTER (?age < 18) .
    }

Without going into details about the :DeleteTriple template, it is a spin:UpdateTemplate that wraps a SPARQL DELETE command on the specified triple. The template takes three arguments, rdf:subject, rdf:predicate and rdf:object, which are filled by the CONSTRUCT above using the current variable bindings. The template would have a human-readable label so that user interfaces can propose a fix from a context menu.

2.2 Rules

The property spin:rule can be used to link an rdfs:Class with SPARQL CONSTRUCT queries or DELETE/INSERT update operations. Each query or operation defines an inference rule that is applied to all instances of the associated class and its subclasses.

2.2.1 Inference Rules using CONSTRUCT

If the property spin:rule points to a CONSTRUCT query, then this defines an inference rule that defines how additional triples can be inferred from what is stated in the WHERE clause. For each binding of the pattern in the WHERE clause of the rule, the triple templates from the CONSTRUCT clause are instantiated and added as inferred triples to the underlying model. At query execution time, the SPARQL variable ?this is bound to the current instance of the class.

The following example defines a rule that infers the values of the ex:grandParent property from values of ex:child.

    ex:Person
      a       rdfs:Class ;
      rdfs:label "Person"^^xsd:string ;
      rdfs:subClassOf owl:Thing ;
      spin:rule
              [ a       sp:Construct ;
                sp:text """
                    CONSTRUCT {
                        ?this ex:grandParent ?grandParent .
                    }
                    WHERE {
                        ?parent ex:child ?this .
                        ?grandParent ex:child ?parent .
                    }"""
              ] .

The property spin:rule has the type spin:RuleProperty. Any sub-property of spin:rule can be used in addition to spin:rule to store rules.

The newly inferred triples of a CONSTRUCT rule will go into a special inferences graph. This graph is part of the default query graph that the WHERE clause will be evaluated on, so that inferences from the previous step will be visible to the "next" rule.

2.2.2 Update Rules using DELETE/INSERT

If the property spin:rule points to a SPARQL UPDATE operation, then this defines an update rule that will lead to assertions, not inferences. This is potentially more powerful than CONSTRUCT rules, because UPDATE rules may include a DELETE clause (DELETE WHERE is also supported), and furthermore it becomes possible to specify the target graphs that shall be modified. Another advantage of using UPDATE rules is that no specific inferences graph may be needed, and thus rule execution may be faster.

However, note that UPDATEs with DELETEs may more easily lead to infinite loops, because rules may undo the results of previous steps. Also note that changes will be made to the actual models, so that the result of rules may be accidentally saved while editing and evaluating the rules.

The following example defines a rule that infers the values of the ex:grandParent property from values of ex:child, and inserts the resulting triples into a specified named graph.

    WITH <http://example.org/people/relationships>
    INSERT {
    	?this ex:grandParent ?grandParent .
    }
    WHERE {
    	?parent ex:child ?this .
    	?grandParent ex:child ?parent .
    }

2.2.3 Controlling the Execution Order of Rules

By default all rules may be executed in a random order. However, the SPIN vocabulary provides some properties that can be used to guide the execution engine. Designers can use these properties to attach metadata to the rules to help the engine skip certain rules. This often makes it possible to just do a single iteration over all rules.

On the most granular level, it is possible to group rules into categories using sub-properties of spin:rule. The property spin:nextRuleProperty can be used to link two such properties together, so that rules from one group will be executed before others. For example, if you want to run all ex:importRules before all spin:rules then add the triple ex:importRule spin:nextRuleProperty spin:rule and use ex:importRule to attach the high priority rules to your class.

The rdfs:comment of a rule should be used as the final level of ordering. This allows developers to insert comments such as "# Step 1: ...", "# Step 2: ..." etc as first lines of their rules. User interfaces can use the same ordering and (like TopBraid) may do so by default.

The property spin:rulePropertyMaxIterationCount can be attached to any sub-property of spin:rule to instruct the engine to execute certain rules a certain number of times. In particular this makes it possible to make sure that rules that create blank nodes are only executed once, avoiding problems with infinite loops.

2.2.4 Organizing Rules in Libraries

In some cases, rules may be imported from a library. Such library files may only declare the rules themselves but have no additional triples in them that would be relevant for the rule execution. The base URI (owl:Ontology) of such files can be annotated to be an instance of spin:LibraryOntology. In this case, all triples from that named graph should be ignored by SPIN rule engines.

2.2.5 Controlling the binding of ?this

In the default setting, a SPIN rules or constraint checking engine will pre-bind the variable ?this with all instances of the associated class. A naive implementation of this is to add a triple match such as ?this rdf:type ex:Class to the WHERE clause. This check will make sure that the system only iterates over values of that class. However, this additional check can also be a significant amount of work for the engine, and may lead to many unnecessary matches. Furthermore, in many cases this pre-binding is not really necessary because the remaining conditions in the WHERE clause may be sufficiently strong to bind ?this with all expected instances.

In those cases, the property spin:thisUnbound should be set to "true"^^xsd:boolean to instruct the engine to bypass the explicit binding of ?this. The spin:thisUnbound property has been introduced with SPIN 1.1. As a recommendation, display engines should render the variable ?this in bold face to indicate that it has a special meaning, unless spin:thisUnbound is set.

2.3 Constructors

The property spin:constructor can be used to link an rdfs:Class with SPARQL CONSTRUCT queries or DELETE/INSERT update operations. Each Construct query defines an inference rule that can be applied to all new instances of the associated class and its subclasses at creation-time. Constructors are usually only evaluated once, to set initial default values to some properties of the instances. At query execution time, the SPARQL variable ?this is bound to the current instance of the class.

For example, if a user creates a new instance of ex:USCitizen then its constructor can be used to automatically set its ex:birthCountry property to ex:USA. However, this value may of course be replaced later, during the evolution of the model.

    ex:USCitizen
      a       rdfs:Class ;
      rdfs:label "US citizen"^^xsd:string ;
      rdfs:subClassOf ex:Person ;
      spin:constructor
              [ a       sp:Construct ;
                sp:text """
                    CONSTRUCT {
                        ?this ex:birthCountry ex:USA .
                    }
                    WHERE {
                    }"""
              ] .

At execution time, the only triple that will be reliably present for ?this is the rdf:type triple that links ?this with the class that has the constructor attached to it, or a subclass thereof.

SPIN-compliant engines need to make sure that superclass constructors are executed before subclass constructors. As a consequence, constructors of subclasses may assume that additional triples are present, and may query them in their WHERE clauses.

Constructors should also be executed by SPIN inference engines: If a rule creates a new rdf:type triple, then the corresponding constructors should be called on the subject of the type triple. The constructors should be executed after the rule has been executed completely - this will make sure that all triples mentioned in the rule's construct template will have been created and can be queried by the WHERE clause of the constructors.

 

3 Meta-Modeling Vocabulary

SPARQL queries are often similar and only differ in a value or two. SPIN can be used to generalize SPARQL queries so that they can be reused in multiple contexts. The SPIN Meta-Modeling Vocabulary described in the following sections introduces three mechanisms of encapsulating SPARQL query templates: Templates, Functions and Magic Properties.

3.1 Templates

Templates are reusable "boxed" SPARQL queries that can be used in conjunction with properties such as spin:rule or spin:constraint, but also in other places.

The following example defines a template ex:MinCardinality that can be used to verify that ?this has at least ?count values for a given property ?predicate. Note that the function ex:cardinality is a user-defined SPIN function that will be described later.

    ex:MinCardinality
      a       spin:AskTemplate ;
      rdfs:subClassOf 	spin:AskTemplates ;
      rdfs:comment "Checks whether ?this has at least ?count values for a given property ?predicate." ;
      rdfs:label "Min cardinality"^^xsd:string ;
      spin:constraint
              [ a       spl:Argument ;
                rdfs:comment "the minimum number of values expected" ;
                spl:predicate ex:count ;
                spl:valueType xsd:integer
              ] ;
      spin:constraint
              [ a       spl:Argument ;
                rdfs:comment "the property being restricted" ;
                spl:predicate ex:predicate ;
                spl:valueType rdf:Property
              ] ;
      spin:labelTemplate "at least {?count} values for {?predicate}"^^xsd:string ;
      spin:body
              [ a       sp:Ask ;
                sp:text """
                    ASK WHERE {
                        FILTER (ex:cardinality(?predicate) < ?count) .
                    }"""
              ] .

Templates are instances of subclasses of the metaclass spin:Template: spin:AskTemplate, spin:ConstructTemplate, spin:SelectTemplate and spin:UpdateTemplate, depending on the type of query that they wrap. These metaclasses should not be confused with the similarly sounding classes such as spin:AskTemplates, which are only meant to serve as a way of organizing existing templates in categories, e.g. so that they show up nicely in class trees.

In order to "call" a template, the user needs to instantiate the template class. In the following example, the above template ex:MinCardinality is used to constrain that all instances of the class ex:Parent must have at least one value for the ex:child property.

    ex:Parent
      a       owl:Class ;
      rdfs:label "Parent"^^xsd:string ;
      rdfs:subClassOf owl:Thing ;
      spin:constraint
              [ a ex:MinCardinality ;
                ex:count 1 ;
                ex:predicate ex:child
              ] .

Each argument of a template must be represented by one argument descriptor (instance of the template spl:Argument, where the namespace for the prefix spl is http://spinrdf.org/spl#). Each argument descriptor must point to an RDF property using spl:predicate. This property will hold the actual template values when the template is instantiated. The local name of that property is used as variable name for the argument. For example, the property sp:arg1 represents the variable ?arg1. Arguments can also have a value type using spl:valueType and might be spl:optional. Furthermore, arguments can define a default value using spl:defaultValue. The default value shall be used for an argument if no value has been specified explicitly in the template call. It is strongly recommended to define an rdfs:comment for each argument, to describe the role of the argument in the query.

The encapsulated query itself must be stored using spin:body. Depending on the type of template, spin:body must be an instance of sp:Ask, sp:Construct, sp:Describe or sp:Select, sp:Modify or sp:DeleteWhere.

At execution time, the query will already have the variables declared in the argument descriptors pre-assigned with the arguments in the template call.

Templates should define a value for spin:labelTemplate with place-holders for the variable names between { and } so that user interfaces can render the template calls in a human-readable way. For example, the template call above would be rendered as

    at least 1 values for ex:child

Templates can be published on the Semantic Web and then imported by other models. SPIN agents could in principle dynamically resolve templates from the internet, similar to distributed Web Services. Such template libraries can specify domain-specific modeling languages (as alternatives to, or in addition to languages like OWL). Since templates are executable, these template-based modeling languages have well-defined formal semantics.

See also: Understanding SPIN Templates

3.1.1 SPIN Column Metadata

As of SPIN 1.3.0 (published with TopBraid 4.3 in June 2013), the SPIN Modeling Vocabulary has been extended with classes and properties that can be used to attach metadata to tabular data, especially those tables produced by SELECT templates. The class spin:TableDataProvider has been introduced to serve as one superclass of spin:SelectTemplate. Each spin:TableDataProvider (and thus SELECT template) may point to one or more instances of spin:Column via the property spin:column. The instances of spin:Column are often blank nodes. Each spin:Column may have the following properties (all except spin:columnIndex are optional):

Property Range Description
rdfs:label xsd:string A display label for the head of the table.
spin:columnIndex xsd:integer The index of the column from left to right, starting with 0.
spin:columnType rdfs:Class The (data) type of the column. For example, xsd:float columns should be right-aligned.
spin:columnWidth xsd:integer The width of the column in pixels.

The following example (in Turtle notation) defines a SPIN template for a SELECT query that returns the name and birth date of each child of a given parent.

<http://topbraid.org/schema.ui#ChildrenOverview>
      a       spin:SelectTemplate ;
      rdfs:comment "A (demo) SELECT template displaying some information about the children of a given schema:Person."^^xsd:string ;
      rdfs:label "Children overview"^^xsd:string ;
      rdfs:subClassOf spin:SelectTemplates ;
      spin:constraint
              [ a       spl:Argument ;
                rdfs:comment "The schema:Person to get the children of."^^xsd:string ;
                spl:predicate arg:parent ;
                spl:valueType schema:Person
              ] ;
      spin:body
              [ a       sp:Select ;
                sp:text """
                    SELECT ?childName ?birthDate
                    WHERE {
                        ?child schema:parent ?parent .
                        ?child rdfs:label ?childName .
                        OPTIONAL { 
                            ?child schema:birthDate ?birthDate .
                        } .
                    }
                    """
              ] ;
      spin:column
              [ a       spin:Column ;
                rdfs:label "Name of Child"^^xsd:string ;
                spin:columnIndex 0 ;
                spin:columnType xsd:string ;
                spin:columnWidth 200
              ] ;
      spin:column
              [ a       spin:Column ;
                rdfs:label "Birth date"^^xsd:string ;
                spin:columnIndex 1 ;
                spin:columnType xsd:date ;
                spin:columnWidth 100
              ] ;
      spin:labelTemplate "Overview of children of {?parent}"^^xsd:string .

A possible rendering of such a table could look like the following (here, using swa:ResultSetGrid from SPARQL Web Pages).

 

3.2 Functions

SPIN can be used to define new SPARQL functions so that these new function can be used in expressions such as FILTER or BIND clauses. Technically, SPIN Functions are very similar to SPIN Templates. Functions are defined by a body and zero or more argument descriptors. The body of a function must be an Ask query, or a Select query with exactly one result variable.

The following example declares a new function ex:cardinality that gets the number of values of a given property at the current subject (?this), using a SPARQL COUNT query.

    ex:cardinality
      a       spin:Function ;
      rdfs:subClassOf 	spin:Functions ;
      rdfs:comment "Gets the number of values of a given property at the current subject (?this)." ;
      rdfs:label "cardinality"^^xsd:string ;
      spin:constraint
              [ a       spl:Argument ;
                rdfs:comment "The property to get the cardinality of." ;
                spl:predicate sp:arg1
              ] ;
      spin:body
              [ a       sp:Select ;
                sp:text """
                    SELECT (COUNT(?object) AS ?result)
                    WHERE {
                        ?this ?arg1 ?object .
                    }"""
              ] .

Once the function is defined, it can be used in SPARQL queries such as

    FILTER (ex:cardinality(ex:child) > 0)

The expression above is represented in SPIN syntax as

    [ a       sp:Filter ;
      sp:expression
                 [ sp:arg1 [ sp:arg1 :child ;
                             a ex:cardinality
                           ] ;
                   sp:arg2 0 ;
                   a sp:gt
                 ]
    ]

Arguments are declared the same way as with Templates (see above). The main difference is that a function's arguments must be ordered so that the traditional textual SPARQL notation can correctly round- trip them. By default, a Function's arguments are sorted alphabetically by their local name. Functions often use the system properties sp:arg1, sp:arg2 etc to declare the arguments in their expected order, but any other property can be used as well. Function calls will use the declared default values for an argument if no other values have been explicitly specified.

Functions do not need to have a spin:body. In those cases, the execution environment is assumed to provide different implementations of them, e.g. as hard-coded Java functions or using JavaScript (see SPINx).

Function (and template) classes may be marked to be abstract by setting the property spin:abstract to true. This indicates to the environment that the function shall not be instantiated directly, but only serves as a way to organize and group other functions in a hierarchy.

Functions can be marked to be private by setting the property spin:private to true. This tells the environment that the function is only supposed to be used as a "helper" of other functions, but not elsewhere.

See also: Understanding SPIN Functions

3.2.1 The built-in Function spin:eval

SPIN compliant SPARQL engines must provide a built-in SPARQL function spin:eval that can be used to evaluate a SPIN expression or query at execution time. This makes it possible to define higher level functions, that take other function calls and queries as arguments. For example, it is possible to place a SPIN expression as an argument into a template. The body of the template can then reference the expression (as a pre-bound variable) and evaluate it.

spin:eval takes any odd number of arguments. The first argument must be a reference to a SPIN expression (e.g., instance of a SPARQL function from the SP namespace) or a sp:Select or a sp:Ask. The other arguments must come in pairs, so that the first one is a property and the second is a value that shall be pre-bound in the evaluation of the expression.

In the following example, an expression (bound to a variable ?expression) is executed, with the variable ?arg3 bound to the literal "value". The result of the function will be assigned to ?result.

    BIND (spin:eval(?expression, sp:arg3, "value") AS ?result)

If the expression argument is a sp:Select, then the result will be the first binding of the first result variable. If the expression argument is a sp:Ask, then a typed boolean literal will be returned.

3.3 Magic Properties

Magic Properties are supported by many SPARQL engines (such as Jena ARQ) to dynamically compute values at query time. Typically, a magic property is backed by a calculation function that determines bindings of the variables on the left or right side of the predicate.

SPIN enables users to define such magic properties. The magic properties are declared in a very similar way as SPIN Functions, but provide greater flexibility. In contrast to LET/FILTER functions, magic properties can return multiple values. Furthermore, any input or output variable may be unbound, and it is the task of the magic property to find all potential bindings of those variables.

The metaclass spin:MagicProperty is used to represent magic properties. As shown below, spin:MagicProperty is a subclass of spin:Function and rdf:Property, making it possible to use them either as functions or as predicates in a triple match.

SPIN metaclass diagram

Let's look at an example magic property grandParent which defines the grandparent relationship between persons. This is defined as a spin:MagicProperty with one argument ?arg1 of type kennedys:Person. The spin:body of the magic property is as follows:

    SELECT ?grandParent
    WHERE {
        ?parent kennedys:child ?arg1 .
        ?grandParent kennedys:child ?parent .
    }

The complete SPIN RDF definition of this magic property is shown below.


    :grandParent
      a       spin:MagicProperty ;
      rdfs:label "grand parent"^^xsd:string ;
      rdfs:subClassOf spin:MagicProperties ;
      spin:constraint
              [ a       spl:Argument ;
                rdfs:comment "The Person to get the grandparent(s) of."^^xsd:string ;
                spl:predicate sp:arg1 ;
                spl:valueType kennedys:Person
              ] ;
      spin:body
              [ a       sp:Select ;
                sp:text """
                    SELECT ?grandParent
                    WHERE {
                        ?parent kennedys:child ?arg1 .
                        ?grandParent kennedys:child ?parent .
                    }"""
              ] .

An example use of this function is

    SELECT *
    WHERE {
        kennedys:JohnKennedyJr :grandParent ?grandParent .
    }

Which returns bindings of ?grandParent with kennedys:JosephKennedy and kennedys:RoseFitzgerald. At execution time, the predicate :grandParent is treated like a function call based on the SPARQL query defined as body of the magic property. The value on the left side of the magic property will be passed into the function as ?arg1. The result bindings of the variables in the SELECT clause will be bound to the variables on the right hand side of the magic property. The names of the input and output variables are irrelevant, just the order of occurrence counts.

Note that a property like :grandParent can also be used as a regular property. It is left to the implementation whether it will create regular triples or do the computation dynamically.

Magic properties can also take multiple arguments and result values using a (rather obscure) list syntax - these cases are technically supported but complex to represent in the SPIN RDF syntax. If possible, it is strongly recommended to define magic properties with just one argument and one result variable. This provides greater flexibility to the implementations, and simplifies the syntax and implementation burden.

With this background, let's look at another use case of the same function from above:

    SELECT *
    WHERE {
        ?grandChild :grandParent kennedys:RoseFitzgerald .
    }

The query above binds ?grandChild with all grand children of kennedys:RoseFitzgerald (a cast of dozens, including kennedys:JohnKennedyJr). It is also possible to leave the object blank, to get all grandParent relationships in the whole model.

At execution time, the engine should add the arguments to the result variables of the body (here: change the signature to SELECT ?grandParent ?arg1). Then pre-bind all variables that have been supplied with values and execute the SELECT query. Use the result bindings to fill in values for the the unbound variables. If a magic property is used like a triple match, then the result must include all existing values in the query graph, in addition to the bindings derived from the function.

Since spin:MagicProperty is a subclass of spin:Function, any magic property can also be used as a regular FILTER or LET function. In this case, only the first result variable of the first result row will be returned.

 

Note that SPIN functions and magic properties can be used to implement behavior known as backward chaining in rule languages. Functions can call each other, even recursively, and the SPARQL engine will iterate over multiple possible solution spaces and do back tracking if it does not find bindings for the original query.

 

4 SPIN Libraries

SPIN is a modeling language that has been designed with the Semantic Web in mind. This chapter describes how SPIN documents should be organized to make the best use of this infrastructure.

4.1 SPIN and the Semantic Web

SPIN constraints, rules and constructors are associated with RDFS/OWL classes using dedicated properties such as spin:constraint. On the Semantic Web this means that when an RDFS class is shared online, then users of the class will also get the associated semantics in the same way as they get labels, comments etc. So typically, class definitions and semantics are stored in the same document.

With the SPIN meta-modeling vocabulary the situation is slightly different, because SPIN functions and templates can also be used in other places. However, SPIN functions and templates have a unique URI and therefore can be uploaded to some Semantic Web site so that SPIN-aware agents can find an executable description of the function or template by following its URI. In practice this means that SPIN users planning to share their files should make sure that the selected URIs are de-referencable. This simply means that if a software agent encounters a SPIN function in a SPARQL query, and the function is not natively known to the engine, then the engine can follow the link to the function to get all triples needed to understand how to execute it, including spin:body.

If SPIN files are not shared on the Semantic Web but only used locally, then it is a good practice to help the environment find function and template definitions. We recommend a naming convention to store such SPIN files with an ending such as .spin.ttl or .spin.rdf. This makes it easier for tools to recognize that the file may contain SPIN functions and templates so that the tool can pre-load the functions into memory in advance.

4.2 SPIN Imports

Some constraint or rule libraries are independent from a particular domain ontology, and rather describe general patterns. For example, it is possible to describe the semantics of a subset of OWL using SPIN constraints, and thus re-use the OWL restrictions vocabulary to validate integrity constraints on existing OWL models. Such libraries may contain spin:constraints that are simply attached to owl:Thing or another global root class.

In order to use such libraries in the context of OWL, one option is to define an owl:imports to link the domain ontology with the constraint library. However, this means that OWL semantics would be applied to the library itself, because OWL engines would merge in all the triples from the SPIN file and treat them on the same level as the actual domain triples. Furthermore, the additional triples might clutter up user interfaces.

SPIN includes the spin:imports property, that can be used to link the base URI of a domain ontology with a SPIN file, specified by the base URI of the SPIN file. For a SPIN constraint checker (or rule engine), the spin:imports keyword has the same meaning as owl:imports, i.e. all triples from the imported file will be added to the current RDF graph. However, the triples specified by spin:imports will not be imported in an OWL sense and therefore remain invisible to any OWL tool.

In the following example, the current domain ontology (spinOWLTest) imports a SPIN constraint library that defines a subset of OWL for constraint checking, so that SPIN engines will report a constraint violation whenever an instance of :Person does not have 1 :lastName. Note that this file does not import the SPIN namespace - it just declares a prefix for it.

    @prefix :        <http://tests.spin.topbraid.org/imports/spinOWLTest#> .
    @prefix owl:     <http://www.w3.org/2002/07/owl#> .
    @prefix spin:    <http://spinrdf.org/spin#> .
    @prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
    @prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
    @prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .

    <http://tests.spin.topbraid.org/imports/spinOWLTest>
      rdf:type owl:Ontology ;
      spin:imports <http://topbraid.org/spin/spinowl> .

    :Person
      rdf:type owl:Class ;
      rdfs:label "Person"^^xsd:string ;
      rdfs:subClassOf owl:Thing ;
      rdfs:subClassOf
              [ rdf:type owl:Restriction ;
                owl:cardinality "1"^^xsd:int ;
                owl:onProperty :lastName
              ] ;...

 

Appendix: Reference

The URL of the SPIN schema is http://spinrdf.org/spin The URL of the SPIN Standard Module library (SPL) is http://spinrdf.org/spl