Skip to content

Commit 8a7748c

Browse files
#288: Added sh:SelectExpression, with test cases and TTL changes
1 parent 630e1ba commit 8a7748c

File tree

8 files changed

+339
-1
lines changed

8 files changed

+339
-1
lines changed

shacl12-core/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1775,7 +1775,7 @@ <h3>Node Expressions</h3>
17751775
</p>
17761776
<aside class="example" title="A dynamically computed property using a node expression based on a SPARQL query">
17771777
<p>
1778-
Here is an example use of a node expression based on <a href="shacl12-sparql">SHACL-SPARQL</a>, computing
1778+
Here is an example use of a node expression based on <a data-cite="shacl12-sparql#SelectExpression">SHACL-SPARQL</a>, computing
17791779
the values of a property shape for the property "full name" as the concatenation of <code>ex:firstName</code>,
17801780
a space and the <code>ex:lastName</code>.
17811781
</p>

shacl12-sparql/index.html

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ <h3>Terminology</h3>
383383
<dfn data-cite="rdf12-concepts#dfn-literal" data-lt="literal|literals">literal</dfn>,
384384
<dfn data-cite="rdf12-concepts#dfn-blank-node" data-lt="blank node|blank nodes">blank node</dfn>,
385385
<dfn data-cite="rdf12-concepts#dfn-node" data-lt="node|nodes">node</dfn> of an RDF graph,
386+
<dfn data-cite="rdf12-concepts#dfn-datatype" data-lt="datatype|datatypes">datatype</dfn>,
386387
<dfn data-cite="rdf12-concepts#dfn-rdf-term" data-lt="term|terms">RDF term</dfn>, and
387388
<dfn data-cite="rdf12-concepts#dfn-subject" data-lt="subject|subjects">subject</dfn>,
388389
<dfn data-cite="rdf12-concepts#dfn-predicate" data-lt="predicate|predicates">predicate</dfn>, and
@@ -409,6 +410,11 @@ <h3>Terminology</h3>
409410
<dfn data-cite="shacl12-core#dfn-sparql-property-path" data-lt="sparql property path|sparql property paths">SPARQL property path</dfn>,
410411
<dfn data-cite="shacl12-core#dfn-shapes-graph" data-lt="shapes graph">shapes graph</dfn>,
411412
<dfn data-cite="shacl12-core#dfn-validators" data-lt="validator|validators">validator</dfn>,
413+
<dfn data-cite="shacl12-core#dfn-node-expression" data-lt="node expression|node expresssions">node expression</dfn>,
414+
<dfn data-cite="shacl12-core#dfn-node-expression-function" data-lt="node expression function|node expresssion functions">node expression function</dfn>,
415+
<dfn data-cite="shacl12-core#dfn-function-name" data-lt="node expression function name">function name</dfn>,
416+
<dfn data-cite="shacl12-core#dfn-key-parameter" data-lt="key parameter">key parameter</dfn>,
417+
<dfn data-cite="shacl12-core#dfn-output-nodes" data-lt="output nodes">output nodes</dfn>,
412418
<dfn data-cite="shacl12-core#dfn-conform" data-lt="conform|conforms">conform</dfn>,
413419
<dfn data-cite="shacl12-core#dfn-failure" data-lt="failure|failures">failure</dfn>,
414420
<dfn data-cite="shacl12-core#dfn-shacl-instance" data-lt="shacl instance">SHACL instance</dfn>,
@@ -1205,6 +1211,118 @@ <h3>Validation with SPARQL-based Constraint Components</h3>
12051211
</section>
12061212
</section>
12071213

1214+
<section id="sparql-node-expressions">
1215+
<h2>SPARQL-based Node Expressions</h2>
1216+
<p>
1217+
This section introduces <a>node expression functions</a> based on SPARQL.
1218+
</p>
1219+
1220+
<section id="SelectExpression">
1221+
<h3>Select Expressions</h3>
1222+
<p>
1223+
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>
1224+
<code>sh:SelectExpression</code>.
1225+
</p>
1226+
<p class="syntax">
1227+
<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>
1228+
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>
1229+
with <a>datatype</a> <code>xsd:string</code>.</span>
1230+
<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>
1231+
<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>
1232+
</p>
1233+
<div class="def" id="LiteralExpression-evaluation">
1234+
<div class="def-header">EVALUATION OF SELECT EXPRESSIONS</div>
1235+
<p>
1236+
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)
1237+
variable that is projected from the SELECT clause.
1238+
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>.
1239+
<br/>
1240+
<br/>
1241+
<code>eval(expr, activeGraph, scope) -> resultNodes</code>
1242+
</p>
1243+
</div>
1244+
<p><em>The remainder of this section is informative.</em></p>
1245+
<aside class="example" title="A dynamically computed property using a node expression based on a SPARQL query">
1246+
<p>
1247+
Here is an example use of a <a>select expression</a>, computing the values of a property shape for the property
1248+
"full name" as the concatenation of <code>ex:firstName</code>, a space and the <code>ex:lastName</code>.
1249+
</p>
1250+
<div class="shapes-graph">
1251+
<div class="turtle">
1252+
ex:Person-fullName
1253+
a sh:PropertyShape ;
1254+
sh:name "full name" ;
1255+
sh:path <b>[
1256+
sh:prefixes &lt;http://example.org/ns&gt; ;
1257+
sh:select """
1258+
SELECT ?fullName
1259+
WHERE {
1260+
$this ex:firstName ?firstName .
1261+
$this ex:lastName ?lastName .
1262+
BIND (CONCAT(?firstName, " ", ?lastName) AS ?fullName) .
1263+
}
1264+
"""
1265+
]</b> ;
1266+
sh:datatype xsd:string .
1267+
1268+
&lt;http://example.org/ns&gt;
1269+
a owl:Ontology ;
1270+
sh:declare [
1271+
sh:prefix "ex" ;
1272+
sh:namespace "http://example.org/ns#"^^xsd:anyURI ;
1273+
] .
1274+
</div>
1275+
</div>
1276+
<p>
1277+
This example also illustrates the use of <code>sh:prefixes</code> to insert PREFIX declarations into the beginning of the query before parsing.
1278+
Note that the query is executed with the current <a>focus node</a> <a>pre-bound</a> to the variable <code>this</code>.
1279+
</p>
1280+
</aside>
1281+
<aside class="example" title="Dynamically computed target nodes using a node expression based on a SPARQL query">
1282+
<p>
1283+
Here is an example use of a <a>select expression</a>, computing the target nodes of a shape to consist of all instances of
1284+
<code>ex:Person</code> where the <code>ex:age</code> is less than <code>18</code>.
1285+
</p>
1286+
<div class="shapes-graph">
1287+
<div class="turtle">
1288+
ex:ChildShape
1289+
a sh:NodeShape ;
1290+
rdfs:label "Child shape" ;
1291+
rdfs:comment "This shape applies to all persons under 18 years of age." ;
1292+
sh:targetNode <b>[
1293+
sh:select """
1294+
PREFIX ex: &lt;http://example.org/ns#&gt;
1295+
SELECT ?person
1296+
WHERE {
1297+
?person a/rdfs:subClassOf* ex:Person .
1298+
?person ex:age ?age .
1299+
FILTER (?age &lt; 18) .
1300+
}
1301+
"""
1302+
]</b> .
1303+
</div>
1304+
</div>
1305+
<p>
1306+
From the following data graph, only <code>ex:Benjamin</code> is a target node.
1307+
</p>
1308+
<div class="data-graph">
1309+
<div class="turtle">
1310+
<span class="focus-node-selected">ex:Benjamin</span>
1311+
a ex:Person ;
1312+
ex:age 17 .
1313+
1314+
ex:Klaus
1315+
a ex:Person ;
1316+
ex:age 48 .
1317+
1318+
ex:Bernd
1319+
a ex:Person .
1320+
</div>
1321+
</div>
1322+
</aside>
1323+
</section>
1324+
</section>
1325+
12081326
<div style="padding-top: 30px">
12091327
<h1 id="appendix" style="font-size: 160%; font-weight: bold">Appendix</h1>
12101328
</div>
@@ -1317,6 +1435,13 @@ <h2>Revision History</h2>
13171435
<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>
13181436
</ul>
13191437
</section>
1438+
1439+
<section class="appendix informative" id="changes-12">
1440+
<h2>Changes between SHACL 1.0 SPARQL and SHACL 1.2 SPARQL Extensions</h2>
1441+
<ul>
1442+
<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>
1443+
</ul>
1444+
</section>
13201445

13211446
</body>
13221447

shacl12-test-suite/tests/sparql/manifest.ttl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@
88
mf:include <node/manifest.ttl> ;
99
mf:include <property/manifest.ttl> ;
1010
mf:include <pre-binding/manifest.ttl> ;
11+
mf:include <targets/manifest.ttl> ;
1112
.

shacl12-test-suite/tests/sparql/property/manifest.ttl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
<>
66
a mf:Manifest ;
77
rdfs:label "Tests converted from http://datashapes.org/sh/tests/tests/sparql/property" ;
8+
mf:include <property-select-001.ttl> ;
89
mf:include <sparql-001.ttl> ;
910
.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
@prefix dash: <http://datashapes.org/dash#> .
2+
@prefix ex: <http://example.org/ns#> .
3+
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
4+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
5+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
6+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7+
@prefix sh: <http://www.w3.org/ns/shacl#> .
8+
@prefix sht: <http://www.w3.org/ns/shacl-test#> .
9+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
10+
11+
ex:Person-fullName
12+
a sh:PropertyShape ;
13+
sh:targetClass ex:Person ;
14+
sh:name "full name" ;
15+
sh:path [
16+
sh:prefixes <http://example.org/ns> ;
17+
sh:select """
18+
SELECT ?fullName
19+
WHERE {
20+
$this ex:firstName ?firstName .
21+
$this ex:lastName ?lastName .
22+
BIND (CONCAT(?firstName, " ", ?lastName) AS ?fullName) .
23+
}
24+
"""
25+
] ;
26+
sh:datatype xsd:string ;
27+
sh:hasValue "John Muir" .
28+
29+
<http://example.org/ns>
30+
a owl:Ontology ;
31+
sh:declare [
32+
sh:prefix "ex" ;
33+
sh:namespace "http://example.org/ns#"^^xsd:anyURI ;
34+
] .
35+
36+
ex:JohnMuir
37+
a ex:Person ;
38+
ex:firstName "John" ;
39+
ex:lastName "Muir" .
40+
41+
ex:JohnWayne
42+
a ex:Person ;
43+
ex:firstName "John" ;
44+
ex:lastName "Wayne" .
45+
46+
<>
47+
rdf:type mf:Manifest ;
48+
mf:entries (
49+
<property-select-001>
50+
) ;
51+
.
52+
<property-select-001>
53+
rdf:type sht:Validate ;
54+
rdfs:label "Test of a sh:property with a sh:select expression 001" ;
55+
mf:action [
56+
sht:dataGraph <> ;
57+
sht:shapesGraph <> ;
58+
] ;
59+
mf:result [
60+
rdf:type sh:ValidationReport ;
61+
sh:conforms "false"^^xsd:boolean ;
62+
sh:result [
63+
rdf:type sh:ValidationResult ;
64+
sh:focusNode ex:JohnWayne ;
65+
sh:resultPath [
66+
sh:prefixes <http://example.org/ns> ;
67+
sh:select """
68+
SELECT ?fullName
69+
WHERE {
70+
$this ex:firstName ?firstName .
71+
$this ex:lastName ?lastName .
72+
BIND (CONCAT(?firstName, " ", ?lastName) AS ?fullName) .
73+
}
74+
"""
75+
] ;
76+
sh:resultSeverity sh:Violation ;
77+
sh:sourceConstraintComponent sh:HasValueConstraintComponent ;
78+
sh:sourceShape ex:Person-fullName ;
79+
] ;
80+
] ;
81+
mf:status sht:approved ;
82+
.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
2+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
3+
@prefix sht: <http://www.w3.org/ns/shacl-test#> .
4+
5+
<>
6+
a mf:Manifest ;
7+
rdfs:label "Tests for SPARQL-based targets" ;
8+
mf:include <targetNode-select-001.ttl> ;
9+
.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
@prefix dash: <http://datashapes.org/dash#> .
2+
@prefix ex: <http://example.org/ns#> .
3+
@prefix mf: <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
4+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
5+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
6+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7+
@prefix sh: <http://www.w3.org/ns/shacl#> .
8+
@prefix sht: <http://www.w3.org/ns/shacl-test#> .
9+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
10+
11+
ex:ChildShape
12+
a sh:NodeShape ;
13+
rdfs:label "Child shape" ;
14+
rdfs:comment "This shape applies to all persons under 18 years of age." ;
15+
sh:targetNode [
16+
sh:select """
17+
PREFIX ex: <http://example.org/ns#>
18+
SELECT ?person
19+
WHERE {
20+
?person a/rdfs:subClassOf* ex:Person .
21+
?person ex:age ?age .
22+
FILTER (?age < 18) .
23+
}
24+
"""
25+
] ;
26+
sh:property ex:ChildShape-driversLicense .
27+
28+
ex:ChildShape-driversLicense
29+
a sh:PropertyShape ;
30+
sh:path ex:driversLicense ;
31+
sh:maxCount 0 .
32+
33+
ex:Benjamin
34+
a ex:Person ;
35+
ex:driversLicense "123" ;
36+
ex:age 17 .
37+
38+
ex:Klaus
39+
a ex:Person ;
40+
ex:driversLicense "456" ;
41+
ex:age 48 .
42+
43+
ex:Bernd
44+
a ex:Person ;
45+
ex:driversLicense "789" .
46+
47+
<>
48+
rdf:type mf:Manifest ;
49+
mf:entries (
50+
<targetNode-select-001>
51+
) ;
52+
.
53+
<targetNode-select-001>
54+
rdf:type sht:Validate ;
55+
rdfs:label "Test of sh:targetNode with a sh:select expression 001" ;
56+
mf:action [
57+
sht:dataGraph <> ;
58+
sht:shapesGraph <> ;
59+
] ;
60+
mf:result [
61+
rdf:type sh:ValidationReport ;
62+
sh:conforms "false"^^xsd:boolean ;
63+
sh:result [
64+
rdf:type sh:ValidationResult ;
65+
sh:focusNode ex:Benjamin ;
66+
sh:resultPath ex:driversLicense ;
67+
sh:resultSeverity sh:Violation ;
68+
sh:sourceConstraintComponent sh:MaxCountConstraintComponent ;
69+
sh:sourceShape ex:ChildShape-driversLicense ;
70+
] ;
71+
] ;
72+
mf:status sht:approved ;
73+
.

0 commit comments

Comments
 (0)