shacl

Objectified property comparison SHACL


I want to use SHACL to do comparison of 2 dates. In the simplest case, this would be fine and not very verbose:

Data:
@prefix ex: <http://example.org/> .
ex:patient1 a ex:Patient ;
    ex:hasBirthDate "2000"^^xsd:gYear ;
    ex:hasDeathDate "1999"^^xsd:gYear .

Shape:
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

ex:PatientBirthDateShape
    a sh:NodeShape ;
    sh:targetClass ex:Patient ;
    sh:property [
        sh:path ex:hasBirthDate ;
        sh:lessThan ex:hasDeathDate ;
    ] .

However, as soon as I objectify the values:

@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

ex:patient a ex:Patient ;
    ex:hasDate ex:birthdate1, ex:deathdate1 .

ex:birthdate1 a ex:BirthDate ;
    ex:hasYear "2000"^^xsd:gYear .

ex:deathdate1 a ex:DeathDate ;
    ex:hasYear "1999"^^xsd:gYear .

I have a problem! How do I conditionally check that in a neat and not too-verbose way?

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

ex:PatientBirthDateShape
    a sh:NodeShape ;
    sh:targetClass ex:Patient ;
    sh:property [
        sh:path ex:hasDate ;
        sh:node [
            a sh:NodeShape ;
            sh:targetClass ex:BirthDate ;
            sh:property [
                sh:path ex:hasYear ;
                sh:lessThan [
                    a sh:NodeShape ; 
                    sh:targetClass ex:DeathDate ; 
                    sh:property [
                        sh:path ex:hasYear
                    ]
                ]
            ]
        ]
    ] .

Something like that - but even this painful to look at shape does not work - I don't know how to conditionally target and compare the right nodes...

I cannot just use the sh:path ex:hasYear - as this will sometimes point to a birthYear, and sometimes a deathYear... Maybe a SPARQL target? But this seems like such a heavy solution for something as basic as objectification. Am I missing something obvious?


Solution

  • With the provided data, I believe SPARQL is the only real solution.

    The problem is the generic hasDate property where the distinction is then made one level down through the rdf:type of the nested date objects. I would argue that ontology design is an anti pattern. It is making not only constraint validation more difficult but something as simple as querying for the birth date becomes very inefficient as any query will need to filter through multiple values only to query one specific value. Every consumer of this data downstream will struggle with this design choice.