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.
CONSTRUCT { ?subject qb:componentProperty ?object . } WHERE { ?subject qb:attribute|qb:dimension|qb:measure|qb:measureDimension ?object . }
Every qb:DataSet has exactly one associated qb:DataStructureDefinition.
Every qb:DataStructureDefinition must include at least one declared measure.
Note: we assume here that subclasses of qb:MeasureProperty are also permitted.
The only components of a qb:DataStructureDefinition that may be marked as optional, using qb:componentRequired are attributes.
SELECT $this WHERE { $this qb:component ?componentSpec . ?componentSpec qb:componentRequired false ; qb:componentProperty ?component . FILTER NOT EXISTS { ?component a qb:AttributeProperty } }
IC-5: Concept dimensions have code lists
Every dimension with range skos:Concept must have a qb:codeList.
Every dimension declared in a qb:DataStructureDefinition must have a declared rdfs:range.
Every qb:Observation has exactly one associated qb:DataSet.
Every qb:Observation has a value for each dimension declared in its associated qb:DataStructureDefinition.
SELECT $this WHERE { $this qb:dataSet/qb:structure/qb:component/qb:componentProperty ?dim . ?dim a qb:DimensionProperty; FILTER NOT EXISTS { $this ?dim [] } }
No two qb:Observations in the same qb:DataSet may have the same value for all dimensions.
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 ) }
Every qb:Observation has a value for each declared attribute that is marked as required.
SELECT $this WHERE { $this qb:dataSet/qb:structure/qb:component ?component . ?component qb:componentRequired true ; qb:componentProperty ?attr . FILTER NOT EXISTS { $this ?attr [] } }
In a qb:DataSet which does not use a Measure dimension then each individual qb:Observation must have a value for every declared measure.
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 [] } }
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.
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 [] } }
IC-16. Single measure on measure dimension observation
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).
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) }
IC-17. All measures present in measures dimension cube
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.
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) } }
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.
SELECT $this WHERE { ?slice qb:observation $this . ?dataset qb:slice ?slice . FILTER NOT EXISTS { $this qb:dataSet ?dataset . } }
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.
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.
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 } }
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 } }
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.
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) . } }
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.
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) . } }
Every qb:SliceKey must be associated with a qb:DataStructureDefinition.
SELECT $this WHERE { FILTER NOT EXISTS { [a qb:DataStructureDefinition] qb:sliceKey $this } }
Every qb:componentProperty on a qb:SliceKey must also be declared as a qb:component of the associated qb:DataStructureDefinition.
SELECT $this WHERE { $this qb:componentProperty ?prop . ?dsd qb:sliceKey $this . FILTER NOT EXISTS { ?dsd qb:component [qb:componentProperty ?prop] } }
Each qb:Slice must have exactly one associated qb:sliceStructure.
Every qb:Slice must have a value for every dimension declared in its qb:sliceStructure.
SELECT $this WHERE { $this qb:sliceStructure [qb:componentProperty ?dim] . FILTER NOT EXISTS { $this ?dim [] } }
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 { FILTER (?subject = ?object || EXISTS { ?subject ?predicate ?o . FILTER qb:hasZeroOrMore(?o, ?predicate, ?object) . }) }
The "predicate", i.e. the property to traverse.
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.
SHACL shapes for RDF Data Cube Vocabulary
Created with TopBraid Composer. This is currently completely untested.
A shape that applies to all subjects of skos:memberList triples.
Materializes any skos:member triples from skos:memberList values, if needed.
CONSTRUCT { $this skos:member ?member . } WHERE { $this skos:memberList ?list . ?list rdf:rest*/rdf:first ?member . }