class SHACL::Algebra::Shape
Constants
- NAME
- NODE_KIND_COMPARE
The matrix of comparisons of different types of nodes @return {Hash{Class => RDF::URI}}
Public Instance Methods
Specifies that each value node is a SHACL
instance of a given type.
@example
ex:ClassExampleShape a sh:NodeShape ; sh:targetNode ex:Bob, ex:Alice, ex:Carol ; sh:property [ sh:path ex:address ; sh:class ex:PostalAddress ; ] .
@param [Array<RDF::URI>] types The type expected for each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 52 def builtin_class(types, node, path, value_nodes, **options) value_nodes.map do |n| has_type = n.resource? && begin objects = graph.query({subject: n, predicate: RDF.type}).objects types.all? {|t| objects.include?(t)} end satisfy(focus: node, path: path, value: n, message: "is#{' not' unless has_type} of class #{type.to_sxp}", resultSeverity: (options.fetch(:severity) unless has_type), component: RDF::Vocab::SHACL.ClassConstraintComponent, **options) end.flatten.compact end
Specifies a condition to be satisfied with regards to the datatype of each value node.
@example
ex:DatatypeExampleShape a sh:NodeShape ; sh:targetNode ex:Alice, ex:Bob, ex:Carol ; sh:property [ sh:path ex:age ; sh:datatype xsd:integer ; ] .
@param [RDF::URI] datatype the expected datatype of each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 83 def builtin_datatype(datatype, node, path, value_nodes, **options) value_nodes.map do |n| has_datatype = n.literal? && n.datatype == datatype && n.valid? satisfy(focus: node, path: path, value: n, message: "is#{' not' unless has_datatype} a valid literal with datatype #{datatype.to_sxp}", resultSeverity: (options.fetch(:severity) unless has_datatype), component: RDF::Vocab::SHACL.DatatypeConstraintComponent, **options) end.flatten.compact end
Specifies the condition that the set of value nodes is disjoint with the set of objects of the triples that have the focus node as subject and the value of sh:disjoint as predicate.
@example
ex:DisjointExampleShape a sh:NodeShape ; sh:targetNode ex:USA, ex:Germany ; sh:property [ sh:path ex:prefLabel ; sh:disjoint ex:altLabel ; ] .
@param [Array<RDF::URI>] properties the properties of the focus node whose values must be disjoint with the value nodes. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 111 def builtin_disjoint(properties, node, path, value_nodes, **options) disjoint_nodes = properties.map do |prop| graph.query({subject: node, predicate: prop}).objects end.flatten.compact value_nodes.map do |n| has_value = disjoint_nodes.include?(n) satisfy(focus: node, path: path, value: n, message: "is#{' not' unless has_value} disjoint with #{disjoint_nodes.to_sxp}", resultSeverity: (options.fetch(:severity) if has_value), component: RDF::Vocab::SHACL.DisjointConstraintComponent, **options) end.flatten.compact end
Specifies the condition that the set of all value nodes is equal to the set of objects of the triples that have the focus node as subject and the value of sh:equals as predicate.
@example
ex:EqualExampleShape a sh:NodeShape ; sh:targetNode ex:Bob ; sh:property [ sh:path ex:firstName ; sh:equals ex:givenName ; ] .
@param [RDF::URI] property the property of the focus node whose values must be equal to some value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 142 def builtin_equals(property, node, path, value_nodes, **options) equal_nodes = graph.query({subject: node, predicate: property}).objects (value_nodes.map do |n| has_value = equal_nodes.include?(n) satisfy(focus: node, path: path, value: n, message: "is#{' not' unless has_value} a value in #{equal_nodes.to_sxp}", resultSeverity: (options.fetch(:severity) unless has_value), component: RDF::Vocab::SHACL.EqualsConstraintComponent, **options) end + equal_nodes.map do |n| !value_nodes.include?(n) ? not_satisfied(focus: node, path: path, value: n, message: "should have a value in #{value_nodes.to_sxp}", resultSeverity: options.fetch(:severity), component: RDF::Vocab::SHACL.EqualsConstraintComponent, **options) : nil end).flatten.compact end
Specifies the condition that at least one value node is equal to the given RDF
term.
@example
ex:StanfordGraduate a sh:NodeShape ; sh:targetNode ex:Alice ; sh:property [ sh:path ex:alumniOf ; sh:hasValue ex:Stanford ; ] .
@param [RDF::URI] term the term that must be a value of a value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 181 def builtin_hasValue(term, node, path, value_nodes, **options) has_value = value_nodes.include?(term) [satisfy(focus: node, path: path, message: "is#{' not' unless has_value} the value #{term.to_sxp}", resultSeverity: (options.fetch(:severity) unless has_value), component: RDF::Vocab::SHACL.HasValueConstraintComponent, **options)] end
Specifies the condition that each value node is a member of a provided SHACL
list.
@example
ex:InExampleShape a sh:NodeShape ; sh:targetNode ex:RainbowPony ; sh:property [ sh:path ex:color ; sh:in ( ex:Pink ex:Purple ) ; ] .
@param [RDF::URI] list the list which must contain the value nodes.. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 206 def builtin_in(list, node, path, value_nodes, **options) value_nodes.map do |n| has_value = list.include?(n) satisfy(focus: node, path: path, value: n, message: "is#{' not' unless has_value} a value in #{list.to_sxp}", resultSeverity: (options.fetch(:severity) unless has_value), component: RDF::Vocab::SHACL.InConstraintComponent, **options) end.flatten.compact end
The condition specified by sh:languageIn is that the allowed language tags for each value node are limited by a given list of language tags.
@example
ex:NewZealandLanguagesShape a sh:NodeShape ; sh:targetNode ex:Mountain, ex:Berg ; sh:property [ sh:path ex:prefLabel ; sh:languageIn ( "en" "mi" ) ; ] .
@param [Array<RDF::URI>] datatypes the expected datatype of each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 233 def builtin_languageIn(datatypes, node, path, value_nodes, **options) value_nodes.map do |n| has_language = n.literal? && datatypes.any? {|l| n.language.to_s.start_with?(l)} satisfy(focus: node, path: path, value: n, message: "is#{' not' unless has_language} a literal with a language in #{datatypes.to_sxp}", resultSeverity: (options.fetch(:severity) unless has_language), component: RDF::Vocab::SHACL.LanguageInConstraintComponent, **options) end.flatten.compact end
Compares value nodes to be < than the specified value.
@example
ex:NumericRangeExampleShape a sh:NodeShape ; sh:targetNode ex:Bob, ex:Alice, ex:Ted ; sh:property [ sh:path ex:age ; sh:minInclusive 0 ; sh:maxInclusive 150 ; ] .
@param [RDF::URI] term the term is used to compare each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 262 def builtin_maxExclusive(term, node, path, value_nodes, **options) compare(:<, [term], node, path, value_nodes, RDF::Vocab::SHACL.MaxExclusiveConstraintComponent, **options) end
Compares value nodes to be <= than the specified value.
@example
ex:NumericRangeExampleShape a sh:NodeShape ; sh:targetNode ex:Bob, ex:Alice, ex:Ted ; sh:property [ sh:path ex:age ; sh:minInclusive 0 ; sh:maxInclusive 150 ; ] .
@param [RDF::URI] term the term is used to compare each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 284 def builtin_maxInclusive(term, node, path, value_nodes, **options) compare(:<=, [term], node, path, value_nodes, RDF::Vocab::SHACL.MaxInclusiveConstraintComponent, **options) end
Specifies the maximum string length of each value node that satisfies the condition. This can be applied to any literals and IRIs, but not to blank nodes.
@param [RDF::URI] term the term is used to compare each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 296 def builtin_maxLength(term, node, path, value_nodes, **options) value_nodes.map do |n| compares = !n.node? && n.to_s.length <= term.to_i satisfy(focus: node, path: path, value: n, message: "is#{' not' unless compares} a literal at with length <= #{term.to_sxp}", resultSeverity: (options.fetch(:severity) unless compares), component: RDF::Vocab::SHACL.MaxLengthConstraintComponent, **options) end.flatten.compact end
Compares value nodes to be > than the specified value.
@example
ex:NumericRangeExampleShape a sh:NodeShape ; sh:targetNode ex:Bob, ex:Alice, ex:Ted ; sh:property [ sh:path ex:age ; sh:minInclusive 0 ; sh:maxInclusive 150 ; ] .
@param [RDF::URI] term the term is used to compare each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 325 def builtin_minExclusive(term, node, path, value_nodes, **options) compare(:>, [term], node, path, value_nodes, RDF::Vocab::SHACL.MinExclusiveConstraintComponent, **options) end
Compares value nodes to be >= than the specified value.
@example
ex:NumericRangeExampleShape a sh:NodeShape ; sh:targetNode ex:Bob, ex:Alice, ex:Ted ; sh:property [ sh:path ex:age ; sh:minInclusive 0 ; sh:maxInclusive 150 ; ] .
@param [RDF::URI] term the term is used to compare each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus nod to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 347 def builtin_minInclusive(term, node, path, value_nodes, **options) compare(:>=, [term], node, path, value_nodes, RDF::Vocab::SHACL.MinInclusiveConstraintComponent, **options) end
Specifies the minimum string length of each value node that satisfies the condition. This can be applied to any literals and IRIs, but not to blank nodes.
@param [RDF::URI] term the term is used to compare each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 359 def builtin_minLength(term, node, path, value_nodes, **options) value_nodes.map do |n| compares = !n.node? && n.to_s.length >= term.to_i satisfy(focus: node, path: path, value: n, message: "is#{' not' unless compares} a literal with length >= #{term.to_sxp}", resultSeverity: (options.fetch(:severity) unless compares), component: RDF::Vocab::SHACL.MinLengthConstraintComponent, **options) end.flatten.compact end
Specifies a condition to be satisfied by the RDF
node kind of each value node.
@example
ex:NodeKindExampleShape a sh:NodeShape ; sh:targetObjectsOf ex:knows ; sh:nodeKind sh:IRI .
@param [RDF::URI] term the kind of node to match each value node. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the to the value nodes. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 404 def builtin_nodeKind(term, node, path, value_nodes, **options) value_nodes.map do |n| compares = NODE_KIND_COMPARE.fetch(n.class, []).include?(term) satisfy(focus: node, path: path, value: n, message: "is#{' not' unless compares} a node kind match of #{term.to_sxp}", resultSeverity: (options.fetch(:severity) unless compares), component: RDF::Vocab::SHACL.NodeKindConstraintComponent, **options) end.flatten.compact end
Specifies a regular expression that each value node matches to satisfy the condition.
@example
ex:PatternExampleShape a sh:NodeShape ; sh:targetNode ex:Bob, ex:Alice, ex:Carol ; sh:property [ sh:path ex:bCode ; sh:pattern "^B" ; # starts with 'B' sh:flags "i" ; # Ignore case ] .
@param [RDF::URI] pattern A regular expression that all value nodes need to match. @param [RDF::Term] node the focus node @param [RDF::URI, SPARQL::Algebra::Expression] path (nil) the property path from the focus node to the value nodes.. @param [Array<RDF::Term>] value_nodes @return [Array<SHACL::ValidationResult>]
# File lib/shacl/algebra/shape.rb, line 433 def builtin_pattern(pattern, node, path, value_nodes, **options) flags = options[:flags].to_s regex_opts = 0 | regex_opts |= Regexp::MULTILINE if flags.include?(?m) regex_opts |= Regexp::IGNORECASE if flags.include?(?i) regex_opts |= Regexp::EXTENDED if flags.include?(?x) pat = Regexp.new(pattern, regex_opts) value_nodes.map do |n| compares = !n.node? && pat.match?(n.to_s) satisfy(focus: node, path: path, value: n, message: "is#{' not' unless compares} a match #{pat.inspect}", resultSeverity: (options.fetch(:severity) unless compares), component: RDF::Vocab::SHACL.PatternConstraintComponent, **options) end.flatten.compact end
Returns the nodes matching this particular shape, based upon the shape properties:
* `targetNode` * `targetClass` * `targetSubjectsOf` * `targetObjectsOf` * `id` – where `type` includes `rdfs:Class`
@return [Array<RDF::Term>]
# File lib/shacl/algebra/shape.rb, line 15 def targetNodes (Array(@options[:targetNode]) + Array(@options[:targetClass]).map do |cls| graph.query({predicate: RDF.type, object: cls}).subjects end + Array(@options[:targetSubjectsOf]).map do |pred| graph.query({predicate: pred}).subjects end + Array(@options[:targetObjectsOf]).map do |pred| graph.query({predicate: pred}).objects end + ( Array(type).include?(RDF::RDFS.Class) ? graph.query({predicate: RDF.type, object: id}).subjects : [] )).flatten.uniq end
Protected Instance Methods
Common comparison logic for lessThan, lessThanOrEqual, max/minInclusive/Exclusive
# File lib/shacl/algebra/shape.rb, line 455 def compare(method, terms, node, path, value_nodes, component, **options) value_nodes.map do |left| results = terms.map do |right| case left when RDF::Literal unless right.literal? && ( (left.simple? && right.simple?) || (left.is_a?(RDF::Literal::Numeric) && right.is_a?(RDF::Literal::Numeric)) || (left.datatype == right.datatype && left.language == right.language)) :incomperable else left.send(method, right) end when RDF::URI right.uri? && left.send(method, right) else :incomperable end end if results.include?(:incomperable) not_satisfied(focus: node, path: path, value: left, message: "is incomperable with #{terms.to_sxp}", resultSeverity: options.fetch(:severity), component: component, **options) elsif results.include?(false) not_satisfied(focus: node, path: path, value: left, message: "is not #{method} than #{terms.to_sxp}", resultSeverity: options.fetch(:severity), component: component, **options) else satisfy(focus: node, path: path, value: left, message: "is #{method} than #{terms.to_sxp}", component: component, **options) end end.flatten.compact end