Skip to content

#222, #288: Update SHACL-SHACL for general NodeExpression and SelectExpression #302

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

Draft
wants to merge 6 commits into
base: gh-pages
Choose a base branch
from
2 changes: 1 addition & 1 deletion shacl12-core/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1775,7 +1775,7 @@ <h3>Node Expressions</h3>
</p>
<aside class="example" title="A dynamically computed property using a node expression based on a SPARQL query">
<p>
Here is an example use of a node expression based on <a href="shacl12-sparql">SHACL-SPARQL</a>, computing
Here is an example use of a node expression based on <a data-cite="shacl12-sparql#SelectExpression">SHACL-SPARQL</a>, computing
the values of a property shape for the property "full name" as the concatenation of <code>ex:firstName</code>,
a space and the <code>ex:lastName</code>.
</p>
Expand Down
125 changes: 125 additions & 0 deletions shacl12-sparql/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ <h3>Terminology</h3>
<dfn data-cite="rdf12-concepts#dfn-literal" data-lt="literal|literals">literal</dfn>,
<dfn data-cite="rdf12-concepts#dfn-blank-node" data-lt="blank node|blank nodes">blank node</dfn>,
<dfn data-cite="rdf12-concepts#dfn-node" data-lt="node|nodes">node</dfn> of an RDF graph,
<dfn data-cite="rdf12-concepts#dfn-datatype" data-lt="datatype|datatypes">datatype</dfn>,
<dfn data-cite="rdf12-concepts#dfn-rdf-term" data-lt="term|terms">RDF term</dfn>, and
<dfn data-cite="rdf12-concepts#dfn-subject" data-lt="subject|subjects">subject</dfn>,
<dfn data-cite="rdf12-concepts#dfn-predicate" data-lt="predicate|predicates">predicate</dfn>, and
Expand All @@ -409,6 +410,11 @@ <h3>Terminology</h3>
<dfn data-cite="shacl12-core#dfn-sparql-property-path" data-lt="sparql property path|sparql property paths">SPARQL property path</dfn>,
<dfn data-cite="shacl12-core#dfn-shapes-graph" data-lt="shapes graph">shapes graph</dfn>,
<dfn data-cite="shacl12-core#dfn-validators" data-lt="validator|validators">validator</dfn>,
<dfn data-cite="shacl12-core#dfn-node-expression" data-lt="node expression|node expresssions">node expression</dfn>,
<dfn data-cite="shacl12-core#dfn-node-expression-function" data-lt="node expression function|node expresssion functions">node expression function</dfn>,
<dfn data-cite="shacl12-core#dfn-function-name" data-lt="node expression function name">function name</dfn>,
<dfn data-cite="shacl12-core#dfn-key-parameter" data-lt="key parameter">key parameter</dfn>,
<dfn data-cite="shacl12-core#dfn-output-nodes" data-lt="output nodes">output nodes</dfn>,
<dfn data-cite="shacl12-core#dfn-conform" data-lt="conform|conforms">conform</dfn>,
<dfn data-cite="shacl12-core#dfn-failure" data-lt="failure|failures">failure</dfn>,
<dfn data-cite="shacl12-core#dfn-shacl-instance" data-lt="shacl instance">SHACL instance</dfn>,
Expand Down Expand Up @@ -1205,6 +1211,118 @@ <h3>Validation with SPARQL-based Constraint Components</h3>
</section>
</section>

<section id="sparql-node-expressions">
<h2>SPARQL-based Node Expressions</h2>
<p>
This section introduces <a>node expression functions</a> based on SPARQL.
</p>

<section id="SelectExpression">
<h3>Select Expressions</h3>
<p>
A <a>node expression</a> that has a <a>value</a> for <code>sh:select</code> is called a <dfn>select expression</dfn> with the <a>function name</a>
<code>sh:SelectExpression</code>.
</p>
<p class="syntax">
<span data-syntax-rule="SelectExpression-syntax">A node in an RDF graph is a <a>well-formed</a> <a>select expression</a> if it is a <a>blank node</a>
that is the <a>subject</a> of exactly one <a>triple</a> with <code>sh:select</code> as <a>predicate</a> and a <a>literal</a> as <a>object</a>
with <a>datatype</a> <code>xsd:string</code>.</span>
<span data-syntax-rule="SelectExpression-query-valid">Using the <a href="#sparql-prefixes">prefix handling rules</a>, the value of <code>sh:select</code> is a valid SPARQL 1.2 SELECT query.</span>
<span data-syntax-rule="SelectExpression-query-output-nodes">The SPARQL query derived from the value of <code>sh:select</code> <a data-cite="sparql12-query/#selectproject">projects</a> exactly one variable in the SELECT clause.</span>
</p>
<div class="def" id="LiteralExpression-evaluation">
<div class="def-header">EVALUATION OF SELECT EXPRESSIONS</div>
<p>
The <a>output nodes</a> of a <a>select expression</a> are the list <code>resultNodes</code> consisting of exactly the bindings of the (only)
variable that is projected from the SELECT clause.
If present in the <a>scope</a>, the value of the scope variable <code>focusNode</code> MUST be <a>pre-bound</a> as the value of the SPARQL variable <code>this</code>.
<br/>
<br/>
<code>eval(expr, activeGraph, scope) -> resultNodes</code>
</p>
</div>
<p><em>The remainder of this section is informative.</em></p>
<aside class="example" title="A dynamically computed property using a node expression based on a SPARQL query">
<p>
Here is an example use of a <a>select expression</a>, computing the values of a property shape for the property
"full name" as the concatenation of <code>ex:firstName</code>, a space and the <code>ex:lastName</code>.
</p>
<div class="shapes-graph">
<div class="turtle">
ex:Person-fullName
a sh:PropertyShape ;
sh:name "full name" ;
sh:path <b>[
sh:prefixes &lt;http://example.org/ns&gt; ;
sh:select """
SELECT ?fullName
WHERE {
$this ex:firstName ?firstName .
$this ex:lastName ?lastName .
BIND (CONCAT(?firstName, " ", ?lastName) AS ?fullName) .
}
"""
]</b> ;
sh:datatype xsd:string .

&lt;http://example.org/ns&gt;
a owl:Ontology ;
sh:declare [
sh:prefix "ex" ;
sh:namespace "http://example.org/ns#"^^xsd:anyURI ;
] .
</div>
</div>
<p>
This example also illustrates the use of <code>sh:prefixes</code> to insert PREFIX declarations into the beginning of the query before parsing.
Note that the query is executed with the current <a>focus node</a> <a>pre-bound</a> to the variable <code>this</code>.
</p>
</aside>
<aside class="example" title="Dynamically computed target nodes using a node expression based on a SPARQL query">
<p>
Here is an example use of a <a>select expression</a>, computing the target nodes of a shape to consist of all instances of
<code>ex:Person</code> where the <code>ex:age</code> is less than <code>18</code>.
</p>
<div class="shapes-graph">
<div class="turtle">
ex:ChildShape
a sh:NodeShape ;
rdfs:label "Child shape" ;
rdfs:comment "This shape applies to all persons under 18 years of age." ;
sh:targetNode <b>[
sh:select """
PREFIX ex: &lt;http://example.org/ns#&gt;
SELECT ?person
WHERE {
?person a/rdfs:subClassOf* ex:Person .
?person ex:age ?age .
FILTER (?age &lt; 18) .
}
"""
]</b> .
</div>
</div>
<p>
From the following data graph, only <code>ex:Benjamin</code> is a target node.
</p>
<div class="data-graph">
<div class="turtle">
<span class="focus-node-selected">ex:Benjamin</span>
a ex:Person ;
ex:age 17 .

ex:Klaus
a ex:Person ;
ex:age 48 .

ex:Bernd
a ex:Person .
</div>
</div>
</aside>
</section>
</section>

<div style="padding-top: 30px">
<h1 id="appendix" style="font-size: 160%; font-weight: bold">Appendix</h1>
</div>
Expand Down Expand Up @@ -1317,6 +1435,13 @@ <h2>Revision History</h2>
<li><b>2024-02-14</b>: New work started by cloning the main SHACL spec and splitting it into SHACL Core and SHACL-SPARQL</li>
</ul>
</section>

<section class="appendix informative" id="changes-12">
<h2>Changes between SHACL 1.0 SPARQL and SHACL 1.2 SPARQL Extensions</h2>
<ul>
<li>Added the <a>node expression function</a> <a href="#SelectExpression"><code>sh:SelectExpression</code></a>, see <a href="https://github.com/w3c/data-shapes/issues/288">Issue 288</a></li>
</ul>
</section>

</body>

Expand Down
1 change: 1 addition & 0 deletions shacl12-test-suite/tests/sparql/manifest.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
mf:include <node/manifest.ttl> ;
mf:include <property/manifest.ttl> ;
mf:include <pre-binding/manifest.ttl> ;
mf:include <targets/manifest.ttl> ;
.
1 change: 1 addition & 0 deletions shacl12-test-suite/tests/sparql/property/manifest.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
<>
a mf:Manifest ;
rdfs:label "Tests converted from http://datashapes.org/sh/tests/tests/sparql/property" ;
mf:include <property-select-001.ttl> ;
mf:include <sparql-001.ttl> ;
.
82 changes: 82 additions & 0 deletions shacl12-test-suite/tests/sparql/property/property-select-001.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://example.org/ns#> .
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@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 sht: <http://www.w3.org/ns/shacl-test#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:Person-fullName
a sh:PropertyShape ;
sh:targetClass ex:Person ;
sh:name "full name" ;
sh:path [
sh:prefixes <http://example.org/ns> ;
sh:select """
SELECT ?fullName
WHERE {
$this ex:firstName ?firstName .
$this ex:lastName ?lastName .
BIND (CONCAT(?firstName, " ", ?lastName) AS ?fullName) .
}
"""
] ;
sh:datatype xsd:string ;
sh:hasValue "John Muir" .

<http://example.org/ns>
a owl:Ontology ;
sh:declare [
sh:prefix "ex" ;
sh:namespace "http://example.org/ns#"^^xsd:anyURI ;
] .

ex:JohnMuir
a ex:Person ;
ex:firstName "John" ;
ex:lastName "Muir" .

ex:JohnWayne
a ex:Person ;
ex:firstName "John" ;
ex:lastName "Wayne" .

<>
rdf:type mf:Manifest ;
mf:entries (
<property-select-001>
) ;
.
<property-select-001>
rdf:type sht:Validate ;
rdfs:label "Test of a sh:property with a sh:select expression 001" ;
mf:action [
sht:dataGraph <> ;
sht:shapesGraph <> ;
] ;
mf:result [
rdf:type sh:ValidationReport ;
sh:conforms "false"^^xsd:boolean ;
sh:result [
rdf:type sh:ValidationResult ;
sh:focusNode ex:JohnWayne ;
sh:resultPath [
sh:prefixes <http://example.org/ns> ;
sh:select """
SELECT ?fullName
WHERE {
$this ex:firstName ?firstName .
$this ex:lastName ?lastName .
BIND (CONCAT(?firstName, " ", ?lastName) AS ?fullName) .
}
"""
] ;
sh:resultSeverity sh:Violation ;
sh:sourceConstraintComponent sh:HasValueConstraintComponent ;
sh:sourceShape ex:Person-fullName ;
] ;
] ;
mf:status sht:approved ;
.
9 changes: 9 additions & 0 deletions shacl12-test-suite/tests/sparql/targets/manifest.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sht: <http://www.w3.org/ns/shacl-test#> .

<>
a mf:Manifest ;
rdfs:label "Tests for SPARQL-based targets" ;
mf:include <targetNode-select-001.ttl> ;
.
73 changes: 73 additions & 0 deletions shacl12-test-suite/tests/sparql/targets/targetNode-select-001.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://example.org/ns#> .
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
@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 sht: <http://www.w3.org/ns/shacl-test#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

ex:ChildShape
a sh:NodeShape ;
rdfs:label "Child shape" ;
rdfs:comment "This shape applies to all persons under 18 years of age." ;
sh:targetNode [
sh:select """
PREFIX ex: <http://example.org/ns#>
SELECT ?person
WHERE {
?person a/rdfs:subClassOf* ex:Person .
?person ex:age ?age .
FILTER (?age < 18) .
}
"""
] ;
sh:property ex:ChildShape-driversLicense .

ex:ChildShape-driversLicense
a sh:PropertyShape ;
sh:path ex:driversLicense ;
sh:maxCount 0 .

ex:Benjamin
a ex:Person ;
ex:driversLicense "123" ;
ex:age 17 .

ex:Klaus
a ex:Person ;
ex:driversLicense "456" ;
ex:age 48 .

ex:Bernd
a ex:Person ;
ex:driversLicense "789" .

<>
rdf:type mf:Manifest ;
mf:entries (
<targetNode-select-001>
) ;
.
<targetNode-select-001>
rdf:type sht:Validate ;
rdfs:label "Test of sh:targetNode with a sh:select expression 001" ;
mf:action [
sht:dataGraph <> ;
sht:shapesGraph <> ;
] ;
mf:result [
rdf:type sh:ValidationReport ;
sh:conforms "false"^^xsd:boolean ;
sh:result [
rdf:type sh:ValidationResult ;
sh:focusNode ex:Benjamin ;
sh:resultPath ex:driversLicense ;
sh:resultSeverity sh:Violation ;
sh:sourceConstraintComponent sh:MaxCountConstraintComponent ;
sh:sourceShape ex:ChildShape-driversLicense ;
] ;
] ;
mf:status sht:approved ;
.
15 changes: 11 additions & 4 deletions shacl12-vocabularies/shacl-shacl.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,6 @@ shsh:ShapeShape
# Shapes are either node shapes or property shapes
sh:xone ( shsh:NodeShapeShape shsh:PropertyShapeShape ) ;

sh:property [
sh:path sh:targetNode ;
sh:nodeKind sh:IRIOrLiteral ; # targetNode-nodeKind
] ;
sh:property [
sh:path sh:targetClass ;
sh:nodeKind sh:IRI ; # targetClass-nodeKind
Expand Down Expand Up @@ -409,3 +405,14 @@ shsh:EntailmentShape
a sh:NodeShape ;
sh:targetObjectsOf sh:entailment ;
sh:nodeKind sh:IRI . # entailment-nodeKind

shsh:SelectExpressionShape
a sh:NodeShape ; # SelectExpression-syntax
sh:targetClass sh:SelectExpression ;
sh:targetSubjectsOf sh:select ;
sh:nodeKind sh:BlankNode ;
sh:property [
sh:datatype xsd:string ;
sh:maxCount 1 ;
sh:path sh:select ;
] .
Loading