← 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 nodePathValueMessage
product:17schema:name"Mechanical keyboard"@enEvery product has exactly one name.
DatatypeConstraintComponent
product:17schema:name"Teclado mecanico"@ptEvery product has exactly one name.
DatatypeConstraintComponent
product:17schema:nameEvery product has exactly one name.
MaxCountConstraintComponent
product:17schema:pricePrice required and not negative.
MinCountConstraintComponent
product:23schema:name"Optical mouse"@enEvery product has exactly one name.
DatatypeConstraintComponent
product:23schema:pricePrice required and not negative.
MinCountConstraintComponent
product:31schema:name"Wireless optical mouse"@enEvery product has exactly one name.
DatatypeConstraintComponent
product:31schema:pricePrice required and not negative.
MinCountConstraintComponent
product:88schema:name"27-inch monitor"@enEvery product has exactly one name.
DatatypeConstraintComponent
product:88schema:pricePrice 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 .