Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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