The rules you already have
Think about the HR graph from the introduction. Nobody wrote it down, but everyone on the team knows that:
- every person has a name, and exactly one;
- a person works for at most one company, and that thing really is a
:Company; - an age is a whole number between 0 and 130;
- a project ends after it starts;
- every team has at least one manager among its members.
Where do those rules live today? Some are in application code, often in more than one service. Some are database constraints: Neo4j can enforce uniqueness and, in Enterprise Edition, existence and property types; LadybugDB enforces column types but has no NOT NULL outside the primary key. A few live in a Cypher query someone runs when a dashboard looks wrong. None of these places can tell you which nodes break which rule right now.
What SHACL is
SHACL, the Shapes Constraint Language, is a W3C Recommendation from 2017 for describing what valid data looks like. You write shapes; a validator checks data against them and produces a report of every violation.
Three properties make it a good fit for graph rules:
- It is declarative. You say "a person has exactly one name". You don't write the query that finds people without one.
- It is closed-world validation. A missing name is a violation, not an unknown fact. That is the meaning you want for a database schema. (OWL, the other famous RDF standard, is open-world: it would infer that the name exists somewhere, unknown. That is why OWL is the wrong tool for this job.)
- It is a standard. Other teams, tools and validators already read it. A shapes file outlives any one codebase.
The catch, and the bridge
SHACL was designed for RDF, where everything is a triple: subject, predicate, object. Your graph is a labeled property graph. The two models are close enough to translate:
| RDF, as SHACL sees it | Your property graph |
|---|---|
A resource with an IRI, e.g. ex:ann | A node, identified by a key property such as id |
ex:ann rdf:type ex:Person | The node has the label :Person |
ex:ann ex:name "Ann", a literal value | The property name: "Ann" |
ex:ann ex:worksFor ex:acme, a resource value | The relationship (ann)-[:WORKS_FOR]->(acme) |
| Several triples with the same predicate | A list property, or several relationships |
| (nothing) | Properties on a relationship |
| Language tags, blank nodes | (nothing) |
shacl2cypher is the bridge. You write standard SHACL as if your graph were RDF. It maps classes to labels and predicates to properties or relationships, then compiles every constraint into a read-only Cypher query that finds the violations. Where the translation is ambiguous, small s2c: annotations make it exact; other SHACL tools ignore them.
Words used in this book
- shape
- A set of constraints with a name. A node shape describes a kind of node; a property shape describes the values reached through one path.
- target
- What a shape applies to, for example "every node labelled
:Person". - focus node
- One node (or, with shacl2cypher, one relationship) being checked against a shape.
- path
- How to get from the focus node to the values being checked: a property, a relationship, or a chain of them.
- value nodes
- The values a path reaches from one focus node. Might be zero, one or many.
- constraint
- One condition inside a shape, e.g.
sh:minCount 1. shacl2cypher compiles each one into a separate named rule. - violation
- A focus node, and usually a value, that fails a constraint. Each one is a row in the report.
Takeaways
- SHACL describes valid data declaratively, with closed-world meaning, the same meaning as a database schema.
- RDF classes map to labels, literal predicates to properties, resource predicates to relationships.
- shacl2cypher compiles each constraint into a read-only query and reports violations with the node and value at fault.