Reputation: 904
I have the following Data Graph.
@prefix hr: <http://learningsparql.com/ns/humanResources#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sch: <http://schema.org/> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
hr:Another a rdfs:Class .
hr:Employee a rdfs:Class ;
rdfs:label "model" ;
rdfs:comment "a good employee" .
hr:Longer a hr:Employee ;
rdfs:label "model" ;
rdfs:comment "a good employee" .
hr:freestanding a rdf:Property ;
sch:rangeIncludes sch:Text .
hr:missing rdfs:comment "some comment about missing" .
hr:name a rdf:Property ;
sch:domainIncludes hr:Employee .
hr:nosuper a rdf:Property ;
sch:domainIncludes hr:Uncreated ;
sch:rangeIncludes sch:Text .
hr:randomtype a hr:invalidtype ;
rdfs:label "some label about randomtype" ;
rdfs:comment "some comment about randomtype" .
hr:typo a rdfs:Classs ;
rdfs:label "some label about typo" ;
rdfs:comment "some comment about typo" .
I am trying to understand the difference between these two Shape Graphs, which I think (wrongly!) should return the same result...validation errors for hr:typo, hr:randomtype, and hr:missing because there is no rdf:type property path to either rdfs:Class or rdf:Property.
The following Shape Graph produces the expected validation errors.
(A) -- good results
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sch: <http://schema.org/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <http://example.org/> .
ex:ClassShape
a sh:NodeShape ;
sh:property [
sh:path [ sh:zeroOrMorePath rdf:type ];
sh:nodeKind sh:IRI ;
sh:hasValue rdfs:Class;
sh:message "class" ;
] .
ex:PropertyShape
a sh:NodeShape ;
sh:property [
sh:path [ sh:zeroOrMorePath rdf:type ];
sh:nodeKind sh:IRI ;
sh:hasValue rdf:Property;
sh:message "property" ;
] .
ex:ClassOrProperty
a sh:NodeShape ;
sh:target [
a sh:SPARQLTarget ;
sh:select """
SELECT ?this
WHERE {
?this ?p ?o .
}
""" ;
] ;
sh:or (
ex:ClassShape
ex:PropertyShape
);
.
The good and expected validation errors produced by (A) are:
Validation Report
Conforms: False
Results (3):
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: ex:ClassOrProperty
Focus Node: hr:randomtype
Value Node: hr:randomtype
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: ex:ClassOrProperty
Focus Node: hr:typo
Value Node: hr:typo
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: ex:ClassOrProperty
Focus Node: hr:missing
Value Node: hr:missing
However, this Shape Graph:
(B) -- bad results
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sch: <http://schema.org/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <http://example.org/> .
ex:ClassOrProperty
a sh:NodeShape ;
sh:target [
a sh:SPARQLTarget ;
sh:select """
SELECT ?this
WHERE {
?this ?p ?o .
}
""" ;
] ;
sh:property [
sh:path [sh:zeroOrMorePath rdf:type] ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:hasValue rdfs:Class; ]
[ sh:hasValue rdf:Property; ]
)
];
.
does not produce only the expected validation errors. I get:
Validation Report
Conforms: False
Results (12):
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:freestanding
Value Node: hr:freestanding
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:name
Value Node: hr:name
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:Another
Value Node: hr:Another
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:nosuper
Value Node: hr:nosuper
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:Employee
Value Node: hr:Employee
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:randomtype
Value Node: hr:randomtype
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:randomtype
Value Node: hr:invalidtype
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:typo
Value Node: hr:typo
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:typo
Value Node: rdfs:Classs
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:missing
Value Node: hr:missing
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:Longer
Value Node: hr:Employee
Result Path: [ sh:zeroOrMorePath rdf:type ]
Constraint Violation in OrConstraintComponent (http://www.w3.org/ns/shacl#OrConstraintComponent):
Severity: sh:Violation
Source Shape: [ sh:nodeKind sh:IRI ; sh:or ( [ sh:hasValue rdfs:Class ] [ sh:hasValue rdf:Property ] ) ; sh:path [ sh:zeroOrMorePath rdf:type ] ]
Focus Node: hr:Longer
Value Node: hr:Longer
Result Path: [ sh:zeroOrMorePath rdf:type ]
Why are the results different?
The reason why I liked (B) over (A) is because it would have been more concise, if it had worked.
Both pySHACL and TopBraid SHACL API shaclvalidate.sh agree on the results.
Upvotes: 3
Views: 378
Reputation: 904
It is important to understand how property paths work. A path is used to reach values. When using sh:path [sh:zeroOrMorePath rdf:path]
and considering the node hr:Longer
, it will reach three values -- (0) hr:Longer
, (1) hr:Employee
, and (2) rdfs:Class
.
With this concept firmly in mind, what is going on in (B) and why it does not work can be fully explained.
Both (A) and (B) have the same target definition and will return the same focus nodes. These are:
hr:Another
hr:Employee
hr:Longer
hr:freestanding
hr:missing
hr:name
hr:nosuper
hr:randomtype
hr:typo
Additionally, common to both (A) and (B) is sh:path [sh:zeroOrMorePath rdf:type] ;
. When considering the node hr:Longer
, for example, it will emit three values, each of which may need to be checked. These three values are (0) hr:Longer
, (1) hr:Employee
, and (2) rdfs:Class
.
For (B), when it considers hr:Longer
and passes emitted value hr:Longer
to sh:or
, it see that it is not either a rdfs:Class
or rdf:Property
. A validation error is emitted because neither clause of sh:or
was satisfied.
To make (B) work, the two clauses in the sh:or
need to be changed to [ sh:path [sh:zeroOrMorePath rdf:type] ; sh:hasValue rdfs:Class; ]
and [ sh:path [sh:zeroOrMorePath rdf:type] ; sh:hasValue rdf:Property; ]
. In this case, when hr:Longer
is passed into the sh:or
, each clause checks the entire path and sh:hasValue
only requires that one of the three values emitted by the path matches.
(B) - Working
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sch: <http://schema.org/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <http://example.org/> .
ex:ClassOrProperty
a sh:NodeShape ;
sh:target [
a sh:SPARQLTarget ;
sh:select """
SELECT ?this
WHERE {
?this ?p ?o .
}
""" ;
] ;
sh:property [
sh:path [sh:zeroOrMorePath rdf:type] ;
sh:nodeKind sh:IRI ;
sh:or (
[ sh:path [sh:zeroOrMorePath rdf:type] ; sh:hasValue rdfs:Class; ]
[ sh:path [sh:zeroOrMorePath rdf:type] ; sh:hasValue rdf:Property; ]
)
];
.
Now considering (A), each focus node is passed to ex:PropertyShape
and ex:ClassShape
. If it validates against one of the shapes, it will validate. Both shapes are similar in that they each use the path sh:path [ sh:zeroOrMorePath rdf:type ];
. Because they use sh:hasValue
, only one of the emitted values for the path needs to match. Considering hr:Longer
again, because the path will emit the value rdfs:Class
, it validates against ex:ClassShape
and no validation error is generated.
Upvotes: 2