Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SHACL focus node & source Shape #88 #160

Merged
merged 4 commits into from
Dec 1, 2023

Conversation

ensaremirerol
Copy link
Contributor

#88

Basically added a extra flag for CLI tool named -addBlankNodes. With this flag enabled tool will append all blank nodes that references shapes file.

Here is the data/shape file I used in testing and the validation report outputted from tool with mention flag enabled

Data and Shape

@prefix : <https://biomedit.ch/rdf/sphn-schema/sphn#> .
@prefix chop: <https://biomedit.ch/rdf/sphn-resource/chop/> .
@prefix constraints: <https://biomedit.ch/rdf/sphn-schema-constraints/sphn#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix eco: <http://purl.obolibrary.org/obo/ECO_> .
@prefix edam: <http://edamontology.org/> .
@prefix efo: <http://www.ebi.ac.uk/efo/EFO_> .
@prefix emdn: <https://biomedit.ch/rdf/sphn-resource/emdn/> .
@prefix genepio: <http://purl.obolibrary.org/obo/GENEPIO_> .
@prefix geno: <http://purl.obolibrary.org/obo/GENO_> .
@prefix icd-10-gm: <https://biomedit.ch/rdf/sphn-resource/icd-10-gm/> .
@prefix obi: <http://purl.obolibrary.org/obo/OBI_> .
@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 sh: <http://www.w3.org/ns/shacl#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix snomed: <http://snomed.info/id/> .
@prefix so: <http://purl.obolibrary.org/obo/SO_> .
@prefix sphn-atc: <https://biomedit.ch/rdf/sphn-resource/atc/> .
@prefix sphn-loinc: <https://biomedit.ch/rdf/sphn-resource/loinc/> .
@prefix ucum: <https://biomedit.ch/rdf/sphn-resource/ucum/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ex: <http://example.org/> .

constraints:AccessDevice a sh:NodeShape ;
    sh:closed false ;
    sh:ignoredProperties ( rdf:type ) ;
    sh:property [ sh:maxCount 1 ;
            sh:message "constraint:AccessDevice_1",
                "message:MinCount, Or, or MaxCount constraint violated, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology",
                "sparql:SELECT ?this (COUNT(?o) AS ?actualCount) ?actual WHERE { ?this <https://biomedit.ch/rdf/sphn-schema/sphn#hasTypeCode> ?o . ?o rdf:type ?actual . }",
                "template:MinCount, Or, or MaxCount constraint violated on {this} on hasTypeCode, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology, found {actualCount} objects of type {actual}" ;
            sh:minCount 0 ;
            sh:or ( [ sh:class :Code ] [ sh:class snomed:105789008 ] [ sh:class :Terminology ] ) ;
            sh:path :hasTypeCode ],
        [ sh:maxCount 1 ;
            sh:message "constraint:AccessDevice_2",
                "message:MinCount, Or, or MaxCount constraint violated, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology",
                "sparql:SELECT ?this (COUNT(?o) AS ?actualCount) ?actual WHERE { ?this <https://biomedit.ch/rdf/sphn-schema/sphn#hasProductCode> ?o . ?o rdf:type ?actual . }",
                "template:MinCount, Or, or MaxCount constraint violated on {this} on hasProductCode, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology, found {actualCount} objects of type {actual}" ;
            sh:minCount 0 ;
            sh:or ( [ sh:class :Code ] [ sh:class :Terminology ] ) ;
            sh:path :hasProductCode ] ;
    sh:targetClass :AccessDevice .

ex:test a :AccessDevice ;
    :hasTypeCode ex:dummy ;
    :hasTypeCode ex:dummy2 .

ex:dummy a ex:dummyType1 .
ex:dummy2 a ex:dummyType2 .

Validation Report with Blank Node/s

@prefix :            <https://biomedit.ch/rdf/sphn-schema/sphn#> .
@prefix chop:        <https://biomedit.ch/rdf/sphn-resource/chop/> .
@prefix constraints: <https://biomedit.ch/rdf/sphn-schema-constraints/sphn#> .
@prefix dash:        <http://datashapes.org/dash#> .
@prefix dcterms:     <http://purl.org/dc/terms/> .
@prefix eco:         <http://purl.obolibrary.org/obo/ECO_> .
@prefix edam:        <http://edamontology.org/> .
@prefix efo:         <http://www.ebi.ac.uk/efo/EFO_> .
@prefix emdn:        <https://biomedit.ch/rdf/sphn-resource/emdn/> .
@prefix ex:          <http://example.org/> .
@prefix genepio:     <http://purl.obolibrary.org/obo/GENEPIO_> .
@prefix geno:        <http://purl.obolibrary.org/obo/GENO_> .
@prefix graphql:     <http://datashapes.org/graphql#> .
@prefix icd-10-gm:   <https://biomedit.ch/rdf/sphn-resource/icd-10-gm/> .
@prefix obi:         <http://purl.obolibrary.org/obo/OBI_> .
@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 sh:          <http://www.w3.org/ns/shacl#> .
@prefix skos:        <http://www.w3.org/2004/02/skos/core#> .
@prefix snomed:      <http://snomed.info/id/> .
@prefix so:          <http://purl.obolibrary.org/obo/SO_> .
@prefix sphn-atc:    <https://biomedit.ch/rdf/sphn-resource/atc/> .
@prefix sphn-loinc:  <https://biomedit.ch/rdf/sphn-resource/loinc/> .
@prefix swa:         <http://topbraid.org/swa#> .
@prefix tosh:        <http://topbraid.org/tosh#> .
@prefix ucum:        <https://biomedit.ch/rdf/sphn-resource/ucum/> .
@prefix xsd:         <http://www.w3.org/2001/XMLSchema#> .

_:b0    sh:maxCount  1 ;
        sh:message   "template:MinCount, Or, or MaxCount constraint violated on {this} on hasTypeCode, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology, found {actualCount} objects of type {actual}" , "sparql:SELECT ?this (COUNT(?o) AS ?actualCount) ?actual WHERE { ?this <https://biomedit.ch/rdf/sphn-schema/sphn#hasTypeCode> ?o . ?o rdf:type ?actual . }" , "message:MinCount, Or, or MaxCount constraint violated, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology" , "constraint:AccessDevice_1" ;
        sh:minCount  0 ;
        sh:or        ( [ sh:class  :Code ]
                       [ sh:class  snomed:105789008 ]
                       [ sh:class  :Terminology ]
                     ) ;
        sh:path      :hasTypeCode .

[ rdf:type     sh:ValidationReport ;
  sh:conforms  false ;
  sh:result    [ rdf:type                      sh:ValidationResult ;
                 sh:focusNode                  ex:test ;
                 sh:resultMessage              "constraint:AccessDevice_1" , "sparql:SELECT ?this (COUNT(?o) AS ?actualCount) ?actual WHERE { ?this <https://biomedit.ch/rdf/sphn-schema/sphn#hasTypeCode> ?o . ?o rdf:type ?actual . }" , "message:MinCount, Or, or MaxCount constraint violated, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology" , "template:MinCount, Or, or MaxCount constraint violated on {this} on hasTypeCode, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology, found {actualCount} objects of type {actual}" ;
                 sh:resultPath                 :hasTypeCode ;
                 sh:resultSeverity             sh:Violation ;
                 sh:sourceConstraintComponent  sh:MaxCountConstraintComponent ;
                 sh:sourceShape                _:b0
               ] ;
  sh:result    [ rdf:type                      sh:ValidationResult ;
                 sh:focusNode                  ex:test ;
                 sh:resultMessage              "sparql:SELECT ?this (COUNT(?o) AS ?actualCount) ?actual WHERE { ?this <https://biomedit.ch/rdf/sphn-schema/sphn#hasTypeCode> ?o . ?o rdf:type ?actual . }" , "constraint:AccessDevice_1" , "template:MinCount, Or, or MaxCount constraint violated on {this} on hasTypeCode, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology, found {actualCount} objects of type {actual}" , "message:MinCount, Or, or MaxCount constraint violated, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology" ;
                 sh:resultPath                 :hasTypeCode ;
                 sh:resultSeverity             sh:Violation ;
                 sh:sourceConstraintComponent  sh:OrConstraintComponent ;
                 sh:sourceShape                _:b0 ;
                 sh:value                      ex:dummy
               ] ;
  sh:result    [ rdf:type                      sh:ValidationResult ;
                 sh:focusNode                  ex:test ;
                 sh:resultMessage              "template:MinCount, Or, or MaxCount constraint violated on {this} on hasTypeCode, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology, found {actualCount} objects of type {actual}" , "message:MinCount, Or, or MaxCount constraint violated, expected minCount is 0 and maxCount is 1 and expected one of the following: https://biomedit.ch/rdf/sphn-schema/sphn#Code, http://snomed.info/id/105789008, https://biomedit.ch/rdf/sphn-schema/sphn#Terminology" , "constraint:AccessDevice_1" , "sparql:SELECT ?this (COUNT(?o) AS ?actualCount) ?actual WHERE { ?this <https://biomedit.ch/rdf/sphn-schema/sphn#hasTypeCode> ?o . ?o rdf:type ?actual . }" ;
                 sh:resultPath                 :hasTypeCode ;
                 sh:resultSeverity             sh:Violation ;
                 sh:sourceConstraintComponent  sh:OrConstraintComponent ;
                 sh:sourceShape                _:b0 ;
                 sh:value                      ex:dummy2
               ]
] .

Copy link
Collaborator

@HolgerKnublauch HolgerKnublauch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work overall! Certainly the last main class needs to be removed, but you may want to look into my other comments too before submitting an update?


Model blankNodes = ModelFactory.createDefaultModel();

Stream<Statement> statements = report.listStatements(null, SH.result, (RDFNode) null).toList().stream();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor comment here: this first creates a List and then a Stream. This may not scale well for large reports. Better would be to use an ExtendedIterator instead, which also has a map function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it all list creation to use only iterators

statements.forEach(statement -> {
blankNodes.add(statement);
if (statement.getObject().isAnon()) {
blankNodes.addAll(resolveAllBlankNodes(model, statement.getResource()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can theoretically run into a recursion if the bnodes have cycles. That is extremely unlikely but is usually handled by passing around a Set of the reached subjects.

Also, it can theoretically cause a stackoverflow when you traverse deep rdf:Lists. Breadth-first traversal avoids that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review current state of the code. I don't know if this is what you wanted

@ensaremirerol
Copy link
Contributor Author

I've refactored code based on your feedback.
Thanks!

@HolgerKnublauch HolgerKnublauch merged commit c48ad9d into TopQuadrant:master Dec 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants