TopQuadrant / shacl

SHACL API in Java based on Apache Jena
Apache License 2.0
214 stars 61 forks source link

subclass inferencing #108

Closed rapw3k closed 3 years ago

rapw3k commented 3 years ago

Hi, I am not very familiar yet with SHACL, so, I am trying to figure out if is possible, and if so, how, to define a shape constraint saying that the range of a property should be of type X AND any of its subclasses (rdf:subclassof*). FOr example, in shape below, how to say that the range of geo:hasGeometry is geo:Geometry and its subclasses ? thanks

https://astrea.linkeddata.es/shapes#3f6891594ac2d163a004bec00f8db48a a sh:PropertyShape ; sh:class geo:Geometry ; sh:nodeKind sh:IRIOrLiteral ; sh:path geo:hasGeometry .

JohannesLipp commented 3 years ago

Dear Raul,

great to see that we can get in touch here as well. In our projects, we appended parts of the ontology to the data graphs to inform the shacl validator about these additional properties.

Example (before):

Example (after, adding the ontology content to the data graph):

Hope this helps!

rapw3k commented 3 years ago

Thank you @JohannesLipp ! Yes, that works. However, this is not really practical. It would be much better that the validator would support at least class inferencing. Perhaps this would be some feature/extension for the future ? cu!

HolgerKnublauch commented 3 years ago

The validator (and the standard) does support subclass inferencing, based on rdfs:subClassOf triples found in the data graph. Without such triples there is no way for it to know that SubGeometry is a subclass of Geometry. Is your issue that you find adding those subClassOf triples impractical because they are only in the Ontology? What we do all the time is to use owl:imports, e.g.

ex:DataGraph owl:imports ex:Ontology

and then build a MultiUnion Graph in jena with all these graphs merged into a single virtual graph. This also means you can have

ex:ShapesGraph owl:imports ex:Ontology

which makes it easier to "attach" constraints to the classes.

rapw3k commented 3 years ago

Thanks @HolgerKnublauch; however that does not look so straightforward to me as you say. Perhaps I am missing something though.

In our case the data graph is encoded as a jsonld that points to the context derived from target ontology, see partial example below :

{
    "@graph" : [ {
      "@id" : "http://www.w3id.org/afarcloud/pCoord?lat=45.75&long=4.85",
      "@type" : "Point",
      "asWKT" : "POINT(45.75 4.85)"
    }, 
    {
      "@id" : "http://www.w3id.org/afarcloud/poi?lat=45.75&long=4.85",
      "@type" : "Feature",
      "hasGeometry" : "http://www.w3id.org/afarcloud/pCoord?lat=45.75&long=4.85"
    }, {
      "@id" : "urn:afc:AS09:cropsManagement:TEC:soil:sen0022",
      "@type" : [ "SoilSensor", "AfarcloudSensors" ]
    }, {
      "@id" : "urn:afc:AS09:sen0022:obs-1514810172",
      "@type" : "Observation",
      "hasFeatureOfInterest" : "http://www.w3id.org/afarcloud/poi?lat=45.75&long=4.85",
      "hasResult" : "urn:afc:AS09:sen0022:obs-1514810172/q1",
      "madeBySensor" : "urn:afc:AS09:cropsManagement:TEC:soil:sen0022",
      "observedProperty" : "http://www.w3id.org/afarcloud/soil_temperature",
      "resultTime" : "2018-01-01T12:36:12Z"
    }, {
      "@id" : "urn:afc:AS09:sen0022:obs-1514810172/q1",
      "@type" : "QuantityValue",
      "identifier" : "q1",
      "numericValue" : "0.27121272683143616",
      "unit" : "http://qudt.org/vocab/unit/DEG_C"
    } ],
    "@context" : [ "https://w3id.org/demeter/agri-context.jsonld", {
      "qudt-unit" : "http://qudt.org/vocab/unit/"
    } ]
  }

Our SHACL is: https://raw.githubusercontent.com/rapw3k/DEMETER/master/models/SHACL/demeterAgriProfile-SHACL.ttl

The error validating the input with the SHACL is:

[ a            sh:ValidationReport ;
  sh:conforms  false ;
  sh:result    [ a                             sh:ValidationResult ;
                 sh:focusNode                  <http://www.w3id.org/afarcloud/poi?lat=45.75&amp;long=4.85> ;
                 sh:resultMessage              "ClassConstraint[<http://www.opengis.net/ont/geosparql#Geometry>]: Expected class :<http://www.opengis.net/ont/geosparql#Geometry> for <http://www.w3id.org/afarcloud/pCoord?lat=45.75&amp;long=4.85>" ;
                 sh:resultPath                 geo:hasGeometry ;
                 sh:resultSeverity             sh:Violation ;
                 sh:sourceConstraintComponent  sh:ClassConstraintComponent ;
                 sh:sourceShape                <https://astrea.linkeddata.es/shapes#3f6891594ac2d163a004bec00f8db48a> ;
                 sh:value                      <http://www.w3id.org/afarcloud/pCoord?lat=45.75&amp;long=4.85>
               ]
] .

I tried the following: 1.--- import in the SHACL the target ontology. Result: no change

@base <https://w3id.org/demeter/shacl> .
<https://w3id.org/demeter/shacl>
    rdf:type owl:Ontology ;
    owl:imports <https://w3id.org/demeter/crossDomain> .

2.--- I tried adding directly the statement in SHACL. Result: no change

sf:Point rdfs:subClassOf geo:Geometry .    

3.--- I tried adding import statement in data graph, although not sure this is correct as we use context already. Result: no change

"http://www.w3.org/2002/07/owl#imports":[
         {
            "@id":"https://w3id.org/demeter/crossDomain"
         }
      ]

4.--- I tried adding directly the statement in data graph. Result: it works. However as I said this is impractical.

{
    "@id": "http://www.opengis.net/ont/sf#Point",
    "http://www.w3.org/2000/01/rdf-schema#subClassOf": {"@id":"http://www.opengis.net/ont/geosparql#Geometry"}
  }

Any idea, or something I am missing?

HolgerKnublauch commented 3 years ago

How do you invoke this? Just adding owl:imports statements by itself doesn't do anything unless the calling code also follows those graphs and knows where to find them.

I am not fluent in JSON-LD and it's too easy to get different triples than we believe. Could you cast this to Turtle so that we can be sure we are talking about the same data, namespaces etc?

rapw3k commented 3 years ago

In order to do the validation, I am using https://jena.apache.org/documentation/shacl/ . I also tried using the shacl playground tool https://shacl.org/playground/

Sure, the data graph in ttl would be:

@prefix ns0: <http://www.opengis.net/ont/geosparql#> .
@prefix dc: <http://purl.org/dc/terms/> .
@prefix ns1: <http://qudt.org/schema/qudt/> .
@prefix sosa: <http://www.w3.org/ns/sosa/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix owl:   <http://www.w3.org/2002/07/owl#> .

<http://www.w3id.org/afarcloud/pCoord?lat=45.75&amp;long=4.85>
  ns0:asWKT "POINT(45.75 4.85)"^^ns0:wktLiteral ;
  a <http://www.opengis.net/ont/sf#Point> .

<http://www.w3id.org/afarcloud/poi?lat=45.75&amp;long=4.85>
  ns0:hasGeometry <http://www.w3id.org/afarcloud/pCoord?lat=45.75&amp;long=4.85> ;
  a ns0:Feature .

<urn:afc:AS09:cropsManagement:TEC:soil:sen0022> a <https://json-ld.org/playground/AfarcloudSensors>, <https://json-ld.org/playground/SoilSensor> .
<urn:afc:AS09:sen0022:obs-1514810172/q1>
  dc:identifier "q1" ;
  ns1:numericValue "0.27121272683143616" ;
  ns1:unit <http://qudt.org/vocab/unit/DEG_C> ;
  a ns1:QuantityValue .

<urn:afc:AS09:sen0022:obs-1514810172>
  a sosa:Observation ;
  sosa:hasFeatureOfInterest <http://www.w3id.org/afarcloud/poi?lat=45.75&amp;long=4.85> ;
  sosa:hasResult <urn:afc:AS09:sen0022:obs-1514810172/q1> ;
  sosa:madeBySensor <urn:afc:AS09:cropsManagement:TEC:soil:sen0022> ;
  sosa:observedProperty <http://www.w3id.org/afarcloud/soil_temperature> ;
  sosa:resultTime "2018-01-01T12:36:12Z"^^xsd:dateTime .

I tried adding the import like this:

@base <https://w3id.org/demeter/example> .
<https://w3id.org/demeter/example>
    owl:imports <https://w3id.org/demeter/crossDomain> .
HolgerKnublauch commented 3 years ago

Sorry but this repo here is about the TopBraid SHACL API, not the one from Jena. I suggest asking the jena-users mailing list.