eclipse-rdf4j / rdf4j

Eclipse RDF4J: scalable RDF for Java
https://rdf4j.org/
BSD 3-Clause "New" or "Revised" License
359 stars 161 forks source link

SHACL - Scoping of logical operators with sh:hasValue and dash:hasValueIn #2458

Closed hmottestad closed 3 years ago

hmottestad commented 4 years ago

There is a scoping issue for sh:or when used node shapes vs property shapes.

@prefix ex: <http://example.com/data/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

ex:shape1
    a sh:NodeShape ;
    sh:targetClass ex:Test ;
    sh:property [
        sh:path ex:info ; 
        sh:or ([ sh:hasValue "blue" ; ] [ sh:hasValue "green" ; ] [ sh:hasValue "red" ; ]) ;
    ]  .
@prefix ex: <http://example.com/data/> .

ex:test1 a ex:Test  .

The above example fails validation in the ShaclSail, but should pass since sh:hasValue is inside a sh:nodeShape and thus each value is validated independently.

abrokenjester commented 4 years ago

I'm not sure I understand the example. Why should this succeed? The contraint appears to expresss that every instance of test must have a value for ex:info that is either blue, green or, red. Why should a shape that doesn't have any value for that property be valid?

hmottestad commented 4 years ago

The definition for sh:and is "for every value" so if there are no values then it should succeed. Further more, each shape inside the sh:and is a node shape. So we are actually saying that each value for sh:info should be "red" and "blue" and ...

It took me a while to understand it. It's fairly counter intuitive.

abrokenjester commented 4 years ago

...but the example is an sh:or, not an sh:and.... I'm confused.

hmottestad commented 4 years ago

I'll update the title. The definition sh:and and sh:or are the same wrt. how they operate if there are not elements and how they operate on value nodes vs. targets.

rdstn commented 4 years ago

If I'm getting this correctly, the way to retain the old behaviour for target shapes is to use paths?

So, for example, if I want this Shape to target people named Aerith or Bob, and not to match on null names, I'd do:

rsx:targetShape [
    sh:or (
        [ sh:path rdfs:name ; sh:hasValue "Aerith" ]
        [ sh:path rdfs:name ; sh:hasValue "Bob" ]
    ) ; ] ;

Whereas if I want to match null names, I'd go for:

rsx:targetShape [
    dash:hasValueIn ( "Aerith", "Bob" )
]

That one is hard to wrap one's head around, maybe you'll need some documentation pointing out how it works. Not that such documentation wouldn't be better off located in the SHACL spec.