shacl2cypher
← Shapes for Property Graphs

3. Targets and focus nodes

A shape's constraints only matter once you say what they apply to. Targets pick the focus nodes: by label, by the relationships a node has, or, in shacl2cypher, by relationship type.

The shapes for this chapter are in 03-targets.ttl.

Class targets: by label

ex:PersonShape a sh:NodeShape ;
    sh:targetClass ex:Person ;
    sh:property [ sh:path ex:name ; sh:minCount 1 ] .

sh:targetClass ex:Person means "every node labelled :Person". It is the target you will use most, and it compiles to MATCH (v0:`Person`).

A shape can have several targets; its focus nodes are the union. A shape with no target at all does nothing on its own. It only matters when another shape refers to it, as chapter 8 shows.

Implicit class targets

If a shape's IRI is also declared a class, it targets its own instances:

ex:Person a rdfs:Class , sh:NodeShape ;
    sh:property [ sh:path ex:name ; sh:minCount 1 ] .

Class hierarchies

ex:Employee rdfs:subClassOf ex:Person .

In SHACL, every ex:Employee is also an ex:Person, so PersonShape checks employees too. shacl2cypher expands the hierarchy when it compiles, transitively, from the shapes files and any --ontology files:

  • Neo4j matches (v0:Person|Employee) by default. If your application already puts both labels on every employee, compile with --neo4j-labels inherited and it matches :Person alone.
  • LadybugDB stores each node in exactly one table, so the query always covers both tables.

A cycle of rdfs:subClassOf statements is a compile error that lists every statement in the cycle.

Subjects-of and objects-of: by relationship

Sometimes the interesting nodes aren't a label but a role. "Anyone who manages someone must have a title":

ex:manages s2c:relationship "MANAGES" .

ex:ManagerShape a sh:NodeShape ;
    sh:targetSubjectsOf ex:manages ;
    sh:property [ sh:path ex:title ; sh:minCount 1 ] .

sh:targetSubjectsOf ex:manages selects every node that is the subject of ex:manages, the node at the start. sh:targetObjectsOf selects the node at the end. The generated query:

MATCH (v0)
WHERE (EXISTS { MATCH (v0)-[:`MANAGES`]->() })
WITH v0
WHERE NOT ( … title has at least one value … )

Why the annotation? On its own, ex:manages could name a property manages or a relationship MANAGES. With no other evidence shacl2cypher picks a property. s2c:relationship settles it. So would a schema snapshot that declares MANAGES. Chapter 6 covers the full rules.

Relationship targets: validating edges

RDF has no properties on relationships, so standard SHACL cannot target them. Property graphs put data there all the time. shacl2cypher adds one target for it:

ex:KnowsShape a sh:NodeShape ;
    s2c:targetRelationship "KNOWS" ;
    sh:property [ sh:path ex:since ; sh:datatype xsd:date ; sh:maxCount 1 ] .

Every KNOWS relationship becomes a focus:

MATCH (s0)-[v0:`KNOWS`]->(e0)
WITH s0, v0, e0
WHERE NOT ( … since has at most one value … )
RETURN …, {type: type(v0), startKey: s0.`id`, endKey: e0.`id`, elementId: elementId(v0)} AS focus, …

Rows identify the relationship by its type and the keys of both ends. Only value constraints on plain properties apply to a relationship focus. You can't traverse onward from an edge.

What isn't a target

sh:targetNode ex:ann targets one specific resource by IRI. Property graph nodes have no IRIs, so shacl2cypher rejects it at compile time.

Identifying focus nodes in results

Every row names its focus node. --node-key id says which property identifies nodes. A shape can override it:

ex:PersonShape s2c:key "email" .

The database element id is always included as well, so rows stay unambiguous even without a key.

Takeaways

  • sh:targetClass selects by label and includes subclasses.
  • sh:targetSubjectsOf / sh:targetObjectsOf select nodes by the relationships they have.
  • s2c:targetRelationship validates properties on relationships.
  • Tell shacl2cypher when a predicate is a relationship, or give it a schema snapshot.