# SHACL rules and CDF data models

When you run validation, instance data is loaded from CDF into an RDF graph using the data model’s **views** (containers). The graph uses **concrete view types** for `rdf:type`, not GraphQL interface names. This affects how you should target SHACL rules.

## Interfaces vs concrete types

In CDF Data Modeling:

- **Interfaces** (e.g. `MMSINumber`) are implemented by one or more **views** (e.g. `NavigationAid`, `ShoreStation`, `SARAircraft`).
- Instances are stored in **containers** (views). Each node in the RDF graph typically gets `rdf:type` set to the **view** it belongs to (e.g. `sb:NavigationAid`), not to the interface (e.g. `sb:MMSINumber`).

So a rule that uses `sh:targetClass sb:MMSINumber` will usually **not match any nodes**, because no node has that type in the graph. The rule will never run and you will not get violations.

## What to do

1. **Target concrete views**  
   Use `sh:targetClass` with the view(s) that implement the interface (e.g. `sb:NavigationAid`, `sb:ShoreStation`, `sb:SARAircraft`). You can use one shape per view with the same constraint, or repeat the same property shape under each.

2. **Use a SPARQL-based target**  
   Use `sh:target` with a SPARQL query that selects all nodes whose type is one of the implementing views. That way you still express “this rule applies to the interface” while matching how types appear in the graph.

## Validation-time warning

When you run validation with verbose output, the runner checks whether any rule targets a class that has **zero instances** in the loaded graph. If so, it prints a warning, for example:

```
  [WARNING] Some rules target a class that has no instances in the loaded graph:
    - MMSINumberShape targets MMSINumber (0 instances)
    This often happens when the target is a CDF *interface*: the graph uses concrete
    view types. Consider targeting the concrete views that implement the interface,
    or use a SPARQL-based sh:target to select all implementing types.
```

Use this warning to find rules that target an interface (or another class that has no instances) and then either target the concrete views or switch to a SPARQL target.

## Not all views have data

In many CDF data models, **not every view has instances**. Some views may show 0 instances (e.g. ORCCertificate, RegattaEntry, Race, Regatta) because that data simply isn’t loaded yet or doesn’t exist in that space.

When a rule targets a view that has **zero instances** in the loaded graph:

- The rule is still evaluated, but there are **no nodes** of that type to validate.
- You get **no violations** for that rule (there is nothing to fail).
- With verbose output, you see the **[WARNING] Some rules target a class that has no instances** message for that target class.

So it’s normal to have rules for views that currently have no data. You can leave those rules in place; they will start reporting violations once instances exist. If you prefer a quieter run, you can remove or comment out rules for views that you know are empty in your environment.
