TopQuadrant / shacl

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

Upgrading from version 3 to version 4 : loss of sh: order (> 1) in the sh:rule ? #137

Closed benellefi closed 2 years ago

benellefi commented 2 years ago

Dear Holger,

Since I upgraded from version 1.3.2 to version 1.4.2, inferences from sh:rules with order greater than 2 are no longer inferred!

If we take the exemple of the sparql rule in the shape (https://www.w3.org/TR/shacl-af/#rules-order) : the uncles are inferred but not the cousins (using the version 1.4.2)

Note: this worked fine in the version 1.3.2 .

Does this make sense to you?

Thank you in advance

HolgerKnublauch commented 2 years ago

I am not aware of changes in this area. Would you be able to send the example files that you have tried, with instances?

benellefi commented 2 years ago
@prefix owl: <http://www.w3.org/2002/07/owl#>.
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
@prefix dash: <http://datashapes.org/dash#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ex: <https://example.com/ontologies#> .

DATA :

ex:Instance1 a ex:Type1 .

ex:Instance2  a ex:Type2 .

ex:Type2 rdfs:subClassOf ex:Type1 .

Rules

  1. Rule in order 1 : Add possibleTables instances to all Type1 (without hasPossibleTable)
  2. Rule in order 2 : add ValueTableComponentType to all possibleTables (without TableComponents)
ex:PossibleTablesRules a sh:NodeShape ;
    sh:target [
        rdf:type sh:SPARQLTarget ;
        sh:select """
            prefix owl: <http://www.w3.org/2002/07/owl#>.
        prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
        prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
        prefix ex: <https://example.com/ontologies#> 

        SELECT distinct $this
        WHERE {
                $this a/rdfs:subClassOf* ex:Type1 .
                FILTER NOT EXISTS {$this ex:hasPossibleTable []}
            }"""
    ];

    sh:rule [
        a sh:SPARQLRule ;
        sh:order 1;
        sh:construct """
        prefix owl: <http://www.w3.org/2002/07/owl#>.
        prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
        prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
        prefix ex: <https://example.com/ontologies#> 

            CONSTRUCT {
                ?idBVT1  a owl:NamedIndividual; a ex:PossibleTable .
                $this ex:hasPossibleTable ?idBVT1 .
                }
            WHERE {
                 $this a/rdfs:subClassOf* ex:Type1 .
                FILTER NOT EXISTS {$this ex:possibleTable []}.
                BIND(IRI(CONCAT("https://example.com/ontologies#PossibleTable", strUUID())) AS ?idBVT1).
            } """
    ];

    sh:rule [
        a sh:SPARQLRule ;
         sh:order 2;
        sh:construct """

        prefix owl: <http://www.w3.org/2002/07/owl#>.
        prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
        prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
        prefix ex: <https://example.com/ontologies#> 

            CONSTRUCT {

                ?idTC01 a owl:NamedIndividual; a ex:ValueTableComponentType1 ;  ex:rangeOrder 1 .

                ?idTC02 a owl:NamedIndividual; a ex:ValueTableComponentType2 ; ex:rangeOrder 2 .

                ?idTC03 a owl:NamedIndividual; a ex:ValueTableComponentType3  ; ex:rangeOrder 3 .

                ?idTC04 a owl:NamedIndividual; a ex:ValueTableComponentType4  ; ex:rangeOrder 4 .

                ?idTC05 a owl:NamedIndividual; a ex:ValueTableComponentType5 ; ex:rangeOrder 5 .

                ?idTC06 a owl:NamedIndividual; a ex:ValueTableComponentType6  ; ex:rangeOrder 6 .

                ?idTC07 a owl:NamedIndividual; a ex:ValueTableComponentType7  ; ex:rangeOrder 7 .

                ?idTC08 a owl:NamedIndividual; a ex:ValueTableComponentType8  ; ex:rangeOrder 8 .

                ?idTC09 a owl:NamedIndividual; a ex:ValueTableComponentType9  ; ex:rangeOrder 9 .

                ?idTC10 a owl:NamedIndividual; a ex:ValueTableComponentType10  ; ex:rangeOrder 10 .

                ?idTC11 a owl:NamedIndividual; a ex:ValueTableComponentType11  ; ex:rangeOrder 11 .

                ?idTC12 a owl:NamedIndividual; a ex:ValueTableComponentType12  ; ex:rangeOrder 12 .

                ?idTC13 a owl:NamedIndividual; a ex:ValueTableComponentType13  ; ex:rangeOrder 13 .

                ?idTC14 a owl:NamedIndividual; a ex:ValueTableComponentType14  ; ex:rangeOrder 14 .

                ?idBVT1  ex:hasTableComponent ?idTC01 ;
                        ex:hasTableComponent ?idTC02 ; 
                        ex:hasTableComponent ?idTC03 ; 
                        ex:hasTableComponent ?idTC04 ; 
                        ex:hasTableComponent ?idTC05 ; 
                        ex:hasTableComponent ?idTC06 ; 
                        ex:hasTableComponent ?idTC07 ; 
                        ex:hasTableComponent ?idTC08 ; 
                        ex:hasTableComponent ?idTC09 ; 
                        ex:hasTableComponent ?idTC10 ; 
                        ex:hasTableComponent ?idTC11 ; 
                        ex:hasTableComponent ?idTC12 ; 
                        ex:hasTableComponent ?idTC13 ; 
                        ex:hasTableComponent ?idTC14 .    
                }
            WHERE {              
               $this ex:hasPossibleTable ?idBVT1 .
               ?idBVT1 a ex:PossibleTable .
                FILTER NOT EXISTS {?idBVT1  ex:hasTableComponent []}

                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType1", strUUID())) AS ?idTC01).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType2", strUUID())) AS ?idTC02).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType3", strUUID())) AS ?idTC03).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType4", strUUID())) AS ?idTC04).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType5", strUUID())) AS ?idTC05).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType6", strUUID())) AS ?idTC06).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType7", strUUID())) AS ?idTC07).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType8", strUUID())) AS ?idTC08).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType9", strUUID())) AS ?idTC09).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType10", strUUID())) AS ?idTC10).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType11", strUUID())) AS ?idTC11).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType12", strUUID())) AS ?idTC12).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType13", strUUID())) AS ?idTC13).
                BIND(IRI(CONCAT("https://example.com/ontologies#ValueTableComponentType14", strUUID())) AS ?idTC14).
                }
            """       ] .

Inferences in 1.4.2

ex:Instance1 ex:hasPossibleTable ex:PossibleTable246aa8-4304-43ae-9dc2-3f0936ae772c .
ex:PossibleTable246aa8-4304-43ae-9dc2-3f0936ae772c  a owl:NamedIndividual; a ex:PossibleTable .

ex:Instance2 ex:hasPossibleTable ex:PossibleTablef14a16012e444cd2bdf8a06c0c2607bf  .
ex:PossibleTablef14a16012e444cd2bdf8a06c0c2607bf  a owl:NamedIndividual; a ex:PossibleTable .

--> missing inferences: all instances of ex:ValueTableComponentType in rule order 2 that used to be inferred in version 1.3.2

HolgerKnublauch commented 2 years ago

For me the queries didn't even parse - it complains about the . at the end of the prefix lines.

But once I removed those, I got those inferences

ex:Instance1 ex:hasPossibleTable ex:PossibleTable4aa39bb1-3dcc-4fce-bbe0-899d8af2a469 ; . ex:Instance2 ex:hasPossibleTable ex:PossibleTableb4a53846-c3df-4dd9-bfce-49f3454a8bcd ; . ex:PossibleTable4aa39bb1-3dcc-4fce-bbe0-899d8af2a469 a owl:NamedIndividual ; a ex:PossibleTable ; . ex:PossibleTableb4a53846-c3df-4dd9-bfce-49f3454a8bcd a owl:NamedIndividual ; a ex:PossibleTable ; .

Does that look as expected? For future reference could you send me complete files so that we can be sure to really talk about the same scenarios? Also, how do you execute that - command line? I ran them from the TopBraid EDG UI, but the algorithm should be the same. In my test the data graph owl:imported the shapes graph. These details matter thus the request for the complete files.

benellefi commented 2 years ago

Dear Holger,

Thank you for the quick reply In my files I am using sh: prefixes which references a local file, so the errors here are from my copy paste and are on the dots at the end of the prefixes in the sparqls (the dots had to be removed). Sorry about that.

Please find in the attached zip the corrected files:

I am using a jena (4.2.0) code to call the RuleUtil java in org.topbraid.shacl.rules.RuleUtil.java

    Model data = JenaUtil.createDefaultModel ();
    Model rules = JenaUtil.createDefaultModel ();

      RDFDataMgr.read (dataModel, inputData, Lang.TTL);
      RDFDataMgr.read (shapeModel, inputRules, Lang.TTL);

ProgressMonitor pm = new SimpleProgressMonitor ("pmShaclInferences");
Model inferenceModel = RuleUtil.executeRules (data, rules, null, pm);

inferencesExample.zip

HolgerKnublauch commented 2 years ago

I ran this through a debugger. There is indeed a difference in between the versions. Look at RuleEngine line 219. This will now re-compute the target nodes once a new sh:order has been reached. In this case, the second rule is executed with empty target nodes, because the instances no longer fulfill the SELECT condition from the SPARQL target.

We made this change 11 months ago because a customer (correctly) reported that some rules may change the target nodes, so that more nodes become targets because additional triples are present.

So I would suggest to take a look whether you can reformulate the SPARQL-based target to be more general. Alternatively, use a conventional sh:targetClass and filter within the WHERE clause of each rule.

benellefi commented 2 years ago

Dear holger, Thank you very much for the clarifications. The reasons for this upgrade/change are indeed understandable. I will update the sparql targets.