Link Search Menu Expand Document
Start for Free

User-defined Rule Reasoning

This page discusses user-defined rule reasoning in Stardog.

Page Contents
  1. Overview
  2. Stardog Rules Syntax
  3. Rule Limitations & Gotchas
  4. Rules Examples

Overview

Stardog supports user-defined rule reasoning in addition to RDFS and OWL reasoning. User-defined rules provide more expressivity compared to RDFS and OWL and allow users to encode complex domain-specific logic. The rules should be stored in the database along with the rest of the schema. Inferences implied by the rules will not be materialized. Instead, rules are used to expand queries, and the results of the expanded query will include the relevant inferences.

To trigger inference rules, simply execute a relevant query.

Stardog Rules Syntax

Stardog Rules Syntax is basically SPARQL “basic graph patterns” (BGPs) plus some very explicit new bits (IF-THEN) to denote the head and body of a rule. Quick refresher: the IF clause defines the conditions to match in the data; if they match, the contents of the THEN clause “fire”; that is, they are inferred and, thus, available for other queries, rules, or axioms, etc.

An example user-define rule looks as follows:

IF {
	?r a :Rectangle ;
	   :width ?w ;
	   :height ?h
    BIND (?w * ?h AS ?area)
}
THEN {
    ?r :area ?area
}

You define URI prefixes in the normal way (examples below) and use regular SPARQL variables for rule variables. As you can see, some SPARQL 1.1 syntactic sugar – property paths, especially, but also bnode syntax – make complex Stardog Rules concise and elegant.

It’s legal to use any valid Stardog function in Stardog Rules (see rule limitations below for a few exceptions).

You include the rules directly in a Turtle file loaded into Stardog. Rules can be mixed with triples in the file. Here’s an example that augments the previous rule with instance data:

:r a :Rectangle ;
   :width 5 ;
   :height 8 .

IF {
	?r a :Rectangle ;
	   :width ?w ;
	   :height ?h
    BIND (?w * ?h AS ?area)
}
THEN {
    ?r :area ?area
}

Inline rules in Turtle data can be named for later reference and management. We assign an IRI, :ExpensiveProductRule in this example, to the rule and use it as the subject of other triples:

@prefix : <http://example.org/> .

RULE :ExpensiveProductRule
IF {
   ?x a :Product ;
      :price ?price
   FILTER (?price > 100)   
}
THEN {
   ?x a :ExpensiveProduct .
}

:ExpensiveProductRule rdfs:comment "This rule defines expensive products" .

In addition to the inline Turtle representation of rules, you can represent the rules with specially constructed RDF triples. This is useful for maintaining Turtle compatibility or for use with SPARQL INSERT DATA queries. This example shows the object of a triple which contains one rule in Stardog Rules syntax embedded as literal.

@prefix rule: <tag:stardog:api:rule:> .

[] a rule:SPARQLRule;
   rule:content """
      IF {
         ?x a :Product ;
            :price ?price
         FILTER (?price > 100)   
      }
      THEN {
         ?x a :ExpensiveProduct .
      }
   """.

Rule Limitations & Gotchas

  1. The RDF serialization of rules in, say, a Turtle file has to use the tag:stardog:api:rule: namespace URI and then whatever prefix mechanism (if any) that’s valid for that serialization. In the examples here, we use Turtle. Hence, we use @prefix, etc.

    However, the namespace URIs used by the literal-embedded rules can be defined in two places: the string that contains the rule – in Rules Examples, you can see the default namespace is urn:test: – or in the Stardog database in which the rules are stored. Either place will work; if there are conflicts, the “closest definition wins”, that is, if foo:Example is defined in both the rule content and in the Stardog database, the definition in the rule content is the one that Stardog will use.

  2. Stardog Rule Syntax has the same expressivity of SWRL, which means the SPARQL features allowed in rules are limited. Specifically, a triple pattern in a rule should be in one of the following forms:

    • term1 rdf:type class-uri
    • term1 prop-uri term2

    where class-uri is a URI referring to a user-defined class and prop-uri is a URI referring to a user-defined property. Built-in URIs such as rdfs:subClassOf or owl:TransitiveProperty are not allowed in rules.

    The only types of property paths allowed in rules are inverse paths (^p), sequence paths (p1 / p2) and alternative paths (p1 | p2), but these paths should not violate the above conditions. For example, the property path rdf:type/rdfs:label is not valid because according to the SPARQL spec, this would mean the object of an rdf:type triple pattern is a variable and not a user-defined class.

    The rule body (IF) and only the rule body may optionally contain UNION, BIND or FILTER clauses. However, functions EXISTS, NOT EXISTS, or NOW() cannot be used in rules. User-defined functions (UDF) may be used in rules, but if the UDF is not a pure function, the results are undefined.

    Other SPARQL features are not allowed in rules.

  3. Having the same predicate both in the rule body (IF) and the rule head (THEN) is supported in a limited way. Cycles are allowed only if the rule body does not contain type triples or filters and the triples in the rule body are linear (i.e., no cycles in the rule body either).

    In other words, a property used in the rule head depends on a property in the rule body, and this dependency graph may contain cycles under some limits. One of these is that a rule body should not contain type triples or filters. Tree-like dependencies are always allowed.

    Of course, the rule body may also contain triple patterns, which constitute a different kind of graph: it should be linear when edge directions are ignored. In other words, no cycles or trees are allowed in this graph pattern. “Linear when directions are ignored” means that { ?x :p ?y . ?x :p ?z } is linear but { ?x :p ?y . ?x :p ?z . ?x :p ?t } is not because there are three edges for the node represented by ?x.

    The reason for these limits boils down to the fact that recursive rules and axioms are rewritten as SPARQL property paths. This is why rule bodies cannot contain anything but property atoms. Cycles are allowed as long as we can express these as a regular grammar. Another way to think about this is that these rules should be as expressive as OWL property chains, and the same restrictions defined for property chains apply here, too.

    Let’s consider some examples. These rules are acceptable since no cycles appear in dependencies.

    IF 
    { ?x :hasFather ?y . ?y :hasBrother ?z } 
    THEN 
    { ?x :hasUncle ?z }
       
    IF 
    { ?x :hasUncle ?y . ?y :hasWife ?z } 
    THEN 
    { ?x :hasAuntInLaw ?z }
    

    These rules are not acceptable since there is a cycle:

    IF 
    { ?x :hasFather ?y . ?y :hasBrother ?z } 
    THEN 
    { ?x :hasUncle ?z } 
       
    IF 
    { ?x :hasChild ?y . ?y :hasUncle ?z } 
    THEN 
    { ?x :hasBrother ?z }
    

    This kind of cycle is allowed:

    IF 
    { ?x :hasChild ?y . ?y :hasSibling ?z } 
    THEN 
    { ?x :hasChild ?z }
    

3 is a general limitation, not one specific to Stardog Rules Syntax. Recursion or cycles can occur through multiple rules, or they may occur as a result of interactions of rules with other axioms (or just through axioms alone).

Rules Examples

PREFIX rule: <tag:stardog:api:rule:>
PREFIX : <urn:test:>
PREFIX gr: <http://purl.org/goodrelations/v1#>

:Product1 gr:hasPriceSpecification [ gr:hasCurrencyValue 100.0 ] .
:Product2 gr:hasPriceSpecification [ gr:hasCurrencyValue 500.0 ] .
:Product3 gr:hasPriceSpecification [ gr:hasCurrencyValue 2000.0 ] .

IF {
   ?offering gr:hasPriceSpecification ?ps .
   ?ps gr:hasCurrencyValue ?price .
   FILTER (?price >= 200.00).
}
THEN {
   ?offering a :ExpensiveProduct .
}

This example is self-contained: it contains some data (the :Product... triples) and a rule. It also demonstrates the use of SPARQL’s FILTER to do numerical (and other) comparisons.

Here’s a more complex example that includes four rules and, again, some data.

PREFIX rule: <tag:stardog:api:rule:>
PREFIX : <urn:test:>

:c a :Circle ;
   :radius 10 .

:t a :Triangle ;
   :base 4 ;
   :height 10 .

:r a :Rectangle ;
   :width 5 ;
   :height 8 .

:s a :Rectangle ;
   :width 10 ;
   :height 10 .

IF {
   ?r a :Rectangle ;
      :width ?w ;
      :height ?h
   BIND (?w * ?h AS ?area)
}
THEN {
    ?r :area ?area
}

IF {
   ?t a :Triangle ;
      :base ?b ;
      :height ?h
   BIND (?b * ?h / 2 AS ?area)
}
THEN {
    ?t :area ?area
}

IF {
     ?c a :Circle ;
        :radius ?r
     BIND (math:pi() * math:pow(?r, 2) AS ?area)
}
THEN {
    ?c :area ?area
}


IF {
     ?r a :Rectangle ;
        :width ?w ;
        :height ?h
     FILTER (?w = ?h)
}
THEN {
    ?r a :Square
}

This example also demonstrates how to use SPARQL’s BIND to introduce intermediate variables and do calculations with or to them.

Let’s look at some other rules to see some other SPARQL features.

This rule says that a person between 13 and 19 (inclusive) years of age is a teenager:

IF {
      ?x a :Person; :hasAge ?age.
      FILTER (?age >= 13 && ?age <= 19)
}
THEN {
      ?x a :Teenager.
}

This rule says that a male person with a sibling who is the parent of a female is an “uncle with a niece”:

IF {
      ?x a :Person; a :Male; :hasSibling ?y;
      ?y :isParentOf ?z;
      ?z a :Female.
}
THEN {
      ?x a :UncleOfNiece.
}

We can use SPARQL 1.1 property paths (and bnodes for unnecessary variables – ones that aren’t used in the THEN block) to render this rule even more concisely:

IF {
      ?x a :Person, :Male; :hasSibling/:isParentOf [a :Female]
}
THEN {
      ?x a :UncleOfNiece.
}

And of course, a person who’s male and has a niece or nephew is an uncle of his niece(s) and nephew(s):

IF {
     ?x a :Male; :isSiblingOf/:isParentOf ?z
}
THEN {
      ?x :isUncleOf ?z.
}

A superuser can read all of the things:

IF {
      ?x a :SuperUser.
      ?y a :Resource.
      ?z a <http://www.w3.org/ns/sparql#UUID>.
}
THEN {
      ?z a :Role.
      ?x :hasRole ?z; :readPermission ?y.
}