Recursion using a sh:PropertyShape with embedded SPARQL

30 views
Skip to first unread message

Steve Ray

unread,
Oct 28, 2020, 9:52:02 PM10/28/20
to TopBraid Suite Users
Hi,
I am trying to recreate a recursive magic property, using a SHACL property shape. However, I suspect that the reason the recursion is not working is because the property identified in the sh:path statement isn't really a function in the same sense as a magic property is a function.

My shape code is as follows:

qudt:QuantityKind-appropriateUnit

  rdf:type sh:NodeShape ;

  sh:property [

      rdf:type sh:PropertyShape ;

      sh:path qudt:dappropriateUnit ;

      sh:values [

          sh:prefixes <http://qudt.org/2.1/schema/shacl/overlay/qudt> ;

          sh:select """SELECT DISTINCT ?unit

WHERE {

    {

        ?unit qudt:hasQuantityKind $this .

    }

    UNION

    {

        NOT EXISTS {

            ?unit qudt:hasQuantityKind $this .

        } .

        $this skos:broader ?parent1 .

        ?parent1 qudt:dappropriateUnit ?unit .

    } .

}  """ ;

        ] ;

    ] ;

  sh:targetClass qudt:QuantityKind ;

.


The non-recursive part works (the first clause of the union). But the second union clause does not work. Is there a way to make the qudt:dappropriateUnit property know that there's this associated sh:values clause?

Steve


Holger Knublauch

unread,
Oct 28, 2020, 10:39:08 PM10/28/20
to topbrai...@googlegroups.com

As you have found out, SPARQL itself doesn't "see" the additional inferred triples. SHACL node expressions do, if you can reformulate this query, using sh:path.

In TopBraid there is a "native" way of querying inferred values within SPARQL too:

Try

    ( ?parent1 qudt:dappropriateUnit ) tosh:values ?unit .

which is a magic property that will look at both inferred and asserted values at query time. Make sure to avoid infinite loops though, i.e. no cycles in the graph.

As this would only work within TopBraid, a more "standard" approach would be to use node expressions. Something like

sh:values [
    sh:distinct [
        sh:if [
            sh:exists [
                sh:path [ sh:inversePath qudt:hasQuantityKind ]               
            ]
        ] ;
        sh:then [
                sh:path [ sh:inversePath qudt:hasQuantityKind ]
            ]
        ] ;
        sh:else [
            sh:path qudt:dappropriateUnit ;
            sh:nodes [
                sh:path skos:broader ;
            ]
        ]
    ]
]

which due to the geekiness and my lack of example data I haven't tested. It's another syntax for "If this has a subject for hasQuantityKind then use that, otherwise recurse into the broader parent". The sh:path near the sh:else would do the recursion automatically, I believe.

    https://w3c.github.io/shacl/shacl-af/#node-expressions-path

(I just noticed the example there was wrong but this will hopefully be fixed soon).

Holger



Steve


--
You received this message because you are subscribed to the Google Groups "TopBraid Suite Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to topbraid-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/topbraid-users/CAGUep86_5U4TAKX-Jjb5erVdzZZZ9Xrfvv5%3DOzNJi-69gCjdzg%40mail.gmail.com.

Steve Ray

unread,
Oct 29, 2020, 6:34:43 PM10/29/20
to TopBraid Suite Users
Thanks Holger! That worked! I had to rebalance some ]'s but then it performed perfectly!

I'm still getting used to the syntax of the sh: operators. If I could trouble you for one more piece of advice. 

I realized that there are some instances of qudt:PhysicalConstant that also point to instances of qudt:QuantityKind, so I want to filter out anything that is not an instance of qudt:Unit. After exploring the documentation at https://w3c.github.io/shacl/shacl-af, I thought sh:filterShape might do the trick. The amended code is shown below. It isn't working - in fact it is weirdly now returning just the value of $this, i.e. the subject QuantityKind. Am I using sh:filterShape correctly? I'm trying to say "Look to see if anything is pointing to you via the hasQuantityKind property, and filter those down to just instances of qudt:Unit. If you find some, return those. If not, follow the skos:broader relation and try again with that QuantityKind."

Here is the functioning code:

qudt:QuantityKind-appropriateUnit

  rdf:type sh:NodeShape ;

  sh:property [

      rdf:type sh:PropertyShape ;

      sh:path qudt:appropriateUnit ;

      sh:group qudt:AppropriateUnitsGroup ;

      sh:order "15"^^xsd:decimal ;

      sh:values [

          sh:distinct [

              sh:else [

                  sh:path qudt:appropriateUnit ;

                  sh:nodes [

                      sh:path skos:broader ;

                    ] ;

                ] ;

              sh:if [

                  sh:exists [

                      sh:path [

                          sh:inversePath qudt:hasQuantityKind ;

                        ] ;

                      sh:filterShape qudt:Unit ;

                    ] ;

                ] ;

              sh:then [

                  sh:path [

                      sh:inversePath qudt:hasQuantityKind ;

                    ] ;

                  sh:filterShape qudt:Unit ;

                ] ;

            ] ;

        ] ;

    ] ;

  sh:targetClass qudt:QuantityKind ;

.


I realized that I need to 
Steve




Holger Knublauch

unread,
Oct 30, 2020, 9:15:32 PM10/30/20
to topbrai...@googlegroups.com

Hi Steve,

your email appears unfinished so I wasn't sure when/whether to respond. But the correct syntax for sh:filterShape would be like

sh:exists [
    sh:filterShape qudt:Unit ;
    sh:nodes [


        sh:path [
            sh:inversePath qudt:hasQuantityKind ;
        ] ;

    ] ;
]

i.e. it needs to be combined with sh:nodes. Imagine these node expressions like a pipeline where the output of one step becomes the input to the next. Here, the input nodes to the filterShape would be the results of the sh:path expression.

HTH
Holger

Reply all
Reply to author
Forward
0 new messages