← every query · class 09 · examples/07-shacl
Check what comes in
Conforms: False Results (10) data/naive-shapes.ttl over data/domain.ttl
The shape almost everyone writes first, over data that is correct. It is not the graph that is wrong — it is the shape: one name per product forbids @pt next to @en, xsd:string rejects language-tagged names, and the price lives in the offer, not on the product.
| Focus node | Path | Value | Message |
|---|---|---|---|
| product:17 | schema:name | "Mechanical keyboard"@en | Every product has exactly one name. DatatypeConstraintComponent |
| product:17 | schema:name | "Teclado mecanico"@pt | Every product has exactly one name. DatatypeConstraintComponent |
| product:17 | schema:name | Every product has exactly one name. MaxCountConstraintComponent | |
| product:17 | schema:price | Price required and not negative. MinCountConstraintComponent | |
| product:23 | schema:name | "Optical mouse"@en | Every product has exactly one name. DatatypeConstraintComponent |
| product:23 | schema:price | Price required and not negative. MinCountConstraintComponent | |
| product:31 | schema:name | "Wireless optical mouse"@en | Every product has exactly one name. DatatypeConstraintComponent |
| product:31 | schema:price | Price required and not negative. MinCountConstraintComponent | |
| product:88 | schema:name | "27-inch monitor"@en | Every product has exactly one name. DatatypeConstraintComponent |
| product:88 | schema:price | Price required and not negative. MinCountConstraintComponent |
The same data under the OWL 2 RL reasoner: 0 inconsistencies. OWL infers; SHACL validates — they are complementary, not alternatives.
The files
data/naive-shapes.ttl
# The naive shape almost everyone writes the first time. It is here for
# comparison: run against the domain as delivered, which is correct, it reports
# ten violations. It is not the graph that is wrong — it is the shape.
#
# sh:maxCount 1 on schema:name forbids multilingual names;
# sh:datatype xsd:string rejects literals with a language tag, which are
# rdf:langString; and sh:path schema:price looks for the price on the product,
# when it lives in the offer. shapes.ttl fixes all three.
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix schema: <https://schema.org/> .
@prefix onto: <https://data.csoares.me/onto#> .
@prefix shapes: <https://data.csoares.me/shapes#> .
shapes:ProductShape a sh:NodeShape ;
sh:targetClass onto:Product ;
sh:property [
sh:path schema:name ;
sh:minCount 1 ; sh:maxCount 1 ;
sh:datatype xsd:string ;
sh:minLength 2 ;
sh:message "Every product has exactly one name."@en ] ;
sh:property [
sh:path schema:price ;
sh:minCount 1 ;
sh:datatype xsd:decimal ;
sh:minInclusive 0 ;
sh:message "Price required and not negative."@en ] ;
sh:property [
sh:path onto:manufacturedBy ;
sh:class onto:Manufacturer ] .
data/domain.ttl
# domain.ttl — the demonstration domain graph: catalogue, orders and
# suppliers. The same file is used in classes 06, 08 and 09.
#
# URI convention (slide "Designing the URIs"):
# https://data.csoares.me/<collection>/<identifier>
# One prefix is declared per collection because in Turtle 1.1 the slash is not
# allowed in a local name without escaping: product:17 is exactly the URI
# https://data.csoares.me/product/17, which is how the slides write it.
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix schema: <https://schema.org/> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix onto: <https://data.csoares.me/onto#> .
@prefix product: <https://data.csoares.me/product/> .
@prefix manufacturer: <https://data.csoares.me/manufacturer/> .
@prefix customer: <https://data.csoares.me/customer/> .
@prefix order: <https://data.csoares.me/order/> .
@prefix component: <https://data.csoares.me/component/> .
@prefix category: <https://data.csoares.me/category/> .
@prefix country: <https://data.csoares.me/country/> .
@prefix batch: <https://data.csoares.me/batch/> .
@prefix container: <https://data.csoares.me/container/> .
@prefix supply: <https://data.csoares.me/supply/> .
## Countries — entities, not literals, so that they can link outwards
country:PT a onto:Country ;
rdfs:label "Portugal"@en ;
owl:sameAs <http://www.wikidata.org/entity/Q45> .
country:DE a onto:Country ;
rdfs:label "Germany"@en ;
owl:sameAs <http://www.wikidata.org/entity/Q183> .
## Manufacturers
manufacturer:9 a onto:Manufacturer ;
schema:name "Acme Lda" ;
onto:country country:PT .
manufacturer:12 a onto:Manufacturer ;
schema:name "Nordwind GmbH" ;
onto:country country:DE .
## Categories — reuses SKOS
category:computing a skos:Concept ;
skos:prefLabel "Computing"@en .
category:peripherals a skos:Concept ;
skos:prefLabel "Peripherals"@en ;
skos:broader category:computing .
category:monitors a skos:Concept ;
skos:prefLabel "Monitors"@en ;
skos:broader category:computing .
## Products
product:17 a onto:Product ;
schema:name "Teclado mecanico"@pt ;
schema:name "Mechanical keyboard"@en ;
schema:sku "TC-0017" ;
dcterms:created "2026-09-01"^^xsd:date ;
schema:category category:peripherals ;
schema:offers [
a schema:Offer ;
schema:price "49.90"^^xsd:decimal ;
schema:priceCurrency "EUR"
] ;
onto:manufacturedBy manufacturer:9 ;
onto:hasPart component:blue-switch , component:usb-c-cable ;
onto:partOf batch:A ;
# a kind of thing, not the same thing: Q250 is the concept «keyboard», not
# this product -- so not owl:sameAs (see class 10, «Alignment predicates»)
schema:additionalType <http://www.wikidata.org/entity/Q250> .
product:23 a onto:Product , onto:DiscontinuedProduct ;
schema:name "Optical mouse"@en ;
schema:sku "RT-0023" ;
dcterms:created "2025-03-14"^^xsd:date ;
schema:category category:peripherals ;
schema:offers [ a schema:Offer ; schema:price "19.90"^^xsd:decimal ; schema:priceCurrency "EUR" ] ;
onto:manufacturedBy manufacturer:9 ;
onto:hasPart component:blue-switch .
product:31 a onto:Product ;
schema:name "Wireless optical mouse"@en ;
schema:sku "RT-0031" ;
dcterms:created "2026-05-06"^^xsd:date ;
schema:category category:peripherals ;
schema:offers [ a schema:Offer ; schema:price "29.90"^^xsd:decimal ; schema:priceCurrency "EUR" ] ;
onto:manufacturedBy manufacturer:9 ;
onto:replaces product:23 .
product:88 a onto:Product ;
schema:name "27-inch monitor"@en ;
schema:sku "MN-0088" ;
dcterms:created "2026-02-11"^^xsd:date ;
schema:category category:monitors ;
schema:offers [ a schema:Offer ; schema:price "249.00"^^xsd:decimal ; schema:priceCurrency "EUR" ] ;
onto:manufacturedBy manufacturer:12 ;
onto:hasPart component:usb-c-cable .
product:52 a onto:DigitalProduct ;
schema:name "Firmware licence"@en ;
schema:sku "LF-0052" ;
dcterms:created "2026-06-30"^^xsd:date ;
schema:category category:computing ;
schema:offers [ a schema:Offer ; schema:price "12.00"^^xsd:decimal ; schema:priceCurrency "EUR" ] ;
onto:manufacturedBy manufacturer:12 .
## Components and physical groupings
component:blue-switch a onto:Component ;
rdfs:label "Blue switch"@en ;
onto:hasPart component:spring .
component:usb-c-cable a onto:Component ;
rdfs:label "USB-C cable"@en .
component:spring a onto:Component ;
rdfs:label "Spring"@en .
batch:A a onto:Batch ;
rdfs:label "Batch A"@en ;
onto:partOf container:7 .
container:7 a onto:Container ;
rdfs:label "Container 7"@en .
## Customers
customer:4 a onto:Customer ;
schema:name "Papelaria Central" .
customer:5 a onto:Customer ;
schema:name "Escola Secundaria do Douro" .
## Orders
order:1001 a onto:Order ;
onto:hasCustomer customer:4 ;
onto:date "2026-10-12"^^xsd:date ;
onto:containsProduct product:17 , product:88 ;
onto:totalCents "52000"^^xsd:integer .
order:1002 a onto:Order ;
onto:hasCustomer customer:5 ;
onto:date "2026-09-20"^^xsd:date ;
onto:containsProduct product:23 ;
onto:totalCents "1990"^^xsd:integer .
## N-ary relation: the fact has an identity of its own (slide "N-ary relations")
supply:2026-10-01\/17 a onto:Supply ;
onto:supplier manufacturer:9 ;
onto:product product:17 ;
onto:quantity "50"^^xsd:integer ;
onto:unitPriceCents "490"^^xsd:integer ;
onto:date "2026-10-01"^^xsd:date .