ComponentPropertyRuleShape
type: NodeShape
comment: A SHACL rule that can be used to infer additional triples that copies all values of the sub-properties of qb:componentProperty into qb:componentProperty itself. This is needed because many constraints here only reference qb:componentProperty.
rule: SPARQLRule
  construct: CONSTRUCT { ?subject qb:componentProperty ?object . } WHERE { ?subject qb:attribute|qb:dimension|qb:measure|qb:measureDimension ?object . }
  order: 0
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
target node: this

Data set shape
type: NodeShape
property:
  path: structure
  comment: IC-2. Unique DSD
  max count: 1
  message: Every qb:DataSet has exactly one associated qb:DataStructureDefinition.
  min count: 1
target class: DataSet

Data structure definition shape
type: NodeShape
property:
  path: component
  comment: IC-3. DSD includes measure
  message: Every qb:DataStructureDefinition must include at least one declared measure.
  qualified min count: 1
  qualified value shape:
    path: componentProperty
    qualified min count: 1
    qualified value shape:
      comment: Note: we assume here that subclasses of qb:MeasureProperty are also permitted.
      class: MeasureProperty
sparql:
  comment: IC-6. Only attributes may be optional
  message: The only components of a qb:DataStructureDefinition that may be marked as optional, using qb:componentRequired are attributes.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:component ?componentSpec . ?componentSpec qb:componentRequired false ; qb:componentProperty ?component . FILTER NOT EXISTS { ?component a qb:AttributeProperty } }
target class: DataStructureDefinition

Dimension property shape
type: NodeShape
node:
  comment: IC-5: Concept dimensions have code lists
  message: Every dimension with range skos:Concept must have a qb:codeList.
  or:
    not:
      path: range
      has value: Concept
  or:
    path: codeList
    min count: 1
property:
  path: range
  comment: IC-4. Dimensions have range
  message: Every dimension declared in a qb:DataStructureDefinition must have a declared rdfs:range.
  min count: 1
target class: DimensionProperty

Observation shape
type: NodeShape
property:
  path: dataSet
  comment: IC-1. Unique DataSet
  max count: 1
  message: Every qb:Observation has exactly one associated qb:DataSet.
  min count: 1
sparql:
  comment: IC-11. All dimensions required
  message: Every qb:Observation has a value for each dimension declared in its associated qb:DataStructureDefinition.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:dataSet/qb:structure/qb:component/qb:componentProperty ?dim . ?dim a qb:DimensionProperty; FILTER NOT EXISTS { $this ?dim [] } }
sparql:
  comment: IC-12. No duplicate observations
  message: No two qb:Observations in the same qb:DataSet may have the same value for all dimensions.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { { # For each pair of observations test if all the dimension values are the same SELECT $this (MIN(?equal) AS ?allEqual) WHERE { $this qb:dataSet ?dataset . ?obs2 qb:dataSet ?dataset . FILTER ($this != ?obs2) ?dataset qb:structure/qb:component/qb:componentProperty ?dim . ?dim a qb:DimensionProperty . $this ?dim ?value1 . ?obs2 ?dim ?value2 . BIND( ?value1 = ?value2 AS ?equal) } GROUP BY $this ?obs2 } FILTER( ?allEqual ) }
sparql:
  comment: IC-13. Required attributes
  message: Every qb:Observation has a value for each declared attribute that is marked as required.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:dataSet/qb:structure/qb:component ?component . ?component qb:componentRequired true ; qb:componentProperty ?attr . FILTER NOT EXISTS { $this ?attr [] } }
sparql:
  comment: IC-14. All measures present
  message: In a qb:DataSet which does not use a Measure dimension then each individual qb:Observation must have a value for every declared measure.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { # Observation in a non-measureType cube $this qb:dataSet/qb:structure ?dsd . FILTER NOT EXISTS { ?dsd qb:component/qb:componentProperty qb:measureType } # verify every measure is present ?dsd qb:component/qb:componentProperty ?measure . ?measure a qb:MeasureProperty; FILTER NOT EXISTS { $this ?measure [] } }
sparql:
  comment: IC-15. Measure dimension consistent
  message: In a qb:DataSet which uses a Measure dimension then each qb:Observation must have a value for the measure corresponding to its given qb:measureType.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { # Observation in a measureType-cube $this qb:dataSet/qb:structure ?dsd ; qb:measureType ?measure . ?dsd qb:component/qb:componentProperty qb:measureType . # Must have value for its measureType FILTER NOT EXISTS { $this ?measure [] } }
sparql:
  comment: IC-16. Single measure on measure dimension observation
  message: In a qb:DataSet which uses a Measure dimension then each qb:Observation must only have a value for one measure (by IC-15 this will be the measure corresponding to its qb:measureType).
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { # Observation with measureType $this qb:dataSet/qb:structure ?dsd ; qb:measureType ?measure ; ?omeasure [] . # Any measure on the observation ?dsd qb:component/qb:componentProperty qb:measureType ; qb:component/qb:componentProperty ?omeasure . ?omeasure a qb:MeasureProperty . # Must be the same as the measureType FILTER (?omeasure != ?measure) }
sparql:
  comment: IC-17. All measures present in measures dimension cube
  message: In a qb:DataSet which uses a Measure dimension then if there is a Observation for some combination of non-measure dimensions then there must be other Observations with the same non-measure dimension values for each of the declared measures.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { { # Count number of other measures found at each point SELECT $this ?numMeasures (COUNT(?obs2) AS ?count) WHERE { { # Find the DSDs and check how many measures they have SELECT $this ?dsd (COUNT(?m) AS ?numMeasures) WHERE { ?dsd qb:component/qb:componentProperty ?m. ?m a qb:MeasureProperty . } GROUP BY ?dsd } # Observation in measureType cube $this qb:dataSet/qb:structure ?dsd; qb:dataSet ?dataset ; qb:measureType ?m1 . # Other observation at same dimension value ?obs2 qb:dataSet ?dataset ; qb:measureType ?m2 . FILTER NOT EXISTS { ?dsd qb:component/qb:componentProperty ?dim . FILTER (?dim != qb:measureType) ?dim a qb:DimensionProperty . $this ?dim ?v1 . ?obs2 ?dim ?v2. FILTER (?v1 != ?v2) } } GROUP BY $this ?numMeasures HAVING (?count != ?numMeasures) } }
sparql:
  comment: IC-18. Consistent data set links
  message: If a qb:DataSet D has a qb:slice S, and S has an qb:observation O, then the qb:dataSet corresponding to O must be D.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { ?slice qb:observation $this . ?dataset qb:slice ?slice . FILTER NOT EXISTS { $this qb:dataSet ?dataset . } }
sparql:
  comment: IC-19. a) Codes from code list
  version info: See section IC-19 in the Data Cubes spec on pre-conditions that need to be met prior to the execution of this constraints. Parts of them are covered by the rules at skos:memberListShape.
  message: If a dimension property has a qb:codeList, then the value of the dimension property on every qb:Observation must be in the code list.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:dataSet/qb:structure/qb:component/qb:componentProperty ?dim . ?dim a qb:DimensionProperty ; qb:codeList ?list . ?list a skos:ConceptScheme . $this ?dim ?v . FILTER NOT EXISTS { ?v a skos:Concept ; skos:inScheme ?list } }
sparql:
  comment: IC-19. b) Codes from code list
  version info: See section IC-19 in the Data Cubes spec on pre-conditions that need to be met prior to the execution of this constraints. Parts of them are covered by the rules at skos:memberListShape.
  message: If a dimension property has a qb:codeList, then the value of the dimension property on every qb:Observation must be in the code list.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:dataSet/qb:structure/qb:component/qb:componentProperty ?dim . ?dim a qb:DimensionProperty ; qb:codeList ?list . ?list a skos:Collection . $this ?dim ?v . FILTER NOT EXISTS { ?v a skos:Concept ; skos:member+ ?v } }
sparql:
  comment: IC-20. Codes from hierarchy
  message: If a dimension property has a qb:HierarchicalCodeList with a non-blank qb:parentChildProperty then the value of that dimension property on every qb:Observation must be reachable from a root of the hierarchy using zero or more hops along the qb:parentChildProperty links.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:dataSet/qb:structure/qb:component/qb:componentProperty ?dim . ?dim a qb:DimensionProperty ; qb:codeList ?list . ?list a qb:HierarchicalCodeList . $this ?dim ?v . ?hierarchy a qb:HierarchicalCodeList ; qb:parentChildProperty ?p . FILTER ( isIRI(?p) ) . FILTER NOT EXISTS { ?list qb:hierarchyRoot ?root . FILTER qb:hasZeroOrMore(?root, ?p, ?v) . } }
sparql:
  comment: IC-21. Codes from hierarchy (inverse)
  message: If a dimension property has a qb:HierarchicalCodeList with an inverse qb:parentChildProperty then the value of that dimension property on every qb:Observation must be reachable from a root of the hierarchy using zero or more hops along the inverse qb:parentChildProperty links.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:dataSet/qb:structure/qb:component/qb:componentProperty ?dim . ?dim a qb:DimensionProperty ; qb:codeList ?list . ?list a qb:HierarchicalCodeList . $this ?dim ?v . ?hierarchy a qb:HierarchicalCodeList; qb:parentChildProperty ?pcp . FILTER( isBlank(?pcp) ) ?pcp owl:inverseOf ?p . FILTER( isIRI(?p) ) FILTER NOT EXISTS { ?list qb:hierarchyRoot ?root . FILTER qb:hasZeroOrMore(?root, ?p, ?v) . } }
target class: Observation

Slice key shape
type: NodeShape
sparql:
  comment: IC-7. Slice Keys must be declared
  message: Every qb:SliceKey must be associated with a qb:DataStructureDefinition.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { FILTER NOT EXISTS { [a qb:DataStructureDefinition] qb:sliceKey $this } }
sparql:
  comment: IC-8. Slice Keys consistent with DSD
  message: Every qb:componentProperty on a qb:SliceKey must also be declared as a qb:component of the associated qb:DataStructureDefinition.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:componentProperty ?prop . ?dsd qb:sliceKey $this . FILTER NOT EXISTS { ?dsd qb:component [qb:componentProperty ?prop] } }
target class: SliceKey

Slice shape
type: NodeShape
property:
  path: sliceStructure
  comment: IC-9. Unique slice structure
  max count: 1
  message: Each qb:Slice must have exactly one associated qb:sliceStructure.
  min count: 1
sparql:
  comment: IC-10. Slice dimensions complete
  message: Every qb:Slice must have a value for every dimension declared in its qb:sliceStructure.
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
  select: SELECT $this WHERE { $this qb:sliceStructure [qb:componentProperty ?dim] . FILTER NOT EXISTS { $this ?dim [] } }
target class: Slice

has zero or more
type: SPARQLFunction
comment: A helper function that simulates a property* path in SPARQL, where the property is a variable. This is needed as a helper for the constraints IC-20 and IC-21. The function takes a subject, predicate and object (all of which must be bound) and returns true if the SPARQL expression ?subject ?predicate* ?object has a match.
ask: ASK { FILTER (?subject = ?object || EXISTS { ?subject ?predicate ?o . FILTER qb:hasZeroOrMore(?o, ?predicate, ?object) . }) }
parameter:
  path: object
  description: The value on the "right hand side".
  name: object
  order: 2
parameter:
  path: predicate
  description: The "predicate", i.e. the property to traverse.
  name: predicate
  node kind: IRI
  order: 1
parameter:
  path: subject
  description: The value on the "left hand side".
  name: subject
  order: 0
prefixes: SHACL shapes for RDF Data Cube Vocabulary
return type: boolean

SHACL shapes for RDF Data Cube Vocabulary
type: Ontology
comment: Implements the integrity constraints defined by section 11.1 of the Data Cube specification. Most of the constraints work in standard SHACL-SPARQL, but two constraints require a SHACL function (from SHACL Advanced Features) to recursively walk a dynamically computed property path. An alternative implementation of these constraints could be produced without a helper function based on SHACL-JS.
imports: dash
imports: cube
imports: core
version info: Created with TopBraid Composer. This is currently completely untested.
declare: PrefixDeclaration
  namespace: http://purl.org/linked-data/cube#
  prefix: qb
entailment: Rules

member list shape
type: NodeShape
comment: A shape that applies to all subjects of skos:memberList triples.
rule: SPARQLRule
  comment: Materializes any skos:member triples from skos:memberList values, if needed.
  construct: CONSTRUCT { $this skos:member ?member . } WHERE { $this skos:memberList ?list . ?list rdf:rest*/rdf:first ?member . }
  prefixes: SHACL shapes for RDF Data Cube Vocabulary
target subjects of: memberList