The ShEx SHACL-SPARQL extension,
http://shex.io/extensions/SHACL-SPARQL/, runs
SHACL-SPARQL
queries as semantic actions. A query means what it means in
SHACL-SPARQL — a SELECT reports one violation per
solution, an ASK passes when it answers true, and
$this and $value are pre-bound — so a query
written for a sh:SPARQLConstraint or a
sh:SPARQLAskValidator can be used as it is. What
surrounds it is ShEx: recursive shapes, CLOSED over
triple expressions, and actions that take part in deciding which
triple matches which constraint rather than being reported after
the fact.
Events and the parts they're made of must each end no earlier than they start:
PREFIX : <http://a.example/> PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> PREFIX shacl-sparql: <http://shex.io/extensions/SHACL-SPARQL/> :Event { :start xsd:date ; :end xsd:date ; :part @:Event * } %shacl-sparql:{ SELECT $this ?start ?end ("the event ends before it starts" AS ?message) WHERE { $this :start ?start ; :end ?end . FILTER (?end < ?start) } %}
PREFIX : <http://a.example/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
:conf :start "2026-10-05"^^xsd:date ;
:end "2026-10-07"^^xsd:date ;
:part :keynote .
:keynote :start "2026-10-05"^^xsd:date ;
:end "2026-10-04"^^xsd:date .
:conf doesn't conform to :Event. Its own
dates pass, but :keynote, reached through the recursive
:part @:Event, fails the action with the message
"the event ends before it starts", and so the
:part triple has no constraint to satisfy.
ShEx leaves the evaluation of a semantic action to its extension and
defines what a failure means
(semActsSatisfied).
For an action named http://shex.io/extensions/SHACL-SPARQL/,
the evaluation is:
$this and
$value, according to where the action is.
The code is parsed as a
SPARQL 1.1
Query. The schema's PREFIX and
BASE declarations are in scope, as if they were the
query's prologue: a query needs no prefix declarations of its own,
which is the job sh:prefixes does in SHACL. A
declaration in the query's own prologue takes precedence.
The query has to be a SELECT or an ASK.
A query that doesn't parse, a CONSTRUCT or a
DESCRIBE, or an action with no code (and no code
supplied from outside the schema) is an
invocation error: the validation stops with an error
rather than reporting a failure.
ShExC reserves % and \ in code blocks,
so a query writes them \% and \\; the
parser unescapes them before the extension sees the query.
$this and $valueWhich variables are pre-bound depends on the schema element the action is attached to:
| the action is on | $this | $value |
|---|---|---|
| a Shape | the focus node | — |
| a NodeConstraint | the node being tested | — |
| a TripleConstraint | the focus node: the matched triple's subject, or its object for an inverse (^) constraint | the other end of the matched triple |
| an EachOf or OneOf | the node every triple it matched shares | — |
| the schema's start actions | — | — |
In ShExC an action written after a triple constraint's value
expression belongs to the triple constraint
(:v xsd:integer %shacl-sparql:{…%} binds
$this to the subject and $value to the
object). To put an action on the value's own node constraint,
label it (:v @:Positive …
:Positive xsd:integer %shacl-sparql:{…%}).
Pre-binding follows
SHACL's definition:
each occurrence of a pre-bound variable in the query stands for
its term. In SPARQL $this and ?this are
the same variable. A pre-bound variable in a SELECT's
projection is still projected, with its pre-bound value.
The query is evaluated over the data the validation reads, not only the neighborhood of the node being validated. A query can look anywhere in that data: "no other book has this ISBN" is a question about the other books.
GRAPH.
The answer depends only on the query, the pre-bound terms and the data, so an implementation may ask each distinct question once per validation.
| query | succeeds when | otherwise | as in SHACL |
|---|---|---|---|
SELECT |
there are no solutions | fails, with one error per solution | sh:SPARQLConstraint |
ASK |
it answers true |
fails, with one error | sh:SPARQLAskValidator |
A solution's ?message, where it binds one, is that
error's message; otherwise the message names the extension and
lists the solution's bindings. An implementation should report
the solution's bindings with the error, as SHACL reports
?value and ?path in a validation result.
An expression error inside the query (comparing a date with an
IRI, say) is SPARQL's business: a FILTER whose
expression errs rejects the solution, as SPARQL defines it. It
isn't an invocation error.
A failure is a semantic-action failure as ShEx defines one, and it means what that means where the action is:
:Term {
:label . %shacl-sparql:{ ASK { FILTER (lang($value) = "de") } %} ;
:label . %shacl-sparql:{ ASK { FILTER (lang($value) = "en") } %}
}
A node with labels "Hund"@de and
"dog"@en conforms: each label goes to the
constraint whose query accepts it. In a CLOSED
shape, a triple that no constraint accepts fails the shape.
$this and $value are pre-bound.
$currentShape and $shapesGraph are not:
a ShEx schema isn't an RDF graph a query could ask about.
sh:prefixes.
?message is used as it is. SHACL's
{?var} and {$var} message templates are
not substituted.
$PATH substitution: each action carries its own
query.
validateShapeMapAsync. Its
examples
load in the shex.js web app.