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.

Introduction

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.

image/svg+xml some:Issue some:User some:Employee ex:state ex:assigned ex:submittedBy ex:assignedTo mailto:bob@... foaf:name Bob Smith foaf:mbox mailto:joe@... foaf:givenName Joe foaf:mbox foaf:familyName Jones

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#

Presentation Syntax

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.

View as: Turtle (press 't') JSON-LD (press 'j') .

Atomic Rules

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:

Shape Declarations

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:

The user referenced by the ex:reportedBy property must have:

# 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.)

View as: RDF Collection (press 'c') multi-valued property, here represented as a turtle objectList (press 'o') .

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.

Property Constraints

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.

Basic Property Characteristics

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.

Choices

We can extend our first example to include a more flexible user which includes given and family names.

:name : xsd:string :givenName : xsd:string :familyName : xsd:string :mbox : AnyURI:User :status = Assigned|Unassigned :reportedOn : xsd:dateTime :reproducedOn : xsd:dateTime? :Issue0..*0..110..*0..*0..1:reportedBy:reproducedBy:related

In this example, a user has:

  • either a simple name:
    • a foaf:name property, .
  • or a composite name:
    • one or more foaf:givenNames
    • a foaf:familyName

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> .

Datatype Property Characteristics

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.

Inverse Property Declarations

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> .

Associating Data with Shapes

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.

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
  ] .

Extensible Constraints

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.

SPARQL-based Constraints

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.)

View as: SPARQL is built-in to SHACL (press 'b') SPARQL is treated as an extension (press 'e') .

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) .
      }
      """ ]
  ] .

Global Constraints

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.

Relationships with other Languages

The design of SHACL was influenced by a number of other languages.

SPIN

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:

Resource Shapes

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

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 OWL

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

RDFUnit

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.