diff --git a/.gitignore b/.gitignore index cc1d7656..b9982bc6 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,5 @@ hs_err_* \#* .#* *.tmp +/tmp +/.roo \ No newline at end of file diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index 0200737a..dd38869a 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -1,415 +1,747 @@ - - - SHACL 1.2 Node Expressions - - + - - + } + + /* example tab selection */ + .ds-selector-tabs .selectors { + padding: 0; + border-bottom: 1px solid #ccc; + height: 28px; + } + + .ds-selector-tabs .selectors button { + display: inline-block; + min-width: 54px; + text-align: center; + font-size: 11px; + font-weight: bold; + height: 27px; + padding: 0 8px; + line-height: 27px; + transition: all, 0.218s; + border-top-right-radius: 2px; + border-top-left-radius: 2px; + color: #666; + border: 1px solid transparent; + } + + .ds-selector-tabs .selectors button:first-child { + margin-left: 2px; + } + + .ds-selector-tabs .selectors button.selected { + color: #202020 !important; + border: 1px solid #ccc; + border-bottom: 1px solid #fff !important; + } + + .ds-selector-tabs .selectors button:hover { + background-color: transparent; + color: #202020; + cursor: pointer; + } + + .ds-selector-tabs .tab { + display: none; + } + + .ds-selector-tabs .selected { + display: block; + } + +
@@ -477,7 +809,7 @@

Terminology

output nodes, focus graph, evaluation, - evaluation failure, + evaluation failure, conform, conformance check, failure, @@ -2104,7 +2436,31 @@

InstancesOf Expressions

- +
+

SPARQL Functions

+

+ This section introduces SHACL SPARQL function expressions based on [[sparql12-query]] that can be used in node expressions. +

+

+ + A blank node that uses a SPARQL function URI sparql:<NAME> + as its predicate with an rdf:List of arguments as its object + is called a SHACL SPARQL function expression with the corresponding SPARQL function name. + +

+
+
EVALUATION OF SPARQL FUNCTION EXPRESSIONS
+

+ The evaluation follows the SPARQL semantics for the corresponding SPARQL function. + Each item in the rdf:List is evaluated as a node expression to produce argument values, + which are then passed to the SPARQL function implementation. + If the SPARQL function produces a single result value, it is wrapped as a singleton list containing that output node. + If the SPARQL function produces no result or an error, the expression produces an empty list or an evaluation failure respectively. +

+
+
+
+

Constraint Components

@@ -2176,6 +2532,7 @@

sh:expression

ex:AccountShape-ibanNumber a sh:PropertyShape ; sh:path ex:ibanNumber ; + sh:datatype xsd:string ; sh:message "IBAN numbers must start with the country code, in upper-case letters." ; sh:expression [ sparql:strstarts ( @@ -2419,115 +2776,6 @@

sh:nodeByExpression

} } ] -} - - -
-
-eg:dataset1 - a qb:DataSet ; - qb:structure eg:dsd1 . - -eg:dsd1 - a qb:DataStructureDefinition ; - rdfs:comment "shipments by time (multiple measures approach)"@en ; - eg:hasShape eg:dsd1-shape ; - qb:component [ - qb:dimension sdmx-dimension:refTime; - ] ; - qb:component [ - qb:measure eg-measure:quantity ; - ] ; - qb:component [ - qb:measure eg-measure:weight ; - ] . - -eg:obs1a - a qb:Observation; - qb:dataSet eg:dataset1; - sdmx-dimension:refTime "2010-07-30"^^xsd:date; - eg-measure:weight 1.3 ; - eg-measure:quantity 42 . - -eg:obs1b - a qb:Observation; - qb:dataSet eg:dataset1; - sdmx-dimension:refTime "2010-07-31T12:00:00"^^xsd:dateTime; - eg-measure:weight 1.4 . -
-
-
{
-	"@graph": [
-		{
-			"@id": "eg:dataset1",
-			"@type": "qb:DataSet",
-			"qb:structure": {
-				"@id": "eg:dsd1"
-			}
-		},
-		{
-			"@id": "eg:dsd1",
-			"@type": "qb:DataStructureDefinition",
-			"rdfs:comment": {
-				"@language": "en",
-				"@value": "shipments by time (multiple measures approach)"
-			}
-			"eg:hasShape": {
-				"@id": "eg:dsd1-shape"
-			},
-			"qb:component": [
-				{
-					"qb:dimension": {
-						"@id": "sdmx-dimension:refTime"
-					}
-				},
-				{
-					"qb:measure": {
-						"@id": "eg-measure:quantity"
-					}
-				},
-				{
-					"qb:measure": {
-						"@id": "eg-measure:weight"
-					}
-				}
-			]
-		},
-		{
-			"@id": "eg:obs1a",
-			"@type": "qb:Observation",
-			"qb:dataSet": {
-				"@id": "eg:dataset1"
-			},
-			"sdmx-dimension:refTime": {
-				"@type": "xsd:date",
-				"@value": "2010-07-30"
-			},
-			"eg-measure:quantity": {
-				"@type": "xsd:integer",
-				"@value": "42"
-			},
-			"eg-measure:weight": {
-				"@type": "xsd:decimal",
-				"@value": "1.3"
-			}
-		},
-		{
-			"@id": "eg:obs1b",
-			"@type": "qb:Observation",
-			"qb:dataSet": {
-				"@id": "eg:dataset1"
-			},
-			"sdmx-dimension:refTime": {
-				"@type": "xsd:dateTime",
-				"@value": "2010-07-31T12:00:00"
-			}
-			"eg-measure:weight": {
-				"@type": "xsd:decimal",
-				"@value": "1.4"
-			},
-		}
-	]
 }
@@ -2670,6 +2918,114 @@

Internationalization Considerations

+
+ +
+ + +
{
+	"@type": "sh:ValidationReport",
+	"sh:conforms": {
+		"@type": "xsd:boolean",
+		"@value": "false"
+	},
+	"sh:result": {
+		"@type": "sh:ValidationResult",
+		"sh:resultSeverity": {
+			"@id": "sh:Violation"
+		},
+		"sh:focusNode": {
+			"@id": "eg:obs1b"
+		},
+		"sh:value": {
+			"@id": "eg:obs1b"
+		},
+		"sh:resultMessage": "Value does not conform to shape eg:dsd1-shape.",
+		"sh:sourceConstraint": {
+			"@id": "eg:dsd1-shape"
+		},
+		"sh:sourceConstraintComponent": {
+			"@id": "sh:NodeByExpressionConstraintComponent"
+		},
+		"sh:sourceShape": {
+			"@id": "eg:ObservationShape"
+		},
+		"sh:detail": [
+			{
+				"@type": "sh:ValidationResult",
+				"sh:resultSeverity": {
+					"@id": "sh:Violation"
+				},
+				"sh:focusNode": {
+					"@id": "eg:obs1b"
+				},
+				"sh:resultPath": {
+					"@id": "sdmx-dimension:refTime"
+				},
+				"sh:value": {
+					"@type": "xsd:dateTime",
+					"@value": "2010-07-31T12:00:00"
+				},
+				"sh:resultMessage": "Value does not have datatype xsd:date",
+				"sh:sourceConstraintComponent": {
+					"@id": "sh:DatatypeConstraintComponent"
+				},
+				"sh:sourceShape": {
+					"@id": "_:b1"
+				}
+			},
+			{
+				"@type": "sh:ValidationResult",
+				"sh:resultSeverity": {
+					"@id": "sh:Violation"
+				},
+				"sh:focusNode": {
+					"@id": "eg:obs1b"
+				},
+				"sh:resultPath": {
+					"@id": "eg-measure:quantity"
+				},
+				"sh:resultMessage": "Less than 1 values",
+				"sh:sourceConstraintComponent": {
+					"@id": "sh:MinCountConstraintComponent"
+				},
+				"sh:sourceShape": {
+					"@id": "_:b2"
+				}
+			}
+		]
+	}
+}
+ + + +
+ + + +
+

Security Considerations

+

TODO

+
+ +
+

Privacy Considerations

+

TODO

+
+ +
+

Acknowledgements

+

Many people contributed to this document, including members of the RDF Data Shapes Working Group.

+
+ +
+

Internationalization Considerations

+

TODO

+
+ +
+
+
diff --git a/shacl12-vocabularies/shnex-sparql.ttl b/shacl12-vocabularies/shnex-sparql.ttl new file mode 100644 index 00000000..ba5b9d0c --- /dev/null +++ b/shacl12-vocabularies/shnex-sparql.ttl @@ -0,0 +1,957 @@ +## W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE +## https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document +## -------- + +# W3C SHACL Node Expressions Vocabulary + +# THIS VERSION IS UNDER DEVELOPMENT BY THE DATA-SHAPES (SHACL 1.2) WG + +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix sh: . +@prefix shnex: . +@prefix shnex-sparql: . +@prefix sparql: . + +shnex-sparql: + a owl:Ontology ; + rdfs:label "W3C SHACL SPARQL Functions Vocabulary"@en ; + rdfs:comment "This vocabulary defines SPARQL functions for use with SHACL."@en ; + sh:declare [ + sh:prefix "sparql" ; + sh:namespace "http://www.w3.org/ns/sparql#"^^xsd:anyURI ; + ] ; + owl:imports , . + + +shnex-sparql:category + a rdf:Property ; + rdfs:label "category"@en ; + rdfs:comment "Indicates the category of a SPARQL function, such as algebraic, numeric, string, date, aggregate, or other."@en ; + rdfs:domain [ + a owl:Class ; + owl:unionOf ( + sparql:Function + sparql:Aggregate + sparql:FunctionalForm + ) + ] ; + rdfs:range xsd:string ; + . + +shnex-sparql:example + a rdf:Property ; + rdfs:label "example"@en ; + rdfs:comment "Provides an example usage of the SPARQL function or form."@en ; + rdfs:domain [ + a owl:Class ; + owl:unionOf ( + sparql:Function + sparql:Aggregate + sparql:FunctionalForm + ) + ] ; + rdfs:range xsd:string ; + . + +shnex-sparql:infixOperator + a rdf:Property ; + rdfs:label "infix operator"@en ; + rdfs:comment "Indicates the infix operator symbol used in SPARQL syntax for this function, enabling conversion from functional form to infix notation."@en ; + rdfs:domain sparql:Function ; + rdfs:range xsd:string ; + . + + +# Spec link pattern: +# For functions in the source whose rdfs:isDefinedBy is the generic spec homepage +# a per-function anchor was constructed +# as where LOCALNAME is +# the SPARQL local name (e.g., sparql:str -> #func-str). Where a function had a +# more specific anchor in the source, the same target was used. (This comment +# documents the fragment pattern used for constructed links.) + +# Imported SPARQL function terms (rdf:type, rdfs:label, rdfs:comment, shnex-sparql:category, rdfs:isDefinedBy) +# Category values: algebraic, numeric, string, date, aggregate, other + + + + + +sparql:plus + a sparql:Function ; + rdfs:label "plus"@en ; + rdfs:comment "This operator adds two numeric expressions and returns their sum."@en ; + shnex-sparql:category "algebraic" ; + shnex-sparql:infixOperator "+" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:plus ( 38 4 ) ]""" ; + . + +sparql:subtract + a sparql:Function ; + rdfs:label "subtract"@en ; + rdfs:comment "This operator subtracts the second numeric expression from the first and returns the result."@en ; + shnex-sparql:category "algebraic" ; + shnex-sparql:infixOperator "-" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:subtract ( 42 7 ) ]""" ; + . + +sparql:multiply + a sparql:Function ; + rdfs:label "multiply"@en ; + rdfs:comment "This operator multiplies two numeric expressions and returns the product."@en ; + shnex-sparql:category "algebraic" ; + shnex-sparql:infixOperator "*" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:multiply ( 6 7 ) ]""" ; + . + +sparql:divide + a sparql:Function ; + rdfs:label "divide"@en ; + rdfs:comment "This operator divides the first numeric expression by the second and returns the result."@en ; + shnex-sparql:category "algebraic" ; + shnex-sparql:infixOperator "/" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:divide ( 84 2 ) ]""" ; + . + +sparql:unary-minus + a sparql:Function ; + rdfs:label "unary minus"@en ; + rdfs:comment "This unary operator returns the negation of a numeric expression."@en ; + shnex-sparql:category "algebraic" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:unary-minus ( 42 ) ]""" ; + . + +sparql:unary-plus + a sparql:Function ; + rdfs:label "unary plus"@en ; + rdfs:comment "This unary operator returns the numeric expression unchanged, acting primarily as a syntactic indicator."@en ; + shnex-sparql:category "algebraic" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:unary-plus ( 42 ) ]""" ; + . + +sparql:equals + a sparql:Function ; + rdfs:label "equals"@en ; + rdfs:comment "This operator compares two expressions for equality."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator "=" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:equals ( "hello" "hello" ) ]""" ; + . + +sparql:not-equals + a sparql:Function ; + rdfs:label "not equals"@en ; + rdfs:comment "This operator tests two expressions for inequality."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator "!=" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:not-equals ( "hello" "world" ) ]""" ; + . + +sparql:greater-than + a sparql:Function ; + rdfs:label "greater than"@en ; + rdfs:comment "This operator tests whether the first RDF term is greater than the second RDF term."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator ">" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:greater-than ( 10 5 ) ]""" ; + . + +sparql:less-than + a sparql:Function ; + rdfs:label "less than"@en ; + rdfs:comment "This operator tests whether the first RDF term is less than the second RDF term."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator "<" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:less-than ( 5 10 ) ]""" ; + . + +sparql:greater-than-or-equal + a sparql:Function ; + rdfs:label "greater than or equal"@en ; + rdfs:comment "This operator tests whether the first RDF term is greater or equal to the second RDF term."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator ">=" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:greater-than-or-equal ( 10 10 ) ]""" ; + . + +sparql:less-than-or-equal + a sparql:Function ; + rdfs:label "less than or equal"@en ; + rdfs:comment "This operator tests whether the first RDF term is less than or equal to the second RDF term."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator "<=" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:less-than-or-equal ( 5 5 ) ]""" ; + . + +sparql:logical-not + a sparql:Function ; + rdfs:label "logical not"@en ; + rdfs:comment "This form computes the logical NOT of a boolean expression."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:logical-not ( true ) ]""" ; + . + +# Functional forms +sparql:bound + a sparql:FunctionalForm ; + rdfs:label "bound"@en ; + rdfs:comment "This form checks whether a variable is bound (assigned a value) in the current solution."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:bound ( ?name ) ]" ; + . + +sparql:if + a sparql:FunctionalForm ; + rdfs:label "if"@en ; + rdfs:comment "This conditional form evaluates a test expression and returns one of two provided expressions based on the boolean outcome of the test."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:if ( [ sparql:greater-than ( 10 5 ) ] "big" "small" ) ]""" ; + . + +sparql:coalesce + a sparql:FunctionalForm ; + rdfs:label "coalesce"@en ; + rdfs:comment "This form returns the first non-error, non-unbound value from a sequence of expressions."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:coalesce ( ?optionalValue \"default\" ) ]" ; + . + +sparql:filter-exists + a sparql:FunctionalForm ; + rdfs:label "filter exists"@en ; + rdfs:comment "This form tests whether a given pattern exists in each solution."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:filter-exists { ?this foaf:name ?name } ]" ; + . + +sparql:filter-not-exists + a sparql:FunctionalForm ; + rdfs:label "filter not exists"@en ; + rdfs:comment "This form tests that a given pattern does not exist in each solution."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:filter-not-exists { ?this foaf:knows ?someone } ]" ; + . + +sparql:logical-or + a sparql:FunctionalForm ; + rdfs:label "logical or"@en ; + rdfs:comment "This form computes the logical OR of two boolean expressions."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator "||" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:logical-or ( true false ) ]""" ; + . + +sparql:logical-and + a sparql:FunctionalForm ; + rdfs:label "logical and"@en ; + rdfs:comment "This form computes the logical AND of two boolean expressions."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator "&&" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:logical-and ( true true ) ]""" ; + . + +sparql:in + a sparql:FunctionalForm ; + rdfs:label "in"@en ; + rdfs:comment "This form checks whether a given value matches any value from a list of expressions."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:in ( "apple" "apple" "banana" "cherry" ) ]""" ; + . + +sparql:not-in + a sparql:FunctionalForm ; + rdfs:label "not in"@en ; + rdfs:comment "This form returns true if the value is not found, or false if the value is found, in the list of expressions."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:not-in ( "grape" "apple" "banana" "cherry" ) ]""" ; + . + +# Functions on RDF terms +sparql:sameTerm + a sparql:Function ; + rdfs:label "same term"@en ; + rdfs:comment "This function checks whether two RDF terms are the same in the strict sense, including their lexical forms, datatypes, and language tags for literals."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator "=" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:sameTerm ( "hello" "hello" ) ]""" ; + . + +sparql:sameValue + a sparql:Function ; + rdfs:label "same value"@en ; + rdfs:comment "This function compares two RDF terms for equivalent RDF values, potentially considering numeric type equivalencies and other canonical forms beyond strict term identity."@en ; + shnex-sparql:category "other" ; + shnex-sparql:infixOperator "=" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:sameValue ( 42 "42"^^xsd:integer ) ]""" ; + . + +sparql:isIRI + a sparql:Function ; + rdfs:label "is iri"@en ; + rdfs:comment "This function returns true if the provided term is an IRI, and false otherwise."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:isIRI ( ex:resource ) ]""" ; + . + +sparql:isURI + a sparql:Function ; + rdfs:label "is uri"@en ; + rdfs:comment "This function returns true if the provided term is an IRI, and false otherwise."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:isURI ( ex:resource ) ]""" ; + . + +sparql:isBlank + a sparql:Function ; + rdfs:label "is blank"@en ; + rdfs:comment "This function returns true if the provided term is a blank node, and false otherwise."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:isBlank ( _:blankNode ) ]" ; + . + +sparql:isLiteral + a sparql:Function ; + rdfs:label "is literal"@en ; + rdfs:comment "This function returns true if the provided term is an RDF literal, and false otherwise."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:isLiteral ( "hello world" ) ]""" ; + . + +sparql:isNumeric + a sparql:Function ; + rdfs:label "is numeric"@en ; + rdfs:comment "This function returns true if the provided term is a numeric literal (e.g., xsd:integer, xsd:decimal, xsd:float, or xsd:double), and false otherwise."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:isNumeric ( 42 ) ]""" ; + . + +sparql:str + a sparql:Function ; + rdfs:label "str"@en ; + rdfs:comment "This function returns the lexical form of an RDF term, which for IRIs is the IRI string, and for literals is the lexical representation."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + . + +sparql:lang + a sparql:Function ; + rdfs:label "lang"@en ; + rdfs:comment "This function returns the language tag of a literal, or an empty string if no language tag is present or the term is not a literal."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:lang ( "hello"@en ) ]""" ; + . + +sparql:langdir + a sparql:Function ; + rdfs:label "langdir"@en ; + rdfs:comment "This function returns the initial text direction of a literal."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:langdir ( \"hello\"@en--ltr ) ]" ; + . + +sparql:hasLang + a sparql:Function ; + rdfs:label "has lang"@en ; + rdfs:comment "This function returns true if the given RDF literal has a specified language, matching the literal’s language tag."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:hasLang ( "hello"@en "en" ) ]""" ; + . + +sparql:hasLangdir + a sparql:Function ; + rdfs:label "has langdir"@en ; + rdfs:comment "This function returns true if the given RDF literal has an initial text direction."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:hasLangdir ( \"hello\"@en--ltr ) ]" ; + . + +sparql:datatype + a sparql:Function ; + rdfs:label "datatype"@en ; + rdfs:comment "This function returns the datatype IRI of a literal term."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:datatype ( "42"^^xsd:integer ) ]""" ; + . + +sparql:iri + a sparql:Function ; + rdfs:label "iri"@en ; + rdfs:comment "This function returns an IRI with the given string."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:iri ( "ex:resource" ) ]""" ; + . + +sparql:uri + a sparql:Function ; + rdfs:label "uri"@en ; + rdfs:comment "This function returns an IRI with the given string."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:uri ( "ex:resource" ) ]""" ; + . + +sparql:bnode + a sparql:Function ; + rdfs:label "bnode"@en ; + rdfs:comment "This function returns a blank node."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:bnode ]""" ; + . + +sparql:strdt + a sparql:Function ; + rdfs:label "strdt"@en ; + rdfs:comment "This function creates a typed literal from a string and a datatype IRI, returning an RDF literal with the specified lexical form and datatype."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:strdt ( "42" xsd:integer ) ]""" ; + . + +sparql:strlang + a sparql:Function ; + rdfs:label "strlang"@en ; + rdfs:comment "This function creates an RDF literal with the specified lexical form and language tag."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:strlang ( "hello" "en" ) ]""" ; + . + +sparql:strlangdir + a sparql:Function ; + rdfs:label "strlangdir"@en ; + rdfs:comment "This function creates an RDF literal with language tag and initial text direction."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:strlangdir ( \"hello\" \"en\" \"ltr\" ) ]" ; + . + +sparql:uuid + a sparql:Function ; + rdfs:label "uuid"@en ; + rdfs:comment "This function generates a UUID as an IRI."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:uuid ]""" ; + . + +sparql:struuid + a sparql:Function ; + rdfs:label "struuid"@en ; + rdfs:comment "This function generates a UUID as a string."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:struuid ]""" ; + . + +# Functions on strings +sparql:strlen + a sparql:Function ; + rdfs:label "strlen"@en ; + rdfs:comment "This function returns the length of the lexical form of a string literal, measured in characters."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:strlen ( "hello" ) ]""" ; + . + +sparql:substr + a sparql:Function ; + rdfs:label "substr"@en ; + rdfs:comment "This function returns the substring of the given string starting at a specified position and optionally limited to a given length."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:substr ( "hello world" 2 5 ) ]""" ; + . + +sparql:ucase + a sparql:Function ; + rdfs:label "ucase"@en ; + rdfs:comment "This function transforms all alphabetic characters in the input string to uppercase, following Unicode case-folding conventions"@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:ucase ( "hello" ) ]""" ; + . + +sparql:lcase + a sparql:Function ; + rdfs:label "lcase"@en ; + rdfs:comment "This function transforms all alphabetic characters in the input string to lowercase, according to Unicode case-folding rules."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:lcase ( "HELLO" ) ]""" ; + . + +sparql:strstarts + a sparql:Function ; + rdfs:label "strstarts"@en ; + rdfs:comment "This function returns true if the first string argument begins with the second string argument, and false otherwise."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:strstarts ( "hello world" "hello" ) ]""" ; + . + +sparql:strends + a sparql:Function ; + rdfs:label "strends"@en ; + rdfs:comment "This function returns true if the first string argument ends with the second string argument, and false otherwise."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:strends ( "hello world" "world" ) ]""" ; + . + +sparql:contains + a sparql:Function ; + rdfs:label "contains"@en ; + rdfs:comment "This function returns true if the first string argument contains the second string argument as a substring, and false otherwise."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:contains ( "hello world" "lo wo" ) ]""" ; + . + +sparql:strbefore + a sparql:Function ; + rdfs:label "strbefore"@en ; + rdfs:comment "This function returns the substring of the first argument that precedes the first occurrence of the second argument."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:strbefore ( "hello world" " " ) ]""" ; + . + +sparql:strafter + a sparql:Function ; + rdfs:label "strafter"@en ; + rdfs:comment "This function returns the substring of the first argument that follows the first occurrence of the second argument."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:strafter ( "hello world" " " ) ]""" ; + . + +sparql:concat + a sparql:Function ; + rdfs:label "concat"@en ; + rdfs:comment "This function concatenates two or more string literals into one continuous string."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:concat ( "hello" " " "world" ) ]""" ; + . + +sparql:langMatches + a sparql:Function ; + rdfs:label "langmatches"@en ; + rdfs:comment "This function checks whether a given language tag matches a specified language range."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:langMatches ( "en-US" "en" ) ]""" ; + . + +sparql:regex + a sparql:Function ; + rdfs:label "regex"@en ; + rdfs:comment "This function tests whether a string matches a regular expression pattern, optionally with a specified flag (e.g., i for case-insensitive)."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:regex ( "hello123" "[0-9]+" ) ]""" ; + . + +sparql:replace + a sparql:Function ; + rdfs:label "replace"@en ; + rdfs:comment "This function performs a regular expression search-and-replace on a string, returning the modified string."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:replace ( "hello world" "world" "universe" ) ]""" ; + . + +sparql:encode + a sparql:Function ; + rdfs:label "encode"@en ; + rdfs:comment "This function encodes a string using a specified method (e.g., URI-encoding), returning the encoded version."@en ; + shnex-sparql:category "string" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:encode ( "hello world" "UTF-8" ) ]""" ; + . + +# Functions on numbers +sparql:abs + a sparql:Function ; + rdfs:label "abs"@en ; + rdfs:comment "This function returns the absolute value of a numeric argument."@en ; + shnex-sparql:category "numeric" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:abs ( -42 ) ]""" ; + . + +sparql:round + a sparql:Function ; + rdfs:label "round"@en ; + rdfs:comment "This function rounds a numeric argument to the nearest integer."@en ; + shnex-sparql:category "numeric" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:round ( 3.7 ) ]""" ; + . + +sparql:ceil + a sparql:Function ; + rdfs:label "ceil"@en ; + rdfs:comment "This function returns the smallest integer greater than or equal to the numeric argument."@en ; + shnex-sparql:category "numeric" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:ceil ( 3.2 ) ]""" ; + . + +sparql:floor + a sparql:Function ; + rdfs:label "floor"@en ; + rdfs:comment "This function returns the greatest integer less than or equal to the numeric argument."@en ; + shnex-sparql:category "numeric" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:floor ( 3.8 ) ]""" ; + . + +sparql:rand + a sparql:Function ; + rdfs:label "rand"@en ; + rdfs:comment "This function returns a random number between 0 and 1."@en ; + shnex-sparql:category "numeric" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:rand ]""" ; + . + +# Functions on datetimes +sparql:now + a sparql:Function ; + rdfs:label "now"@en ; + rdfs:comment "This function returns the current dateTime (with or without a timezone) at the moment of query execution."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:now ]""" ; + . + +sparql:year + a sparql:Function ; + rdfs:label "year"@en ; + rdfs:comment "This function returns the year component of an xsd:dateTime or xsd:date."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:year ( "2023-12-25T10:30:00"^^xsd:dateTime ) ]""" ; + . + +sparql:month + a sparql:Function ; + rdfs:label "month"@en ; + rdfs:comment "This function returns the month component of an xsd:dateTime or xsd:date."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:month ( "2023-12-25T10:30:00"^^xsd:dateTime ) ]""" ; + . + +sparql:day + a sparql:Function ; + rdfs:label "day"@en ; + rdfs:comment "This function returns the day component of an xsd:dateTime or xsd:date."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:day ( "2023-12-25T10:30:00"^^xsd:dateTime ) ]""" ; + . + +sparql:hours + a sparql:Function ; + rdfs:label "hours"@en ; + rdfs:comment "This function returns the hour component (0–23) of an xsd:dateTime value."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:hours ( "2023-12-25T10:30:00"^^xsd:dateTime ) ]""" ; + . + +sparql:minutes + a sparql:Function ; + rdfs:label "minutes"@en ; + rdfs:comment "This function returns the minute component (0–59) of an xsd:dateTime value."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:minutes ( "2023-12-25T10:30:00"^^xsd:dateTime ) ]""" ; + . + +sparql:seconds + a sparql:Function ; + rdfs:label "seconds"@en ; + rdfs:comment "This function returns the second component (0–60, including leap seconds) of an xsd:dateTime value."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:seconds ( "2023-12-25T10:30:00"^^xsd:dateTime ) ]""" ; + . + +sparql:timezone + a sparql:Function ; + rdfs:label "timezone"@en ; + rdfs:comment "This function returns the timezone component as a dayTimeDuration for an xsd:dateTime value with a specified time zone, or an empty value if none."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:timezone ( "2023-12-25T10:30:00+02:00"^^xsd:dateTime ) ]""" ; + . + +sparql:tz + a sparql:Function ; + rdfs:label "tz"@en ; + rdfs:comment "This function returns the timezone component as a string in ISO 8601 format if present in the xsd:dateTime value, or an empty string otherwise."@en ; + shnex-sparql:category "date" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:tz ( "2023-12-25T10:30:00+02:00"^^xsd:dateTime ) ]""" ; + . + +# Functions on triple terms +sparql:triple + a sparql:Function ; + rdfs:label "triple"@en ; + rdfs:comment "This function constructs a triple term."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:triple ( ex:s ex:p ex:o ) ]" ; + . + +sparql:subject + a sparql:Function ; + rdfs:label "subject"@en ; + rdfs:comment "This function returns the subject of a triple term."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:subject ( << ex:s ex:p ex:o >> ) ]" ; + . + +sparql:predicate + a sparql:Function ; + rdfs:label "predicate"@en ; + rdfs:comment "This function returns the predicate of a triple term."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:predicate ( << ex:s ex:p ex:o >> ) ]" ; + . + +sparql:object + a sparql:Function ; + rdfs:label "object"@en ; + rdfs:comment "This function returns the object of a triple term."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:object ( << ex:s ex:p ex:o >> ) ]" ; + . + +sparql:isTriple + a sparql:Function ; + rdfs:label "is triple"@en ; + rdfs:comment "This function returns true if the argument is a triple term, and false otherwise."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:isTriple ( << ex:s ex:p ex:o >> ) ]" ; + . + +# Hash functions +sparql:md5 + a sparql:Function ; + rdfs:label "md5"@en ; + rdfs:comment "This function computes the MD5 hash of the lexical form of a string, returning a hexadecimal string representation of the hash."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:md5 ( "hello" ) ]""" ; + . + +sparql:sha1 + a sparql:Function ; + rdfs:label "sha1"@en ; + rdfs:comment "This function computes the SHA-1 hash of the lexical form of a string, returning a hexadecimal string representation of the result."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:sha1 ( "hello" ) ]""" ; + . + +sparql:sha256 + a sparql:Function ; + rdfs:label "sha256"@en ; + rdfs:comment "This function computes the SHA-256 hash of the lexical form of a string, returning the resulting hash as a hexadecimal string."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:sha256 ( "hello" ) ]""" ; + . + +sparql:sha384 + a sparql:Function ; + rdfs:label "sha384"@en ; + rdfs:comment "This function computes the SHA-384 hash of the lexical form of a string, returning the resulting hexadecimal string."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:sha384 ( "hello" ) ]""" ; + . + +sparql:sha512 + a sparql:Function ; + rdfs:label "sha512"@en ; + rdfs:comment "This function computes the SHA-512 hash of the lexical form of a string, returning the result as a hexadecimal string."@en ; + shnex-sparql:category "other" ; + rdfs:isDefinedBy ; + shnex-sparql:example """[ sparql:sha512 ( "hello" ) ]""" ; + . + +# Aggregate functions +sparql:agg-count + a sparql:Aggregate ; + rdfs:label "count"@en ; + rdfs:comment "Aggregate function COUNT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-count ( ?values ) ]" ; + . + +sparql:agg-count-distinct + a sparql:Aggregate ; + rdfs:label "count distinct"@en ; + rdfs:comment "Aggregate function COUNT with DISTINCT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-count-distinct ( ?values ) ]" ; + . + +sparql:agg-sum + a sparql:Aggregate ; + rdfs:label "sum"@en ; + rdfs:comment "Aggregate function SUM"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-sum ( ?numbers ) ]" ; + . + +sparql:agg-sum-distinct + a sparql:Aggregate ; + rdfs:label "sum distinct"@en ; + rdfs:comment "Aggregate function SUM with DISTINCT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-sum-distinct ( ?numbers ) ]" ; + . + +sparql:agg-min + a sparql:Aggregate ; + rdfs:label "min"@en ; + rdfs:comment "Aggregate function MIN"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-min ( ?values ) ]" ; + . + +sparql:agg-min-distinct + a sparql:Aggregate ; + rdfs:label "min distinct"@en ; + rdfs:comment "Aggregate function MIN with DISTINCT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-min-distinct ( ?values ) ]" ; + . + +sparql:agg-max + a sparql:Aggregate ; + rdfs:label "max"@en ; + rdfs:comment "Aggregate function MAX"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-max ( ?values ) ]" ; + . + +sparql:agg-max-distinct + a sparql:Aggregate ; + rdfs:label "max distinct"@en ; + rdfs:comment "Aggregate function MAX with DISTINCT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-max-distinct ( ?values ) ]" ; + . + +sparql:agg-avg + a sparql:Aggregate ; + rdfs:label "avg"@en ; + rdfs:comment "Aggregate function AVG"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-avg ( ?numbers ) ]" ; + . + +sparql:agg-avg-distinct + a sparql:Aggregate ; + rdfs:label "avg distinct"@en ; + rdfs:comment "Aggregate function AVG with DISTINCT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-avg-distinct ( ?numbers ) ]" ; + . + +sparql:agg-sample + a sparql:Aggregate ; + rdfs:label "sample"@en ; + rdfs:comment "Aggregate function SAMPLE"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-sample ( ?values ) ]" ; + . + +sparql:agg-sample-distinct + a sparql:Aggregate ; + rdfs:label "sample distinct"@en ; + rdfs:comment "Aggregate function SAMPLE with DISTINCT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-sample-distinct ( ?values ) ]" ; + . + +sparql:agg-group-concat + a sparql:Aggregate ; + rdfs:label "group_concat"@en ; + rdfs:comment "Aggregate function GROUP_CONCAT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-group-concat ( ?strings ) ]" ; + . + +sparql:agg-group-concat-distinct + a sparql:Aggregate ; + rdfs:label "group_concat distinct"@en ; + rdfs:comment "Aggregate function GROUP_CONCAT with DISTINCT"@en ; + shnex-sparql:category "aggregate" ; + rdfs:isDefinedBy ; + shnex-sparql:example "[ sparql:agg-group-concat-distinct ( ?strings ) ]" ; + .