James Hudson
James Hudson

Reputation: 904

What is the difference between these shape graphs which use sh:or?

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

Answers (1)

James Hudson
James Hudson

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

Related Questions