zazuko / rdf-validate-shacl

Validate RDF data purely in JavaScript. An implementation of the W3C SHACL specification on top of the RDFJS stack.
MIT License
98 stars 13 forks source link

Using sh:and inside of property shapes sometimes fails #140

Open matthiaspalmer opened 2 months ago

matthiaspalmer commented 2 months ago

I am trying to find a way to reuse property shapes by utilizing the sh:and. However, the validator breaks for certain constraint components. I wonder if this is a bug or maybe I am missunderstanding certain aspects of the SHACL specification. Here is an example that illustrates the problem playground application:

Shapes Graph:

@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix schema: <http://schema.org/> .
@prefix ex: <http://example.com/> .

ex:PersonAddressShape
    a sh:NodeShape ;
    sh:targetClass schema:Person ;
    sh:property ex:ps3 .

ex:ps1  a sh:PropertyShape ;
        sh:minCount 1 .

ex:ps2  a sh:PropertyShape ;
        sh:minInclusive 17 .

ex:ps3  a sh:PropertyShape ;
        sh:path schema:age ;
        sh:and ( ex:ps2 ) .

Data graph (default data in the playground, provided for completeness):

{
  "@id": "https://example.com/John-Doe",
  "@type": "http://schema.org/Person",
  "http://schema.org/age": {
    "@type": "http://www.w3.org/2001/XMLSchema#integer",
    "@value": "18"
  },
  "http://schema.org/name": [
    "John",
    "Johnny"
  ]
}

The problem: Change the last line of the shapes graph with the sh:and and instead point to ex:ps1, there will be an error in the console stating:

Uncaught (in promise) Error: Cannot find validator for constraint component http://www.w3.org/ns/shacl#MinCountConstraintComponent
    at Wa.validateNodeAgainstConstraint (index-xGuZAY9y.js:2:38091)
    at Wa.validateNodeAgainstShape (index-xGuZAY9y.js:2:37716)
    at mn.nodeConformsToShape (index-xGuZAY9y.js:2:42234)
    at index-xGuZAY9y.js:2:13938
    at Array.every (<anonymous>)
    at Na (index-xGuZAY9y.js:2:13927)
    at pd.execute (index-xGuZAY9y.js:1:190845)
    at Wa.validateValueNodeAgainstConstraint (index-xGuZAY9y.js:2:38552)
    at Wa.validateNodeAgainstConstraint (index-xGuZAY9y.js:2:38282)
    at Wa.validateNodeAgainstShape (index-xGuZAY9y.js:2:37716)

My question is why would the above construction work for a value range constraint component but not for a cardinality constraint component.

Note, I have not tested through which constraint components that works as the cardinality constraint components is one of the most important from an inheritance perspecitive. If this is a bug that should be corrected, I am happy to do a more complete test to facilitate any potential fixes. I am also aware that there might be ways to achieve the same thing by adding the sh:and on a node shape instead, but this is not an option due to other reasons that is out of scope to bring up here in any detail (using SHACL to formally express application profiles).

tpluscode commented 2 months ago

Thank you for reporting this with a minimal reproduction. I have seen that error on some occasions but I never investigated to pinpoint its cause.

I do expect this to be a bug inherited from the original JS implementation. Same behavior is observed on https://shacl.org/playground/ . Interestingly, some other tools known to me also fail this case in various ways. At least in the web version available to experiment.

tpluscode commented 2 months ago

FYI, I am able to work around the issue although for now the report is a little inaccurate but at least the validation result is as expected.

That said, we are internally undecided whether we should support a use where the shape is essentially not conforming to the specification (Property Shapes require sh:path)

matthiaspalmer commented 1 month ago

That is great news @tpluscode, thanks a lot!

I have two questions:

  1. Do you think/know if your workaround makes it work for all constraint components (as I said above, I have not made complete test cases).
  2. In your opinion / reading of the specification, is this something that should be working across all implementations? Hence, should it be reported elsewhere? For instance pySHACL behaves very weird with the example above.

For me it is ok to repeat the sh:path, I did not do it in my example for simplicity and wrongly thinking that it was not needed.

FYI, I did see an example in the specification where it was left out and I just copied the approach, the second example shapes graph in 4.6.3. But that is probably a typo based on the more formal definition in 2.3.