From 9fb5705f3793f383a4148a7bb55223229d967e66 Mon Sep 17 00:00:00 2001 From: Simon Steyskal Date: Mon, 25 Aug 2025 08:38:57 +0200 Subject: [PATCH 1/3] feat(shnex): introduce advanced sequence manipulation expressions (flatMap, findFirst, matchAll) --- shacl12-node-expr/index.html | 148 +++++++++++++++++++++++++++++++++ shacl12-vocabularies/shnex.ttl | 36 +++++++- 2 files changed, 181 insertions(+), 3 deletions(-) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index 0200737a..cd4e8db0 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2044,6 +2044,154 @@

Sum Expressions

+
+

Advanced Sequence Operations

+

+ This section describes advanced node expression functions for sequence manipulation, + providing enhanced operations for complex sequence processing scenarios. +

+ +
+

FlatMap Expressions

+

+ + A blank node that is the subject of the following properties + is called a flatMap expression with the function name shnex:FlatMapExpression: + + + + + + + + + + + + + +
PropertyConstraintsDescription
shnex:flatMapNode expression + The node expression that produces nested sequences to be flattened. +
+
+

+
+
EVALUATION OF FLATMAP EXPRESSIONS
+

+ Let input be the value of shnex:flatMap in the flatMap expression. + Let N be the output nodes of evalExpr(input, focusGraph, focusNode, scope). + The output nodes of the flatMap expression are produced by concatenating all sequences in N, + preserving the order of elements within each sequence and the order of sequences. +

+
+

The remainder of this section is informative.

+

+ The shnex:flatMap operation is useful when working with node expressions that produce nested sequences + that need to be flattened into a single sequence. This is particularly helpful when combining results + from multiple path traversals. +

+
+ +
+

FindFirst Expressions

+

+ + A blank node that is the subject of the following properties + is called a findFirst expression with the function name shnex:FindFirstExpression: + + + + + + + + + + + + + + + + + + +
PropertyConstraintsDescription
shnex:findFirstNode expression + The node expression that produces the input sequence. +
shnex:filterShapeA shape + The shape that the first matching element must conform to. +
+
+

+
+
EVALUATION OF FINDFIRST EXPRESSIONS
+

+ Let input be the value of shnex:findFirst in the findFirst expression. + Let filter be the value of shnex:filterShape (if provided). + Let N be the output nodes of evalExpr(input, focusGraph, focusNode, scope). + The output nodes of the findFirst expression are either an empty sequence, + or a sequence containing exactly the first element from N that conforms to filter + (or the first element if no filter is provided). +

+
+

The remainder of this section is informative.

+

+ The shnex:findFirst operation is useful for finding the first element in a sequence + that matches certain criteria, similar to finding the first occurrence of a pattern. +

+
+ +
+

MatchAll Expressions

+

+ + A blank node that is the subject of the following properties + is called a matchAll expression with the function name shnex:MatchAllExpression: + + + + + + + + + + + + + + + + + + +
PropertyConstraintsDescription
shnex:matchAllNode expression + The node expression that produces the input sequence. +
shnex:filterShapeA shape + The shape that matching elements must conform to. +
+
+

+
+
EVALUATION OF MATCHALL EXPRESSIONS
+

+ Let input be the value of shnex:matchAll in the matchAll expression. + Let filter be the value of shnex:filterShape (if provided). + Let N be the output nodes of evalExpr(input, focusGraph, focusNode, scope). + The output nodes of the matchAll expression are the subsequence of N + containing all elements that conform to filter, preserving their original order. + If no filter is provided, returns all elements of N. +

+
+

The remainder of this section is informative.

+

+ The shnex:matchAll operation is useful for filtering sequences based on criteria, + similar to the existing shnex:filterShape but designed for more general matching scenarios. +

+
+ +
+

Miscellaneous Node Expressions

diff --git a/shacl12-vocabularies/shnex.ttl b/shacl12-vocabularies/shnex.ttl index c870546e..3f24c178 100644 --- a/shacl12-vocabularies/shnex.ttl +++ b/shacl12-vocabularies/shnex.ttl @@ -46,8 +46,38 @@ shnex:intersection rdfs:comment "A list of node expressions that shall be intersected."@en ; rdfs:isDefinedBy shnex: . -shnex:union +shnex:join a rdf:Property ; - rdfs:label "union"@en ; - rdfs:comment "A list of node expressions that shall be used together."@en ; + rdfs:label "join"@en ; + rdfs:comment "A list of node expressions that shall be joined together into a sequence, preserving order."@en ; + rdfs:isDefinedBy shnex: . + +shnex:remove + a rdf:Property ; + rdfs:label "remove"@en ; + rdfs:comment "Removes all nodes from the first input sequence that appear in the second input sequence, preserving order of remaining elements."@en ; + rdfs:isDefinedBy shnex: . + +shnex:pathValues + a rdf:Property ; + rdfs:label "path values"@en ; + rdfs:comment "A property path expression that produces the sequence of values reachable from the focus node via the given path."@en ; + rdfs:isDefinedBy shnex: . + +shnex:flatMap + a rdf:Property ; + rdfs:label "flat map"@en ; + rdfs:comment "Flattens nested sequences into a single sequence, preserving order."@en ; + rdfs:isDefinedBy shnex: . + +shnex:findFirst + a rdf:Property ; + rdfs:label "find first"@en ; + rdfs:comment "Returns the first element in the input sequence that matches the given criteria, or empty sequence if none match."@en ; + rdfs:isDefinedBy shnex: . + +shnex:matchAll + a rdf:Property ; + rdfs:label "match all"@en ; + rdfs:comment "Returns all elements in the input sequence that match the given criteria, preserving order."@en ; rdfs:isDefinedBy shnex: . From 982a851140e6770dcf4b23d9c90de565b849f1d9 Mon Sep 17 00:00:00 2001 From: Simon Steyskal Date: Mon, 25 Aug 2025 08:38:58 +0200 Subject: [PATCH 2/3] refactor(shnex): rename shnex:path to shnex:pathValues throughout specification and examples --- shacl12-node-expr/index.html | 49 +++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index cd4e8db0..d9739b4a 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -669,7 +669,7 @@

Getting started with Node Expressions

sh:datatype xsd:integer ; sh:values [ shnex:count [ - shnex:path ex:employee ; + shnex:pathValues ex:employee ; ] ] . @@ -860,13 +860,13 @@

List Parameter Functions

ex:coalesce ( [ # This is a path expression that is expected to return zero or one values - shnex:path ex:fullName ; + shnex:pathValues ex:fullName ; ] [ ex:concat ( - [ shnex:path ex:firstName ] # Path expression with at most one value + [ shnex:pathValues ex:firstName ] # Path expression with at most one value " " # A constant literal expression - [ shnex:path ex:lastName ] # Path expression with at most one value + [ shnex:pathValues ex:lastName ] # Path expression with at most one value ) ] ) @@ -1055,10 +1055,10 @@

List Expressions

sh:values [ shnex:nodes [ # This returns all transitive superclasses of the current focus node - shnex:path [ sh:zeroOrMorePath rdfs:subClassOf ] ; + shnex:pathValues [ sh:zeroOrMorePath rdfs:subClassOf ] ; ] ; # This removes any superclasses that are in the list below - shnex:minus ( owl:Thing rdfs:Resource ) ; + shnex:remove ( owl:Thing rdfs:Resource ) ; ] .
@@ -1083,7 +1083,7 @@

Path Expressions

- shnex:path + shnex:pathValues Must be a well-formed SHACL property path. @@ -1108,7 +1108,7 @@

Path Expressions

EVALUATION OF PATH EXPRESSIONS

- Let path be the value of shnex:path, + Let path be the value of shnex:pathValues, and nodes be the value of shnex:nodes in the path expression. If shnex:nodes is not given, nodes is the list consistent of exactly the focus node. Let N be the nodes produced by evalExpr(nodes, focusGraph, focusNode, scope). @@ -1117,6 +1117,15 @@

Path Expressions

TODO: Clarify if those can contain duplicates.

+ +
+

Important: Note the distinction between sh:path and shnex:pathValues:

+
    +
  • sh:path is used in property shapes to specify which property path should be constrained during validation. It defines what property or path the shape's constraints apply to.
  • +
  • shnex:pathValues is used in node expressions to specify which property path should be traversed to generate a sequence of values. It produces the actual values found by following the path.
  • +
+

For example, sh:path ex:name in a property shape constrains the values of the ex:name property, while shnex:pathValues ex:name in a node expression generates a sequence containing all values of the ex:name property.

+

The remainder of this section is informative.

The following example illustrates the use of a path expression to compute the value @@ -1142,7 +1151,7 @@

Path Expressions

sh:name "top concept count" ; sh:values [ shnex:count [ - shnex:path skos:hasTopConcept ; + shnex:pathValues skos:hasTopConcept ; ] ; ] .
@@ -1282,7 +1291,7 @@

If Expressions

sh:values [ shnex:if [ shnex:exists [ - shnex:path ex:capitalOf ; + shnex:pathValues ex:capitalOf ; ] ; ] ; shnex:then "blue" ; @@ -1362,9 +1371,9 @@

Distinct Expressions

sh:description "The superclasses of this, always including rdfs:Resource." ; sh:values [ shnex:distinct [ - shnex:union ( + shnex:join ( [ - shnex:path [ sh:zeroOrMorePath rdfs:subClassOf ] ; + shnex:pathValues [ sh:zeroOrMorePath rdfs:subClassOf ] ; ] ( rdfs:Resource ) ) @@ -1604,7 +1613,7 @@

FilterShape Expressions

sh:class ex:Person ; sh:values [ shnex:nodes [ - shnex:path ex:child ; + shnex:pathValues ex:child ; ] ; shnex:filterShape [ sh:property [ @@ -1694,7 +1703,7 @@

Limit Expressions

sh:values [ shnex:nodes [ shnex:nodes [ - shnex:path ex:child ; + shnex:pathValues ex:child ; ] ; shnex:orderBy ex:dateOfBirth ; ] ; @@ -1781,7 +1790,7 @@

Offset Expressions

sh:values [ shnex:nodes [ shnex:nodes [ - shnex:path ex:child ; + shnex:pathValues ex:child ; ] ; shnex:orderBy ex:dateOfBirth ; ] ; @@ -1867,7 +1876,7 @@

Count Expressions

sh:name "top concept count" ; sh:values [ shnex:count [ - shnex:path skos:hasTopConcept ; + shnex:pathValues skos:hasTopConcept ; ] ; ] . @@ -1940,7 +1949,7 @@

Min Expressions

sh:name "min start date" ; sh:values [ shnex:min [ - shnex:path ( ex:employee ex:startDate ) ; + shnex:pathValues ( ex:employee ex:startDate ) ; ] ; ] . @@ -2331,7 +2340,7 @@

sh:expression

[ sparql:ucase ( [ - shnex:path ( ex:country ex:code ) + shnex:pathValues ( ex:country ex:code ) shnex:nodes [ shnex:var "focusNode" ] ] ) @@ -2473,7 +2482,7 @@

sh:nodeByExpression

a sh:NodeShape ; sh:targetClass qb:Observation ; sh:nodeByExpression [ - shnex:path (qb:dataSet qb:structure eg:hasShape) ; + shnex:pathValues (qb:dataSet qb:structure eg:hasShape) ; ] .
@@ -2551,7 +2560,7 @@

sh:nodeByExpression

"@id": "qb:Observation" }, "sh:nodeByExpression": { - "shnex:path": { + "shnex:pathValues": { "@list": [ { "@id": "qb:dataSet" From c142f3fa9a9315ce03c5a69eadc8e130c8b6ed4e Mon Sep 17 00:00:00 2001 From: Simon Steyskal Date: Mon, 25 Aug 2025 08:38:58 +0200 Subject: [PATCH 3/3] refactor(shnex): rename shnex:union and shnex:minus to shnex:join and shnex:remove in spec, examples, and vocabulary --- shacl12-node-expr/index.html | 38 +++++++++++++++++----------------- shacl12-vocabularies/shacl.ttl | 5 ++++- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index d9739b4a..ef332098 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -1037,10 +1037,10 @@

List Expressions

The following example declares a property for instances of rdfs:Class where the values are derived from the values of the path rdfs:subClassOf* but with the constants from the list ( owl:Thing rdfs:Resource ) removed using - shnex:minus. + shnex:remove.

-

-
-

Union Expressions

+
+

Join Expressions

- + A blank node that is the subject of the following properties - is called a union expression with the function name shnex:UnionExpression: + is called a join expression with the function name shnex:JoinExpression: @@ -1464,7 +1464,7 @@

Union Expressions

- + @@ -1476,10 +1476,10 @@

Union Expressions

Property
shnex:unionshnex:join A well-formed SHACL list where each member is a well-formed node expression.

-
+
EVALUATION OF UNION EXPRESSIONS

- Let members be the members of the value of shnex:union in the union expression. + Let members be the members of the value of shnex:join in the join expression. The output nodes of the union expression are the concatenation of all output nodes for each node expression NE in members, using evalExpr(NE, focusGraph, focusNode, scope). The order is preserved, evaluating the members from left to right and keeping the order of each list of output nodes. @@ -1491,16 +1491,16 @@

Union Expressions

Use shnex:distinct to eliminate duplicates.

- The Example for shnex:distinct uses shnex:union. + The Example for shnex:distinct uses shnex:join.

-
-

Minus Expressions

+
+

Remove Expressions

- + A blank node that is the subject of the following properties - is called a minus expression with the function name shnex:MinusExpression: + is called a remove expression with the function name shnex:RemoveExpression: @@ -1509,7 +1509,7 @@

Minus Expressions

- + @@ -1530,10 +1530,10 @@

Minus Expressions

Property
shnex:minusshnex:remove A well-formed node expression.

-
+
EVALUATION OF MINUS EXPRESSIONS

- Let minus be the value of shnex:minus + Let toRemove be the value of shnex:remove and nodes be the value of shnex:nodes in the minus expression. Let M be the output nodes of evalExpr(minus, focusGraph, focusNode, scope). Let N be the output nodes of evalExpr(nodes, focusGraph, focusNode, scope). @@ -1544,7 +1544,7 @@

Minus Expressions

The remainder of this section is informative.

- The List Expression example uses shnex:minus. + The List Expression example uses shnex:remove.

diff --git a/shacl12-vocabularies/shacl.ttl b/shacl12-vocabularies/shacl.ttl index 44bdd2f7..5a317970 100644 --- a/shacl12-vocabularies/shacl.ttl +++ b/shacl12-vocabularies/shacl.ttl @@ -1747,7 +1747,10 @@ sh:intersection rdfs:comment "Deprecated with SHACL 1.2: replaced by shnex:intersection."@en . sh:union - rdfs:comment "Deprecated with SHACL 1.2: replaced by shnex:union."@en . + rdfs:comment "Deprecated with SHACL 1.2: replaced by shnex:join."@en . + +sh:minus + rdfs:comment "Deprecated with SHACL 1.2: replaced by shnex:remove."@en . # Expression Constraints ------------------------------------------------------