diff --git a/shacl/index.html b/shacl/index.html index 32f505b5..cf12fbc1 100644 --- a/shacl/index.html +++ b/shacl/index.html @@ -866,6 +866,9 @@

Inverse Property Constraints (sh:inverseProperty)

sh:minCount 1 ; rdfs:label "is someProperty of" ; # e.g. "parent" ] . +

+ TODO: Possibly use sh:inversePredicate instead of sh:predicate. +

@@ -1058,6 +1061,8 @@

sh:path

sh:path1 and sh:path2 describe a sequence path consisting of two other paths. The values of these properties must be property IRIs or blank nodes of the same recursive path structure. The path produces the concatenation of the two sub-paths. + The values of sh:path1 must either be IRIs or blank nodes with a value for sh:inverse, + which means that only the second path segment may be another sequence path. Blank nodes with sh:path1 and sh:path2 may have the optional type sh:SequencePath. @@ -1703,125 +1708,178 @@

SPARQL-based Execution (sh:sparql)

SPARQL-based Constraints

- The SPARQL queries attached to a constraint via sh:sparql must be of the query form CONSTRUCT or SELECT. - CONSTRUCT is the most flexible option, so the following section starts with describing how these are executed. - The subsequent section describes how SELECT queries can be transformed into equivalent CONSTRUCT forms. -

-

- There is ongoing discussion about whether the CONSTRUCT option below shall be supported, and whether an approach based on variable names only would be better. + The SPARQL queries attached to a constraint via sh:sparql must be of the query form SELECT.

-
-

CONSTRUCT-based Constraints

-

- Constraints backed by a SPARQL CONSTRUCT query are assumed to construct constraint violations. - CONSTRUCT queries consist of a graph template and a WHERE clause. - The output of a CONSTRUCT query is a new RDF graph, and the triples from those RDF graphs need to be added to the result graph of the constraint validation operation. -

-

- The following example illustrates how a CONSTRUCT query is used to produce instances of sh:Error for every instance of ex:ExampleClass that has no rdfs:label. - Note that _:error is a SPARQL syntax representing a new blank node for each row of the result set (WHERE clause). -

-
-sh:ExampleGlobalConstructConstraint
-	a sh:GlobalConstraint ;
-	sh:sparql """
-		CONSTRUCT {
-			_:error a sh:Error .
-			_:error sh:root ?instance .
-			_:error sh:path rdfs:label .
-			_:error sh:message "Instances of ex:ExampleClass must have an rdfs:label" .
-		}
-		WHERE {
-			?instance a ex:ExampleClass .
-			FILTER NOT EXISTS { ?instance rdfs:label ?anyLabel } .
-		}
-		""" .
-}
+
+

Binding the Focus Node in Local SPARQL Constraints (?this)

- The instances of sh:ConstraintViolation produced by such queries are not modified further, - except that the engine SHOULD add sh:source triples that have each constraint violation - instance as subject and the surrounding sh:Constraint as its object. + The SPARQL variable ?this has a special meaning in local constraints. + When SPARQL constraints are executed then the variable ?this needs to be pre-bound to the focus node. + (Need a pointer to what "pre-binding" means in this context)

-
-

SELECT-based Constraints

-

- SPARQL queries of the SELECT form need to produce one constraint violation blank node for each row of the produced result set. - The following example illustrates a constraint that flags warnings for all subjects that have a rdfs:label with the language tag "de". -

-
-ex:ExampleGlobalSelectConstraint
-	a sh:GlobalConstraint ;
-	sh:message "Deutsch is verboten" ;
-	sh:path rdfs:label ;
-	sh:severity sh:Warning ;
-	sh:sparql """
-		SELECT (?subject AS ?root) (?object AS ?value)
-		WHERE {
-			?subject rdfs:label ?object .
-			FILTER (lang(?object) = "de") .
-		}
-		""" .
+
+

Mapping of Result Variables to Constraint Violations

- For execution, the constraint above can be turned into the following CONSTRUCT query. -

-
-CONSTRUCT {
-	_:violation a sh:Warning .
-	_:violation sh:message "Deutsch is verboten" .
-	_:violation sh:path rdfs:label .
-	_:violation sh:root ?subject .
-	_:violation sh:value ?object .
-}
-WHERE {
-	?subject rdfs:label ?object .
-	FILTER (lang(?object) = "de") .
-}
-

- The following naming conventions are used to map the result variables of a SELECT into corresponding RDF properties: + Each row of the result set produced by a SELECT query must be converted into one constraint violation blank node. + The properties of those blank nodes are derived by the following rules, through a combination of result variables and by looking at properties attached to the constraint itself. + In the following table, the host resource is assumed to be the constraint or template that has the executed sh:sparql query as one of its properties. + The production rules are meant to be executed from top to bottom, so that the first bound value will be used.

- - + + - + - - + + - - + + - - + + - - + + + + + +
Result VariableConstraint Violation PropertyPropertyProduction Rules
?severity rdf:type +
    +
  1. The value of sh:severity of the host resource
  2. +
  3. sh:Error as default
  4. +
+
?messagesh:messagesh:root +
    +
  1. The value of the variable ?root
  2. +
  3. The value of the variable ?this
  4. +
  5. The current focus node
  6. +
  7. The value of sh:root of the host resource
  8. +
+
?predicatesh:predicatesh:path +
    +
  1. The value of the variable ?path
  2. +
  3. The value of the variable ?predicate
  4. +
  5. If the variable ?inversePredicate is bound, then create a new blank node as [ sh:inverse ?inversePredicate ].
  6. +
  7. + Starting with N=1, if the variables ?predicateN|?inversePredicateN are bound, + build a new blank node [ sh:path1 ?first ; sh:path2 ?rest ] where + ?first is a the value of ?predicateN if it exists, + or a new blank node [ sh:inverse ?inversePredicateN ], + and ?rest is recursively built using this same rule for ?predicate(N+1)|?inversePredicate(N+1) + until it reaches an N where both ?predicateN and ?inversePredicateN are unbound. +
  8. +
  9. The value of sh:path of the host resource
  10. +
+
?rootsh:rootsh:value +
    +
  1. The value of the variable ?value
  2. +
  3. The value of sh:value of the host resource
  4. +
+
?valuesh:valuesh:message +
    +
  1. The value of the variable ?message
  2. +
  3. The values of sh:message of the host resource
  4. +
+
sh:source +
    +
  1. The host resource
  2. +
+

- If the surrounding subject node defines a sh:message then the constructed blank nodes needs to have corresponding triples, unless the SELECT has corresponding result variables. - Similarly, the values of sh:root, sh:path and sh:value must be inserted (they cannot be blank nodes). + The following example illustrates a constraint that flags warnings for all subjects that have a rdfs:label with the language tag "de".

+
+ex:ExampleGlobalSelectConstraint
+	a sh:GlobalConstraint ;
+	sh:message "Deutsch is verboten" ;
+	sh:path rdfs:label ;
+	sh:severity sh:Warning ;
+	sh:sparql """
+		SELECT (?subject AS ?root) ?value
+		WHERE {
+			?subject rdfs:label ?value .
+			FILTER (lang(?value) = "de") .
+		}
+		""" .
+
+ex:Resource1
+	rdfs:label "Eins"@de ;
+	rdfs:label "Zwei"@de ;
+	rdfs:label "Trois"@fr .

- In local constraints the variable ?this must be projected into ?root unless the SELECT clause already projects to ?root. - This means that the frequently needed pattern SELECT (?this AS ?root) is redundant. + Output created by the example above would be:

-
-
-

Binding the Focus Node in Local SPARQL Constraints (?this)

+
+[
+	a sh:Warning ;
+	sh:root ex:Resource1 ;
+	sh:path rdfs:label ;
+	sh:value "Eins"@de ;
+	sh:message "Deutsch is verboten" ;
+	sh:source ex:ExampleGlobalSelectConstraint ;
+] .
+[
+	a sh:Warning ;
+	sh:root ex:Resource1 ;
+	sh:path rdfs:label ;
+	sh:value "Zwei"@de ;
+	sh:message "Deutsch is verboten" ;
+	sh:source ex:ExampleGlobalSelectConstraint ;
+] .

- The SPARQL variable ?this has a special meaning in local constraints. - When SPARQL constraints are executed then the variable ?this needs to be pre-bound to the focus node. - (Need a pointer to what "pre-binding" means in this context) + Here is a more complex example, producing a path expression ex:property1/^ex:property2. +

+
+ex:LocalShapeWithPathViolationExample
+	a sh:Shape ;
+	sh:constraint [
+		sh:sparql """
+				SELECT ?value (ex:property1 AS ?predicate1) (ex:property2 AS ?inversePredicate2) ?message
+				WHERE {
+					?this ex:property1 ?first .
+					?value ex:property2 ?first .
+					FILTER isBlank(?value) .
+					BIND (CONCAT("The ", "message.") AS ?message) .
+				}
+			""" ;
+	] .
+	
+ex:ExampleRootResource
+	sh:nodeShape ex:LocalShapeWithPathViolationExample ;
+	ex:property1 ex:ExampleIntermediateResource .
+
+ex:ExampleValueResource
+	ex:property2 ex:ExampleIntermediateResource .
+
+

+ Which produces the following error:

+
+[
+	a sh:Error ;
+	sh:root ex:ExampleRootResource ;
+	sh:path [
+		sh:path1 ex:property1 ;
+		sh:path2 [
+			sh:inverse ex:property2
+		]
+	] ;
+	sh:value ex:ExampleValueResource ;
+	sh:message "The message." ;
+	sh:source [ the blank node of the sh:constraint above ] ;
+] .