I recently approached SHACL and I really like it. I have a problem with SHACL rules and I wonder if any of you could help me.
I created this small ontology, a portion of a much bigger ontology for the GDPR that I am working on.
# baseURI: http://w3.org/ns/temp
# prefix: temp
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix temp: <http://w3.org/ns/temp#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://w3.org/ns/temp>
rdf:type owl:Ontology ;
owl:versionInfo "Created with TopBraid Composer" ;
.
temp:Action
rdf:type owl:Class ;
rdfs:subClassOf owl:Thing ;
.
temp:Consent
rdf:type owl:Class ;
rdfs:subClassOf owl:Thing ;
.
temp:DataSubject
rdf:type owl:Class ;
rdfs:subClassOf owl:Thing ;
.
temp:GiveConsent
rdf:type owl:Class ;
rdfs:subClassOf temp:Action ;
.
temp:John
rdf:type temp:DataSubject ;
.
temp:LegalBasis
rdf:type owl:Class ;
rdfs:subClassOf owl:Thing ;
.
temp:PersonalDataProcessing
rdf:type owl:Class ;
rdfs:subClassOf owl:Thing ;
.
temp:c
rdf:type temp:Consent ;
temp:objectOfConsent temp:pdp ;
.
temp:gc
rdf:type temp:GiveConsent ;
temp:hasAgent temp:John ;
temp:hasPatient temp:c ;
.
temp:hasAgent
rdf:type owl:FunctionalProperty ;
rdfs:domain temp:GiveConsent ;
rdfs:range temp:DataSubject ;
.
temp:hasLegalBasis
rdf:type owl:FunctionalProperty ;
rdfs:domain temp:PersonalDataProcessing ;
rdfs:range temp:LegalBasis ;
.
temp:hasPatient
rdf:type owl:FunctionalProperty ;
rdfs:domain temp:GiveConsent ;
rdfs:range temp:Consent ;
.
temp:isLawful
rdf:type owl:DatatypeProperty ;
rdfs:domain temp:PersonalDataProcessing ;
rdfs:range xsd:boolean ;
.
temp:objectOfConsent
rdf:type owl:FunctionalProperty ;
rdfs:domain temp:Consent ;
rdfs:range temp:PersonalDataProcessing ;
.
temp:pdp
rdf:type temp:PersonalDataProcessing ;
.
There are five main classes: PersonalDataProcessing, DataSubject, LegalBasis, Consent, and GiveConsent. And, there are four (functional) object properties:
- hasLegalBasis (domain: PersonalDataProcessing, range: LegalBasis).
- hasAgent (domain: GiveConsent, range: DataSubject)
- hasPatient (domain: GiveConsent, range: Consent)
- objectOfConsent (domain: Consent, range: PersonalDataProcessing)
And there is one datatype (boolean) property called "isLawful" and defined on PersonalDataProcessing: every PersonalDataProcessing can be lawful (isLawful=true) or not (isLawful=false).
I created an individual "gc" within the class GiveConsent. "gc" has an agent "John" (who is a DataSubject) and a patient "c" (which is a Consent). The Consent "c" is connected via the property objectOfConsent to another individual "pdp", which is a PersonalDataProcessing.
Then I have two SHACL rules. One of them has "sh:order 1", so it should be executed after the other one (which has default sh:order equal to 0);
# baseURI: http://w3.org/ns/rules
# imports: http://w3.org/ns/temp
# prefix: rules
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rules: <http://w3.org/ns/rules#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix temp: <http://w3.org/ns/temp#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://w3.org/ns/rules>
rdf:type owl:Ontology ;
owl:imports <http://w3.org/ns/temp> ;
owl:versionInfo "Created with TopBraid Composer" ;
.
rules:givenConsentIsLegalBasis
rdf:type sh:NodeShape ;
sh:rule [
rdf:type sh:TripleRule ;
sh:object [
sh:path temp:hasPatient ;
] ;
sh:predicate temp:hasLegalBasis ;
sh:subject [
sh:path (
temp:hasPatient
temp:objectOfConsent
) ;
] ;
] ;
sh:targetClass temp:GiveConsent ;
.
rules:legalBasisEntailLawful
rdf:type sh:NodeShape ;
sh:rule [
rdf:type sh:TripleRule ;
sh:order 1 ;
sh:condition [
sh:property [
sh:path temp:hasLegalBasis ;
sh:minCount 1 ;
] ;
] ;
sh:object "true"^^xsd:boolean ;
sh:predicate temp:isLawful ;
sh:subject sh:this ;
] ;
sh:targetClass temp:PersonalDataProcessing ;
.
The first rule above states that if someone has given consent to a PersonalDataProcessing, that consent is a legal basis for the PersonalDataProcessing. The second rule (with "sh:order 1 ;") states that every PersonalDataProcessing that has a legal basis is lawful.
Finally I wrote a Java file to execute the rules:
//Load the ontology
Model ontology = JenaUtil.createMemoryModel();
FileInputStream fisOntology = new FileInputStream("./ontology.ttl");
ontology.read(fisOntology, "urn:dummy", FileUtils.langTurtle);
//Load the rules
Model rules = JenaUtil.createMemoryModel();
FileInputStream fisRules = new FileInputStream("./rules.ttl");
rules.read(fisRules, "urn:dummy", FileUtils.langTurtle);
//Executing the rules
Model inferredModel = RuleUtil.executeRules(ontology, rules, null, null);
//Print
System.out.println(ModelPrinter.get().print(inferredModel));
I am writing you because the first rule correctly creates the triple "pdp hasLegalBasis c" via the Java code above:
<http://w3.org/ns/temp#pdp>
<http://w3.org/ns/temp#hasLegalBasis>
<http://w3.org/ns/temp#c> ;
However, the second rule do NOT trigger after this triple is inferred: isLawful is NOT set to true.
On the other hand, if I manually add the triple "pdp hasLegalBasis c" in the ontology, both rules triggers:
<http://w3.org/ns/temp#pdp>
<http://w3.org/ns/temp#hasLegalBasis>
<http://w3.org/ns/temp#c> ;
<http://w3.org/ns/temp#isLawful>
true .
What am I doing wrong? Can any of you help me?
Thank you very much
FWIW your example does work when executed from TopBraid Composer, which does multiple iterations automatically. So I suspect it's about the order of rules. sh:order is only used for the rules within the same shape, but will not inform the "outer" loop across shapes. As a result, the sh:order values in your example have no effect.
As a general alternative, try calling the rule engine twice with the inferences from the first iteration serving as input to the second round. To do that, you need to construct the inferences Model outside of the calls to RuleUtil similar to what RuleUtil does if you leave inferencesModel null. See the source code of the RuleUtil class.