Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Rule Execution With SHACL

DZone's Guide to

Rule Execution With SHACL

A quick, but in-depth look into using SHACL for performing rule execution on RDF data, and some example code to get you started!

· Big Data Zone ·
Free Resource

Hortonworks Sandbox for HDP and HDF is your chance to get started on learning, developing, testing and trying out new features. Each download comes preconfigured with interactive tutorials, sample data and developments from the Apache community.

In my previous post, Using Jena and SHACL to validate RDF Data, I looked at how RDF data can be validated using SHACL. A closely related concern to constraints checking is rule execution, for which SHACL also can be used.

A SHACL Rule Example

We will again use an example from the SHACL specification. Assume we have a file rectangles.ttl   that contains the following data:

@prefix ex: <http://example.com/ns#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

ex:InvalidRectangle
  a ex:Rectangle .

ex:NonSquareRectangle
  a ex:Rectangle ;
  ex:height 2 ;
  ex:width 3 .

ex:SquareRectangle
  a ex:Rectangle ;
  ex:height 4 ;
  ex:width 4 .

Assuming we want to infer that when the height and width of a rectangle are equal, the rectangle represents a square, the following SHACL rule specification can be used (which we will store in  rectangleRules.ttl ):

@prefix ex: <http://example.com/ns#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix dash: <http://datashapes.org/dash#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:Rectangle
  a rdfs:Class, sh:NodeShape ;
  rdfs:label "Rectangle" ;
  sh:property [
    sh:path ex:height ;
    sh:datatype xsd:integer ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:name "height" ;
  ] ;
  sh:property [
    sh:path ex:width ;
    sh:datatype xsd:integer ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:name "width" ;
  ] ;
  sh:rule [
    a sh:TripleRule ;
    sh:subject sh:this ;
    sh:predicate rdf:type ;
    sh:object ex:Square ;
    sh:condition ex:Rectangle ;
    sh:condition [
      sh:property [
        sh:path ex:width ;
        sh:equals ex:height ;
      ] ;
    ] ;
  ] .

A Code Example Using Jena

Naturally, you will need to add SHACL to your Maven pom dependencies. Then the following code will execute your SHACL rules:

package org.shacl.tutorial;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.apache.jena.rdf.model.Model;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.riot.RDFFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.topbraid.shacl.rules.RuleUtil;
import org.topbraid.spin.util.JenaUtil;

public class ShaclRuleExecution {
  private static Logger logger = LoggerFactory.getLogger(ShaclValidation.class);
  // Why This Failure marker
  private static final Marker WTF_MARKER = MarkerFactory.getMarker("WTF");


  public static void main(String[] args) {
    try {   
      Path path = Paths.get(".").toAbsolutePath().normalize();
      String data = "file:" + path.toFile().getAbsolutePath() + 
        "/src/main/resources/rectangles.ttl";
      String shape = "file:" + path.toFile().getAbsolutePath() + 
         "/src/main/resources/rectangleRules.ttl";   

      Model dataModel = JenaUtil.createDefaultModel();
      dataModel.read(data);
      Model shapeModel = JenaUtil.createDefaultModel();
      shapeModel.read(shape);
      Model inferenceModel = JenaUtil.createDefaultModel();

      inferenceModel = RuleUtil.executeRules(dataModel, shapeModel, 
        inferenceModel, null);

      String inferences = path.toFile().getAbsolutePath() + 
        "/src/main/resources/inferences.ttl";
      File inferencesFile = new File(inferences);
      inferencesFile.createNewFile();     
      OutputStream reportOutputStream = new FileOutputStream(inferencesFile);

      RDFDataMgr.write(reportOutputStream, inferenceModel, RDFFormat.TTL);        
    } catch (Throwable t) {
      logger.error(WTF_MARKER, t.getMessage(), t);
    }   
  }
}

Running the Code

Running the code will cause an inferences.ttl file to be written out to $Project/src/main/resources/. It contains the following output:

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

<http://example.com/ns#SquareRectangle>
  a  <http://example.com/ns#Square> .

Note that ex:InvalidRectangle has been ignored because it does not adhere to  sh:condition ex:Rectangle, since it does not have ex:height and ex:width properties. Also, ex:NonSquareRectangle is a rectangle, not a square.

Conclusion

In this post, I gave a brief overview of how SHACL can be used to implement rules on RDF data. This code example is available at GitHub.

Hortonworks Community Connection (HCC) is an online collaboration destination for developers, DevOps, customers and partners to get answers to questions, collaborate on technical articles and share code examples from GitHub.  Join the discussion.

Topics:
shacl ,jena ,rdf ,big data ,rule execution

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}