This document is currently not maintained and is out of date. For the latest specification, see SHACL Specification.
SHACL (Shapes Constraint Language) is an RDF vocabulary for describing RDF graph structures. These graph structures are captured as "shapes", which are expected to correspond to nodes in RDF graphs. These shapes identify predicates and their associated cardinalities and datatypes. Additional constraints can be associated with shapes using SPARQL or other languages which complement SHACL. SHACL shapes can be used to communicate data structures associated with some process or interface, generate or validate data, or drive user interfaces.
This document introduces SHACL to people familiar with basic concepts of RDF and SPARQL. This primer develops examples from an issue tracking application to introduce all major features of SHACL, including the construction of shapes and their correspondence with RDF graphs. This primer is a possible companion to the langauge specifation that serves as a language reference and full specification.
Most applications that share data do so using prescribed data structures. While RDFS and OWL enable one to make assertions about the objects in some domain, SHACL (Shapes Constraint Language) describes data structures. Features of SHACL include:
SHACL is based on RDF and is compatible with Linked Data principles, making it possible to mix SHACL shapes with other semantic web data.
SHACL definitions are represented in RDF and can be serialized in multiple RDF formats. The example snippets in this document use Turtle [[turtle]] and JSON-LD [[json-ld]] notation. the reader should be familiar with basic RDF concepts [[rdf11-concepts]] such as triples and (for the advanced concepts of SHACL) with SPARQL [[sparql11-overview]].
This document uses a running example illustrated by the following graph in which an Issue which is submitted by some person and potentially assigned to the same person or someone else.
These issues can have a status of unassigned
or assigned
.
The rest of this document shows shapes which describe the issue data consumable by some application, including the submitter and the person to which the issue is assigned. Examples include data, both conforming and not. Some mechanisms by which the instance data may be associated with, or validated against the a shape are descibed later in Associating Data with Shapes.
The SHACL system vocabulary is defined in itself, using its own SHACL File. This file also includes in-line comments for open issues as well as many technical details that are omitted in this primer for space considerations.
Within this document, the following namespace prefix bindings are used:
Prefix | Namespace |
---|---|
sh: | http://www.w3.org/ns/shacl# |
rdf: | http://www.w3.org/1999/02/22-rdf-syntax-ns# |
xsd: | http://www.w3.org/2001/XMLSchema# |
ex: | http://example.com/ns# |
RDF has several syntaxes speciallized for different purposes. In principle, any of those can be used to convey constraints or data. This Primer is available in Turtle and JSON-LD.
SHACL's facilities for describing the structure of graphs are based on rules that describe triples in a graph.
These rules are evaluated against particular nodes in the instance data; these are called focus nodes
.
Each rule identifies a predicate and describes the expected triples with the focus node
as a subject and the corresponding predicate.
These descriptions include how many such triples are required/permitted, and what kinds of objects are expected.
The rules can be written in Turtle or JSON-LD (recall that you can press 't' or 'j') as a set of properties on a property constraint
.
In this example, the sh:predicate property identifies the predicate, foaf:name, to be matched on the focus node.
The sh:valueType here specifies that the matching triples must have an object with is an RDF literal with a datatype of xsd:string.
The last two triples say that there should be exactly one because the sh:minCount and sh:maxCount, also known as the minimum cardinality
and maximum cardinality
, are both 1.
In summary, we execpt that the focus node
will have exactly one foaf:name property and the object will be a literal with a datatype xsd:string.
If we validate the nodes inst:User1, inst:User2 and inst:User3 below, we see that the first passes this requirement.
inst:User2 has the wrong datatype and inst:User3 has too many foaf:name properties:
# property constraint (Turtle) [ sh:predicate foaf:name ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 ] .
// shapes (JSON-LD) { "sh:predicate": { "@id": "foaf:name" }, "sh:valueType": { "@id": "xsd:string" }, "sh:maxCount": 1, "sh:minCount": 1 }
# passing data inst:User1 foaf:name "Bob Smith" .
# failing data inst:User2 foaf:name "Bob Smith"^^xsd:anyURI . inst:User3 foaf:name "Joe Jones" ; foaf:name "Joseph Jones" .
Above is an example of a property constraint
with a value type
constraint.
Property constraints
can include one of the following constraints:
value type
: identifies the datatype of an RDF literal.node kind
: identifies the kind of RDF node (IRI, RDF literal, or Blank Node).allowed value
: enumerates specific expected values.value shape
: asserts that the object is described by another shape (described below).
Property constraints
attached to a construct called a shape
which enumerates a useful set of constraints to be applied to a focus node
.
Property constraints
can be attached indirectly through logical constructs for AND and OR, but here we introduce a simple form where they are attached directly to the shape
with the sh:property.
To illustrate this, we can invent some rules for our issue example.
We can describe the shape
of our example issue by identifying the triples which are part of an issue.
(Mousing over the the following list items items will highlight the corresponding elements in the example text.)
Our first example issue must doesn't include ex:assignedTo as illustrated in the image above, but it must have:
allowed value
,value shape
referenceing some user shape.The user referenced by the ex:reportedBy property must have:
value type
,node kind
.# shapes (Turtle) my:IssueShape a sh:Shape ; sh:property [ sh:predicate ex:state ;sh:allowedValue (ex:unassigned ex:assigned) ;sh:allowedValue ex:unassigned , ex:assigned ;sh:minCount 1 ; sh:maxCount 1 ] ; sh:property [ sh:predicate ex:reportedBy ; sh:valueShape my:UserShape ; sh:minCount 1 ; sh:maxCount 1 ] . my:UserShape a sh:Shape ; sh:property [ sh:predicate foaf:name ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:property [ sh:predicate foaf:mbox ; sh:nodeKind sh:IRI ; sh:minCount 1 ] .
// shapes (JSON-LD) { "@context": { "ex": "http://example.com/ns/ex#", "foaf": "http://xmlns.com/foaf/", "sh": "http://example.com/ns/sh#", "xsd": "http://www.w3.org/2001/XMLSchema#" }, "@graph": [ { "@id": "my:IssueShape", "@type": "sh:Shape", "sh:property": [ { "sh:predicate": { "@id": "ex:state" },"sh:allowedValue": { "@list": [ { "@id": "ex:unassigned" }, { "@id": "ex:assigned" } ] },"sh:allowedValue": [ { "@id": "ex:unassigned" }, { "@id": "ex:assigned" } ],"sh:minCount": 1, "sh:maxCount": 1 }, { "sh:predicate": { "@id": "ex:reportedBy" }, "sh:valueShape": { "@id": "my:UserShape" }, "sh:minCount": 1, "sh:maxCount": 1 } ] }, { "@id": "my:UserShape", "@type": "sh:Shape", "sh:property": [ { "sh:predicate": { "@id": "foaf:name" }, "sh:valueType": { "@id": "xsd:string" }, "sh:minCount": 1, "sh:maxCount": 1 }, { "sh:predicate": { "@id": "foaf:mbox" }, "sh:nodeKind": { "@id": "sh:IRI" }, "sh:minCount": 1 } ] } ] }
# passing data PREFIX ex: http://ex.example/# PREFIX foaf: http://xmlns.com/foaf/ PREFIX xsd: http://www.w3.org/2001/XMLSchema# inst:Issue1 a ex:Issue ; ex:state ex:unassigned ; ex:reportedBy inst:User2 . inst:User2 a foaf:Person ; foaf:name "Bob Smith" ; foaf:mbox <mailto:bob@example.org> ; foaf:mbox <mailto:rs@example.org> .
# failing data PREFIX ex: http://ex.example/# PREFIX foaf: http://xmlns.com/foaf/ PREFIX xsd: http://www.w3.org/2001/XMLSchema# inst:Issue3 a ex:Issue ; ex:state ex:unsinged ; ex:reportedBy inst:User4 . inst:User4 a foaf:Person ; foaf:name "Bob Smith", "Robert Smith" ; foaf:mbox <mailto:bob@example.org> ; foaf:mbox <mailto:rs@example.org> .
In the top example, inst:Issue1 conforms to my:IssueShape because every predicate identified in my:IssueShape corresponds to a triple with a subject of inst:Issue1.
There are two proposed forms for enumerations: (The form below should be in this issue block.)
Shapes my extend other shapes by referencing them with the sh:extends
property.
The property sh:abstract
that can be set to true
for shapes that serve only as building blocks for more complex shapes.
Furthermore, shapes may be marked sh:private
to indicate that they
are only meant to be used inside of the graph that they have been defined in.
This can be used to discourage instantiation outside of controlled scenarios.
The above example shows how property constraints
identify predicates and their associated cardinalities and object constraints.
The following sections describe more attributes of of property constraints
.
It is common (in UML etc) to specify the name of a property and its cardinality (min/max value count) as well as the value type. In SHACL, these declarations can be attached to a class as follows:
# shapes (Turtle) my:IssueShape a sh:Shape ; sh:property [ a sh:PropertyConstraint ; # This type declaration is optional rdfs:label "state" ; rdfs:comment "whether this shape has been assigned to someone." ; sh:predicate ex:state ; sh:allowedValue ex:unassigned , ex:assigned ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:property [ a sh:PropertyConstraint ; rdfs:label "reported by" ; rdfs:comment "who submitted a report about this bug." ; sh:predicate ex:reportedBy ; sh:valueShape my:UserShape ; sh:minCount 1 ; sh:maxCount 1 ] .
// shapes (JSON-LD) { "@graph": [ { "@id": "my:IssueShape", "@type": "sh:Shape", "sh:property": [ { "@type": "sh:PropertyConstraint", # This type declaration is optional "rdfs:label": "state", "rdfs:comment": "whether this shape has been assigned to someone.", "sh:predicate": { "@id": "ex:state" }, "sh:allowedValue": [ { "@id": "ex:unassigned" }, { "@id": "ex:assigned" } ], "sh:minCount": 1, "sh:maxCount": 1 }, { "@type": "sh:PropertyConstraint", "rdfs:label": "reported by", "rdfs:comment": "who submitted a report about this bug.", "sh:predicate": { "@id": "ex:reportedBy" }, "sh:valueShape": { "@id": "my:UserShape" }, "sh:minCount": 1, "sh:maxCount": 1 } ] } ] }
Tools that understand SHACL can use the information above for many purposes. For example, they can figure out which fields should appear for an input form, and what values those fields can have, as well as labels and comments. Furthermore, they can verify that a given node in a graph fulfills the requirements for a my:IssueShape.
The system property sh:property
is used to link a shape with its
property declarations.
The values of this property are nodes with the following properties:
Property | Description |
---|---|
sh:predicate |
Required. Must point to the URI of an RDF property used to store the values of the property. |
sh:minCount |
Type: integer >= 0, the minimum number of values required. Defaults to 0. |
sh:maxCount |
Type: integer >= 0, the maximum number of values required. Defaults to unlimited. |
sh:valueShape |
The object matches the refrenced sh:Shape .
|
sh:valueType |
identifies the datatype for an RDF literal. |
sh:allowedValues |
Type: sh:Set (with property sh:member ), points to a set enumerating the allowed values. |
sh:allowedValue |
Type: the same as the sh:valueType of the surrounding property. |
rdfs:label |
A human-readable label of the property in the context of the associated class. |
rdfs:comment |
A description of the property's role in the context of the associated class. |
We can extend our first example to include a more flexible user which includes given and family names.
In this example, a user has:
The user must have exactly one foaf:mbox.
# shapes (Turtle) my:UserShape a sh:Shape ; sh:choice [ sh:property [ sh:predicate foaf:name ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:propertyGroup [ sh:property [ sh:predicate foaf:givenName ; sh:valueType xsd:string ; sh:minCount 1 ] ; sh:property [ sh:predicate foaf:familyName ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 ] ] ] ; sh:property [ sh:predicate foaf:mbox ; sh:nodeKind sh:IRI ; sh:minCount 1 ] .
// shapes (JSON-LD) { "@graph": [ { "@id": "my:UserShape", "@type": "sh:Shape", "sh:choice": { "sh:property": { "sh:predicate": { "@id": "foaf:name" }, "sh:valueType": { "@id": "xsd:string" }, "sh:maxCount": 1, "sh:minCount": 1 }, "sh:propertyGroup": { "sh:property": [ { "sh:predicate": { "@id": "foaf:givenName" }, "sh:valueType": { "@id": "xsd:string" }, "sh:minCount": 1 }, { "sh:predicate": { "@id": "foaf:familyName" }, "sh:valueType": { "@id": "xsd:string" }, "sh:maxCount": 1, "sh:minCount": 1 } ] } }, "sh:property": { "sh:predicate": { "@id": "foaf:mbox" }, "sh:nodeKind": { "@id": "sh:IRI" }, "sh:minCount": 1 } } ] }
# passing data inst:User1 a foaf:Person ; foaf:name "Alice Walker" ; foaf:mbox <mailto:awalker@example.org> . inst:User2 a foaf:Person ; foaf:givenName "Robert" ; foaf:givenName "Paris" ; foaf:familyName "Moses" ; foaf:mbox <mailto:rpmoses@example.org> .
# failing data inst:User3 a foaf:Person ; foaf:givenName "Smith" ; # error: no familyName foaf:mbox <mailto:bobs@example.org> . inst:User4 a foaf:Person ; foaf:name "A" ; # error: both name and given/family name foaf:givenName "B" ; foaf:familyName "C" ; foaf:mbox <mailto:bobs@example.org> .
In addition to those basic property characteristics, it is possible to use others such as min/max value ranges for properties that have datatype literals as values. Here we constrain the length of a foaf:name to be >= 3 and <= 255:
# shapes (Turtle) my:UserShape a sh:Shape ; sh:property [ sh:predicate foaf:name ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 sh:minLength 3 ; sh:maxLength 255 ] .
// shapes (JSON-LD) { "@graph": [ { "@id": "my:UserShape", "@type": "sh:Shape", "sh:property": [ { "sh:predicate": { "@id": "foaf:name" }, "sh:valueType": { "@id": "xsd:string }, "sh:minCount": 1, "sh:maxCount": 1, "sh:minLength": 3, "sh:maxLength": 255 } ] } ] }
# passing data inst:User2 foaf:name "Bob Smith" .
# failing data inst:User9 foaf:name "BS".
The following built-in constraints are available for datatype values. (XML schema has additional facets: whiteSpace, fractionDigits, totalDigits. Schema.org also suggests: stepValue).
Property | Description |
---|---|
sh:length |
Type: xsd:integer , number of characters of valid values. |
sh:maxLength |
Type: xsd:integer , maximum number of characters of valid values. |
sh:minLength |
Type: xsd:integer , minimum number of characters of valid values. |
sh:maxExclusive |
Type: the same as the sh:valueType of the surrounding property. |
sh:maxInclusive |
Type: the same as the sh:valueType of the surrounding property. |
sh:minExclusive |
Type: the same as the sh:valueType of the surrounding property. |
sh:minInclusive |
Type: the same as the sh:valueType of the surrounding property. |
sh:pattern |
Type: xsd:string , a regular expression to match. |
Due to the triple-based data model of RDF, it is possible to "walk" a relationship
in both directions - from subject to object and from object to subject.
SHACL can be used to declare that a property is associated with a class, even
if the predicate is actually used in the inverse direction.
As shown below, the system property sh:inverseProperty
is used
in such cases, and very similar characteristics as previously mentioned can
be applied in these inverse property declarations.
Here we change the first example so that instead of an issue having a ex:reportedBy identifying a node corresponding to my:UserShape, the user node has a predicate ex:reportedIssue which identifies the issue.
# shapes (Turtle) my:IssueShape a sh:Shape ; sh:property [ sh:predicate ex:state ; sh:allowedValue ex:unassigned , ex:assigned ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:inverseProperty [ sh:predicate ex:reportedIssue ; sh:valueShape my:UserShape ; sh:minCount 1 ; sh:maxCount 1 ] . my:UserShape a sh:Shape ; sh:property [ sh:predicate foaf:name ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:property [ sh:predicate foaf:mbox ; sh:nodeKind sh:IRI ; sh:minCount 1 ] .
// shapes (JSON-LD) { "@context": { "ex": "http://example.com/ns/ex#", "foaf": "http://xmlns.com/foaf/", "sh": "http://example.com/ns/sh#", "xsd": "http://www.w3.org/2001/XMLSchema#" }, "@graph": [ { "@id": "my:IssueShape", "@type": "sh:Shape", "sh:property": [ { "sh:predicate": { "@id": "ex:state" }, "sh:allowedValue": [ { "@id": "ex:unassigned" }, { "@id": "ex:assigned" } ], "sh:minCount": 1, "sh:maxCount": 1 }, { "sh:inversePredicate": { "@id": "ex:reportedIssue" }, "sh:valueShape": { "@id": "my:UserShape" }, "sh:minCount": 1, "sh:maxCount": 1 } ] }, { "@id": "my:UserShape", "@type": "sh:Shape", "sh:property": [ { "sh:predicate": { "@id": "foaf:name" }, "sh:valueType": { "@id": "xsd:string" }, "sh:minCount": 1, "sh:maxCount": 1 }, { "sh:predicate": { "@id": "foaf:mbox" }, "sh:nodeKind": { "@id": "sh:IRI" }, "sh:minCount": 1 } ] } ] }
# passing data inst:Issue1 a ex:Issue ; ex:state ex:unassigned . inst:User2 a foaf:Person ; foaf:name "Bob Smith" ; ex:reportedIssue inst:User2 ; foaf:mbox <mailto:bob@example.org> ; foaf:mbox <mailto:rs@example.org> .
# failing data inst:Issue1 a ex:Issue ; ex:state ex:unassigned ; ex:reportedBy inst:User2 . inst:User2 a foaf:Person ; foaf:name "Bob Smith" ; foaf:mbox <mailto:bob@example.org> ; foaf:mbox <mailto:rs@example.org> .
The above examples illustrate how nodes in data can be validated against a shape. There's no limit to the mechanisms that might associate data with a shape, e.g.
an node claiming to conform to some shape
clinic:patient2134 a clinic:PatientRecord ; sh:nodeShape [ sh:property [ sh:predicate clinic:phone ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 ] ] .
nodes in a graph that are instances of that class conforms to a given shape
clinic1234:CompletePatientRecord a owl:Class ; sh:classShape [ sh:property [ sh:predicate clinic1234:phone ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 ] ] .
description of data available at a SPARQL interface
[ sd:endpoint <http://www.example/sparql/> ; sd:defaultDataset [ sd:defaultGraph [ sd:Graph [ sh:hasShape ex:IssueShape ] ] ] ] .
LDP Container with starting element passed in the protocol
[ a ldp:RootedPOSTConstraint ; ldp:onContainer <MyContactsLDPContainer> sh:hasShape <PersonInMyContactsDB> ] .
LDP Container with a query identifying the starting node
[ a ldp:QueriedPOSTConstraint ; ldp:onContainer <MyContactsLDPContainer> sh:withQuery "SELECT ?s { … }" ; sh:hasShape <PersonInMyContactsDB> ] .
SHACL defines two predicates, sh:nodeShape and sh:classShape. The former asserts that a particular node in some graph conforms to a specific shape. The latter asserts that every node of some type conforms to a specific shape. It is expected that different communities will develop many more associations, much as the WSDL community created an association between input and output documents and an XML schema which described them.
The sh:classShape predicate describes a way to associate shapes with classes. It is currently unclear what is implied by attaching shape properties (e.g. sh:property) directly to a class e.g.:
clinic1234:CompletePatientRecord a owl:Class ; sh:property [ sh:predicate clinic1234:phone ; sh:valueType xsd:string ; sh:minCount 1 ; sh:maxCount 1 ] .
The property declarations shown in the previous chapter define conditions that
all graphs conforming to that shape need to fulfill.
They do this in a declarative language with a limited number of properties
such as sh:maxCount
.
However, in many cases it is necessary to define additional conditions
that are used to verify valid data, or to simply communicate the more complex
constraints.
SHACL's extension mechanism permits other languages, like SPARQL, to be used when SHACL's expressivity is insufficient.
For example, the width and height of a Square must be equal.
These constraints can be attached to class definitions using sh:constraint
as shown in the next example:
It's not yet clear whether SPARQL is treated as an extension. (The form below should be in this issue block.)
ex:IssueShape a sh:Shape ; sh:property [ sh:predicate ex:reportedOn ; sh:valueType xsd:dateTime ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:property [ sh:predicate ex:assignedOn ; sh:valueType xsd:dateTime ; sh:minCount 1 ] . sh:constraint [ sh:message "issues can't be assigned before they're reporeted" ; sh:path ex:reportedOn ; sh:path ex:assignedOn ; sh:spin """ ASK { ?this ex:reportedOn ?submDt . ?this ex:assignedOn ?assnDt . FILTER (?submDt != ?assnDt) . } """ ] .
ex:IssueShape a sh:Shape ; sh:property [ sh:predicate ex:reportedOn ; sh:valueType xsd:dateTime ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:property [ sh:predicate ex:assignedOn ; sh:valueType xsd:dateTime ; sh:minCount 1 ] . sh:constraint [ sh:message "issues can't be assigned before they're reporeted" ; sh:path ex:reportedOn ; sh:path ex:assignedOn ; sh:extension [ sh:langauge <http://www.w3.org/Submission/spin-overview/> ; sh:action """ ASK { ?this ex:reportedOn ?submDt . ?this ex:assignedOn ?assnDt . FILTER (?submDt != ?assnDt) . } """ ] ] .
Open issue: Many people find it confusing that the expression seems to work "backwards" and only find the counter-examples. So the term "constraint" might be misleading. An alternative name "exception" has been proposed (although this term has a different meaning in programming languages). Furthermore, many values of sh:constraint are in fact template calls, and the user doesn't even see the SPARQL query (e.g. ldon:minCount uses a positive direction even if its internal SPARQL query checks for counter-examples).
The example above uses SPARQL to implement an executable test.
The SPARQL query must return true
if the condition is violated, using the variable
?this
to refer to the currently tested instance.
The property sh:constraint
is linking a class with the additional
constraints that it needs to fulfill.
Each of the values of sh:constraint
can have a sh:sparql
query (ASK, SELECT or CONSTRUCT), or they can be instances of Templates as shown
later.
In SPARQL-based constraints, the properties sh:message
, sh:path
and sh:level
(with values sh:Error
,
sh:Warning
and sh:FatalError
) can be used to provide
additional information on the constraint violation.
As alternatives to ASK, you can use SPARQL SELECT or CONSTRUCT queries to take more control over which constraint violations to create. While a little bit more verbose, one advantage of those syntaxes is greater flexibility in how to build error messages etc. The following example implements the same error condition as shown above, but using a CONSTRUCT query. An example of SELECT-based constraints can be found below.
ex:IssueShape a sh:Shape ; sh:property [ sh:predicate ex:reportedOn ; sh:valueType xsd:dateTime ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:property [ sh:predicate ex:assignedOn ; sh:valueType xsd:dateTime ; sh:minCount 1 ] . sh:constraint [ sh:sparql """ CONSTRUCT { _:cv a sh:Error ; sh:root ?this ; sh:message "issues can't be assigned before they're reporeted" ; sh:path ex:reportedOn ; sh:path ex:assignedOn } WHERE { ?this ex:reportedOn ?submDt . ?this ex:assignedOn ?assnDt . FILTER (?submDt != ?assnDt) . } """ ] .
ex:IssueShape a sh:Shape ; sh:property [ sh:predicate ex:reportedOn ; sh:valueType xsd:dateTime ; sh:minCount 1 ; sh:maxCount 1 ] ; sh:property [ sh:predicate ex:assignedOn ; sh:valueType xsd:dateTime ; sh:minCount 1 ] . sh:constraint [ sh:extension [ sh:langauge <http://www.w3.org/Submission/spin-overview/> ; sh:action """ CONSTRUCT { _:cv a sh:Error ; sh:root ?this ; sh:message "issues can't be assigned before they're reporeted" ; sh:path ex:reportedOn ; sh:path ex:assignedOn } WHERE { ?this ex:reportedOn ?submDt . ?this ex:assignedOn ?assnDt . FILTER (?submDt != ?assnDt) . } """ ] ] .
In some cases, it is necessary to define constraints that are not scoped to a shape
but rather apply globally.
These can be expressed by instantiating sh:GlobalConstraint
,
as shown in the following example.
ex:MyConstraint a sh:GlobalConstraint ; sh:message "Deprecated properties should not be used anywhere." ; sh:level sh:Warning ; sh:sparql """ SELECT ?root ?predicate ?value WHERE { ?root ?predicate ?value . ?predicate owl:deprecated ?any . } """ .
ex:MyConstraint a sh:GlobalConstraint ; sh:message "Deprecated properties should not be used anywhere." ; sh:level sh:Warning ; sh:extension [ sh:langauge <http://www.w3.org/Submission/spin-overview/> ; sh:action """ SELECT ?root ?predicate ?value WHERE { ?root ?predicate ?value . ?predicate owl:deprecated ?any . } """ ] .
By default, these constraints are assumed to have a SPARQL query, but
it is also possible to instantiate templates.
Global constraints should be named with a URI (in this example: ex:MyConstraint
)
so that they are easier to reference and manage.
The design of SHACL was influenced by a number of other languages.
SHACL can be regarded as the next generation of the SPARQL Inferencing Notation (SPIN). SPIN had introduced the concept of attaching SPARQL-based constraints and rules to classes, of Templates, user-defined Functions and template libraries. Notable differences from SPIN include:
spin:constraint
was split into sh:property
,
sh:argument
and sh:constraint
,
to clarify that some constraints are rather structural information, and as
syntactic sugar.
spl:Attribute
was promoted into a core feature, as
sh:PropertyConstraint
.
sh:Graph
, sh:library
,
sh:include
).
sp:text
has become sh:sparql
, anticipating that other
languages may follow in the future.
Resource Shapes 2.0 looks very similar to the structural constraints defined using sh:property. Here is a rough mapping between the vocabularies:
Resource Shapes | SHACL |
---|---|
oslc:property |
sh:property |
oslc:Property |
sh:PropertyConstraint (implicit) |
oslc:propertyDefinition |
sh:predicate |
oslc:defaultValue |
sh:defaultValue |
oslc:occurs |
sh:minCount , sh:maxCount |
oslc:range , oslc:valueType |
sh:valueType |
oslc:valueShape |
sh:all , sh:some |
oslc:allowedValues |
sh:allowedValues |
dcterms:title |
rdfs:label |
dcterms:description |
rdfs:comment |
ShEx borrows and expands
on many ideas of Resource Shapes, focusing on nested shape declarations.
A unique feature of ShEx is the ShExC
compact syntax.
A large subset of this syntax could be mapped onto SHACL templates such as
sh:ShapeConstraint
.
RDFS and Web Ontology Language (OWL) associate RDF predicates with classes, which may stand for anything, including real-world entities or records. Nodes in RDF graphs may be associated with zero or more classes, but will generally use properties which are or could be defined in an ontology. SHACL defines patterns of RDF arcs, which may include predicates from one or more ontologies.
Here is a rough comparison between SHACL elements and their OWL equivalents, assuming a closed-world interpretation of OWL.
OWL | SHACL |
---|---|
rdfs:subClassOf an owl:Restriction |
sh:property |
owl:onProperty |
sh:predicate |
owl:minCardinality |
sh:minCount |
owl:maxCardinality |
sh:maxCount |
owl:allValuesFrom |
sh:all or sh:valueType |
owl:someValuesFrom |
sh:some |
owl:oneOf |
sh:allowedValues |
owl:hasKey (without URI rule) |
sh:PrimaryKeyPropertyConstraint |
The discussions in the Shapes WG led to refinements of the constraint violation
syntax that also exist in RDFUnit.
Also, RDFUnit has a notion of TestCases which inspired the design of the
sh:TestCase
class. Furthermore, the concept of sh:Context
borrows ideas from the RDFUnit properties appliesTo and source.
[for editors] current beauty contest switches:
(j
)son-ld vs. (t
)urtle
SPARQL as SHACL (b
)uilt-in vs. (e
)xtension
Value enumerations as RDF (c
)ollection or (o
)bject list.