Skip to content

#300: Added support for RDF 1.2 triple terms #368

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

Merged
merged 8 commits into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
94 changes: 94 additions & 0 deletions shacl12-core/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ <h3>Terminology</h3>
<dfn data-cite="rdf12-concepts#dfn-literal" data-lt="literal|literals">literal</dfn>,
<dfn data-cite="rdf12-concepts#dfn-datatype" data-lt="datatype">datatype</dfn>,
<dfn data-cite="rdf12-concepts#dfn-blank-node" data-lt="blank node|blank nodes">blank node</dfn>,
<dfn data-cite="rdf12-concepts#dfn-triple-term" data-lt="triple term|triple terms">triple term</dfn>,
<dfn data-cite="rdf12-concepts#dfn-reifier" data-lt="reifier">reifier</dfn>,
<dfn data-cite="rdf12-concepts#dfn-node" data-lt="node|nodes">node</dfn> of an RDF graph,
<dfn data-cite="rdf12-concepts#dfn-rdf-term" data-lt="term|terms">RDF term</dfn>,
<dfn data-cite="rdf12-concepts#dfn-subject" data-lt="subject|subjects">subject</dfn>,
Expand Down Expand Up @@ -5683,6 +5685,98 @@ <h4>sh:qualifiedValueShape, sh:qualifiedMinCount, sh:qualifiedMaxCount</h4>
</div>
</aside>
</section>

<section id="ReifiableByShapeConstraintComponent">
<h4>sh:reifiableBy, sh:reificationRequired</h4>
<p>
<code>sh:reifiableBy</code> can be used to link a <a>property shape</a> with one or more <a>node shapes</a>.
Any reified statement must conform to these node shapes.
Copy link
Contributor

@afs afs Apr 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{| |} is surface syntax for the triples.

I think SHACL needs to define validation for the basic forms then work up from there. This PR seems to be for an annotation usage pattern but that isn't the only way to use RDF 1.2.

Starting with:

ex:ValidResource1
  ex:propertyA "valid" {|
      ex:propertyB true ;
  |} .

{| |} is syntax to both reify a triple and provide triple about the reification. It is only syntax and is not retained in the RDF graph.

ex:ValidResource1  ex:propertyA  "valid" .
<< ex:ValidResource1 ex:propertyA "valid" >> ex:propertyB  true .

and << >> (an occurence - a use of a triple term) is in turn surface syntax for rdf:reifies
so the whole thing three triples:

ex:ValidResource1  ex:propertyA  "valid" .
_:b0    rdf:reifies   <<( ex:ValidResource1 ex:propertyA "valid" )>>;
        ex:propertyB  true .

<<( ... )>> is a triple term.

I think the intent is to apply a shape to the structure and also the triples insided {| |}.
Is that right?

Only Turtle/TriG have this annotation syntax.

Data does not have to use {| |} and it does not exist graph / triple store.

Sometimes, there may not be an asserted triple:

<< :s :p :o ~ _:reif1 >> :added "2025-04-01" .
<< :s :p :o ~ _:reif1 >> :deleted "2025-05-01" .

which is 3 triples:

_:b0    rdf:reifies  <<( :s :p :o )>>;
        :added       "2025-04-01" .
        rdf:reifies  <<( :s :p :o )>>; ## Duplicate - same as the first triple.
        :deleted     "2025-05-01" .

Note the shared blank node.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's for the use case where an asserted triple can be used to traverse into the triple term. The text definition says that the reifier will become the focus node for that validation.

Based on the discussion in #300, I thought we don't need to cover the use case without asserted triples. Node Expressions, or SPARQL-based targets can be used for that use case.

</p>

<div class="parameters">Parameters:</div>
<table class="term-table">
<tr>
<th>Property</th>
<th>Summary and Syntax Rules</th>
</tr>
<tr>
<td><code>sh:reifiableBy</code></td>
<td>
The <a>node shape</a> that the reified statement must conform to.
<span data-syntax-rule="reifiableBy-node">The values of <code>sh:reifiableBy</code> must be <a>well-formed</a> <a>node shapes</a>.</span>
</td>
</tr>
<tr>
<td><code>sh:reificationRequired</code></td>
<td>
This is an <a>optional parameter</a> of <code>sh:ReifiableByConstraintComponent</code>.
If set to <code>true</code> there must be at least one reification value for the focus node/path combination in the <a>data graph</a>.
<span data-syntax-rule="reificationRequired-datatype">The values of <code>sh:reificationRequired</code> in a shape are literals with datatype <code>xsd:boolean</code>.</span>
</td>
</tr>
</table>

<div class="def def-text">
<div class="def-header">TEXTUAL DEFINITION</div>
<div class="def-text-body" data-validator="ReifiableBy">
Let <code>t</code> be the <a>triple terms</a> of the triples obtained from the last traversal step when following the <code>$path</code> <a>SHACL property path</a> of the <a>property shape</a> from the <a>focus node</a> to the <a>value node</a>.
For each reified statement for the <a>triple terms</a> <code>t</code>, a failure MUST be produced if the validation of the reified statement with the <a>reifier</a> as <a>focus node</a> against the <a>node shape</a> <code>$reifiableBy</code> produces a <a>failure</a>.
</div>
</div>

<div class="def def-text">
<div class="def-header">TEXTUAL DEFINITION of sh:reificationRequired</div>
<div class="def-text-body" data-validator="ReificationRequired">
If <code>$reificationRequired</code> is set to <code>true</code>, a failure MUST be produced when there is no reified statement for the <a>triple term</a> <code>t</code> in the <a>data graph</a>.
Copy link
Contributor

@afs afs Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be zero or more groups of triples

## Reifier, no triple with that as subject
:s :p :o1 ~:r .  
## Same as :s :p :o1 ~:r  {} . 
## Two reifiers, each with triples.
:s :p :o2 {| ex:signedOffBy "A" |}
          {| ex:signedOffBy "B" |} .

In this example, reificationRequired is more count-like. This includes 0 -- the first example above -- here the reifier is allocated for later use.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node Expressions, or SPARQL-based targets can be used for that use case.

How? SPARQL-based constraints can do many things but what would node expressions look like?

General observation: Pushing things onto node expressions may work technically but they aren't necessarily a friendly way to express things. If the WG decides to publish core as a REC ASAP, then core is then fixed and unchangeable. The WG needs to be certain that pushing out things out to phase2 actually ends up with the best outcome.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can handle that change in a separate PR. That's why I've moved the discussion back to the issue.

</div>
</div>

<aside class="example">
<div class="shapes-graph">
<div class="turtle">
ex:ProvenanceShape
a sh:NodeShape ;
sh:property [
sh:path ex:date ;
sh:datatype xsd:date ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path ex:author ;
sh:nodeKind sh:IRI ;
sh:maxCount 1 ;
] .

ex:PersonShape
a sh:NodeShape ;
sh:targetClass ex:Person ;
sh:property ex:PersonShape-age .

ex:PersonShape-age
a sh:PropertyShape ;
sh:path ex:age ;
sh:datatype xsd:integer ;
sh:maxCount 1 ;
sh:reifiableBy ex:ProvenanceShape ;
sh:reificationRequired true .
</div>
<div class="jsonld">
<pre class="jsonld"></pre>
</div>
</div>
<div class="data-graph">
<div class="turtle">
ex:Bob ex:age 23 {|
ex:date "2019-12-05"^^xsd:date .
ex:author ex:Claire
|}.
</div>
<div class="jsonld">
<pre class="jsonld"></pre>
</div>
</div>
</aside>
</section>
</section>

<section id="core-components-others">
Expand Down
2 changes: 2 additions & 0 deletions shacl12-test-suite/tests/core/node/manifest.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
mf:include <or-001.ttl> ;
mf:include <pattern-001.ttl> ;
mf:include <pattern-002.ttl> ;
mf:include <reifiableBy-001.ttl> ;
mf:include <reifiableBy-002.ttl> ;
mf:include <xone-001.ttl> ;
mf:include <xone-duplicate.ttl> ;
mf:include <qualified-001.ttl> ;
Expand Down
66 changes: 66 additions & 0 deletions shacl12-test-suite/tests/core/node/reifiableBy-001.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://datashapes.org/sh/tests/core/node/reifiableBy-001.test#> .
@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:InvalidResource1
ex:propertyA "invalid" {|
ex:properrtyB false ;
|} ;
.
ex:ReifyShape
rdf:type sh:NodeShape ;
sh:property ex:ReifyShape-propertyB ;
.
ex:ReifyShape-propertyB
sh:path ex:propertyB ;
sh:in ( true ) ;
.
ex:TestShape
rdf:type sh:NodeShape ;
rdfs:label "Test shape" ;
sh:property ex:TestShape-propertyA ;
sh:targetNode ex:InvalidResource1 ;
sh:targetNode ex:ValidResource1 ;
.
ex:TestShape-propertyA
sh:path ex:propertyA ;
sh:reifiableBy ex:ReifyShape ;
.
ex:ValidResource1
ex:propertyA "valid" {|
ex:properrtyB true ;
|} ;
.
<>
rdf:type mf:Manifest ;
mf:entries (
<reifiableBy-001>
) ;
.
<reifiableBy-001>
rdf:type sht:Validate ;
rdfs:label "Test of sh:reifiableBy 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:InvalidResource1 ;
sh:resultSeverity sh:Violation ;
sh:sourceConstraintComponent sh:ReifiableByConstraintComponent ;
sh:sourceShape ex:TestShape-propertyA ;
sh:value "invalid" ;
] ;
] ;
mf:status sht:approved ;
.
64 changes: 64 additions & 0 deletions shacl12-test-suite/tests/core/node/reifiableBy-002.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
@prefix dash: <http://datashapes.org/dash#> .
@prefix ex: <http://datashapes.org/sh/tests/core/node/reifiableBy-002.test#> .
@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:InvalidResource1
ex:propertyA "invalid" ;
.
ex:ReifyShape
rdf:type sh:NodeShape ;
sh:property ex:ReifyShape-propertyB ;
.
ex:ReifyShape-propertyB
sh:path ex:propertyB ;
.
ex:TestShape
rdf:type sh:NodeShape ;
rdfs:label "Test shape" ;
sh:property ex:TestShape-propertyA ;
sh:targetNode ex:InvalidResource1 ;
sh:targetNode ex:ValidResource1 ;
.
ex:TestShape-propertyA
sh:path ex:propertyA ;
sh:reifiableBy ex:ReifyShape ;
sh:reificationRequired true ;
.
ex:ValidResource1
ex:propertyA "valid" {|
ex:properrtyB true ;
|} ;
.
<>
rdf:type mf:Manifest ;
mf:entries (
<reifiableBy-002>
) ;
.
<reifiableBy-002>
rdf:type sht:Validate ;
rdfs:label "Test of sh:reifiableBy 002 with sh:reificationRequired" ;
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:InvalidResource1 ;
sh:resultSeverity sh:Violation ;
sh:sourceConstraintComponent sh:ReifiableByConstraintComponent ;
sh:sourceShape ex:TestShape-propertyA ;
sh:value "invalid" ;
] ;
] ;
mf:status sht:approved ;
.
34 changes: 34 additions & 0 deletions shacl12-vocabularies/shacl.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,40 @@ sh:qualifiedValueShapesDisjoint
rdfs:isDefinedBy sh: .


sh:ReifiableByConstraintComponent
a sh:ConstraintComponent ;
rdfs:label "ReifiableBy constraint component"@en ;
rdfs:comment "A constraint component that can be used to verify that reified triples conform to a given shape."@en ;
sh:parameter sh:ReifiableByConstraintComponent-reifiableBy ;
sh:parameter sh:ReifiableByConstraintComponent-reificationRequired ;
rdfs:isDefinedBy sh: .

sh:ReifiableByConstraintComponent-reifiableBy
a sh:Parameter ;
sh:path sh:reifiableBy ;
rdfs:isDefinedBy sh: .

sh:ReifiableByConstraintComponent-reificationRequired
a sh:Parameter ;
sh:path sh:reificationRequired ;
sh:datatype xsd:boolean ;
rdfs:isDefinedBy sh: .

sh:reifiableBy
a rdf:Property ;
rdfs:label "reifiable by"@en ;
rdfs:comment "The shape that the reified triples must conform to."@en ;
rdfs:range sh:NodeShape ;
rdfs:isDefinedBy sh: .

sh:reificationRequired
a rdf:Property ;
rdfs:label "reification required"@en ;
rdfs:comment "Can be used to mark the reification statements required."@en ;
rdfs:range xsd:boolean ;
rdfs:isDefinedBy sh: .


sh:SingleLineConstraintComponent
a sh:ConstraintComponent ;
rdfs:label "Single line constraint component"@en ;
Expand Down