Reputation: 287
In the course of trying to selectively invoke shapes/rules when classes are asserted, I'm working with the following example shapes definition (in TTL):
# baseURI: http://example.org/familyShapes
# imports: http://datashapes.org/dash
# prefix: familyShapes
@prefix dash: <http://datashapes.org/dash#> .
@prefix familyShapes: <http://example.org/familyShapes#> .
@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 sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<http://example.org/familyShapes>
rdf:type owl:Ontology ;
owl:imports <http://datashapes.org/dash> ;
.
familyShapes:FemaleShape
rdf:type sh:NodeShape ;
rdfs:label "Female shape" ;
sh:property [
sh:path familyShapes:gender ;
sh:hasValue familyShapes:female ;
] ;
.
familyShapes:Gender
rdf:type rdfs:Class ;
rdfs:label "Gender" ;
rdfs:subClassOf rdfs:Resource ;
.
familyShapes:GrandpaRuleShape
rdf:type sh:NodeShape ;
rdfs:label "Grandpa rule shape" ;
sh:rule [
rdf:type sh:SPARQLRule ;
rdfs:comment "Shape to infer grandpa/grandchild relationship" ;
rdfs:label "Infer grandpas and grandchildren" ;
sh:construct """
PREFIX familyShapes: <http://example.org/familyShapes#>
CONSTRUCT {
?child familyShapes:grandPa $this .
$this familyShapes:grandChild ?child .
}
WHERE {
{
?child familyShapes:mother ?mom .
?mom familyShapes:father $this .
}
UNION
{
?child familyShapes:father ?dad .
?dad familyShapes:father $this .
}
}
""" ;
sh:order 10 ;
] ;
sh:targetClass familyShapes:Person ;
.
familyShapes:MaleShape
rdf:type sh:NodeShape ;
rdfs:label "Male shape" ;
sh:property [
sh:path familyShapes:gender ;
sh:hasValue familyShapes:male ;
] ;
.
familyShapes:Person
rdf:type rdfs:Class ;
rdf:type sh:NodeShape ;
rdfs:label "Person" ;
rdfs:subClassOf rdfs:Resource ;
sh:property [
rdf:type sh:PropertyShape ;
sh:path familyShapes:father ;
sh:class familyShapes:Person ;
sh:description "A Person's father." ;
sh:maxCount 1 ;
sh:name "father" ;
sh:node familyShapes:MaleShape ;
sh:nodeKind sh:IRI ;
sh:sparql [
sh:message "A person cannot be a father to that same person." ;
sh:select """PREFIX familyShapes: <http://example.org/familyShapes#>
SELECT $this
WHERE {
$this familyShapes:father $this .
}""" ;
] ;
] ;
sh:property [
rdf:type sh:PropertyShape ;
sh:path familyShapes:firstName ;
sh:datatype xsd:string ;
sh:description "A Person's first name (aka given name)." ;
sh:minCount 1 ;
sh:name "first name" ;
] ;
sh:property [
rdf:type sh:PropertyShape ;
sh:path familyShapes:gender ;
sh:class familyShapes:Gender ;
sh:description "A Person's gender." ;
sh:maxCount 1 ;
sh:minCount 1 ;
sh:name "gender" ;
] ;
sh:property [
rdf:type sh:PropertyShape ;
sh:path familyShapes:lastName ;
sh:datatype xsd:string ;
sh:description "A Person's last name (aka family name)." ;
sh:maxCount 1 ;
sh:minCount 1 ;
sh:name "last name" ;
] ;
sh:property [
rdf:type sh:PropertyShape ;
sh:path familyShapes:mother ;
sh:class familyShapes:Person ;
sh:description "A Person's mother." ;
sh:maxCount 1 ;
sh:name "mother" ;
sh:node familyShapes:FemaleShape ;
sh:nodeKind sh:IRI ;
sh:sparql [
rdfs:comment "A person cannot be that same person's mother." ;
sh:message "A person cannot be that same person's mother." ;
sh:select """PREFIX familyShapes: <http://example.org/familyShapes#>
SELECT $this
WHERE {
$this familyShapes:mother $this .
}""" ;
] ;
] ;
sh:rule [
rdf:type sh:SPARQLRule ;
rdfs:label "Infer grandmas and grandchildren" ;
sh:construct """PREFIX familyShapes: <http://example.org/familyShapes#>
CONSTRUCT {
?child familyShapes:grandMa $this .
$this familyShapes:grandChild ?child .
}
WHERE {
{
?child familyShapes:mother ?mom .
?mom familyShapes:mother $this .
}
UNION
{
?child familyShapes:father ?dad .
?dad familyShapes:mother $this .
}
}
""" ;
] ;
.
familyShapes:female
rdf:type familyShapes:Gender ;
rdfs:label "female" ;
.
familyShapes:firstName
rdf:type rdf:Property ;
rdfs:comment "A Person's first name (aka given name)." ;
rdfs:label "first name" ;
.
familyShapes:grandChild
rdf:type owl:ObjectProperty ;
rdfs:domain familyShapes:Person ;
rdfs:label "grand child" ;
rdfs:range familyShapes:Person ;
.
familyShapes:grandMa
rdf:type owl:ObjectProperty ;
rdfs:domain familyShapes:Person ;
rdfs:label "grand ma" ;
rdfs:range familyShapes:Person ;
.
familyShapes:grandPa
rdf:type owl:ObjectProperty ;
rdfs:domain familyShapes:Person ;
rdfs:label "grand pa" ;
rdfs:range familyShapes:Person ;
.
familyShapes:male
rdf:type familyShapes:Gender ;
rdfs:label "male" ;
.
familyShapes:mother
rdf:type rdf:Property ;
rdfs:comment "A Person's mother." ;
rdfs:label "mother" ;
.
I'm focused at this time on the familyShapes:GrandpaRuleShape
shape (starting on line 30) which, I believe, on line 58 targets the familyShapes:Person
class.
The SHACL API's RuleUtil.getShapesWithTargetNode
method returns an empty list, which is not the result I was expecting, so I've created a temporary local copy of the RuleUtil.getShapesWithTargetNode
method as shown below to help me debug my own code.
private static List<Shape> getShapesWithTargetNode(RDFNode focusNode, ShapesGraph shapesGraph) {
// TODO: Not a particularly smart algorithm - walks all shapes that have rules
List<Shape> shapes = new ArrayList<>();
for(Shape shape : shapesGraph.getRootShapes()) {
SHShape sr = shape.getShapeResource();
boolean shapeHasRule = sr.hasProperty(SH.rule);
boolean shapeFocused = sr.hasTargetNode(focusNode);
if(shapeHasRule && shapeFocused) {
shapes.add(shape);
}
}
return shapes;
}
I've stopped execution in the debugger in this method with focusNode
=http://example.org/familyShapes#Person
and the shapesGraph representing the shapes file above. The breakpoint is at the conditional in the for loop, after the two booleans have been assigned. The first value of shape
is familyShapes:GrandpaRuleShape
. However, the boolean shapeFocused
is false
. The boolean shapeHasRule
is true
as expected.
I was expecting that shapeFocused
would be true
at this point of execution. At a higher level, I was expecting that this method would return a list at least containing the grandpa shape, but it returns empty. I think I must be setting up the call to this method incorrectly, but I'm not sure what I'm doing wrong. Any suggestions?
Upvotes: 1
Views: 156
Reputation: 1421
I think it works correctly. Person is a class and the rule shape has the sh:targetClass Person. This means that the focus/target nodes are the instances of that class. If you invoke the function with a specific instance of Person then it should work.
Upvotes: 2