Model choices decide whether queries are one fast traversal or a slow scan, and they are hard to change later.
Graph modelling starts, like any design, from the questions the application asks. Sketch them as patterns, then choose structure:
- Nodes for things you look up or connect to; relationships for verbs between them; properties for attributes you filter or return.
- Make relationship types specific:
(:Person)-[:LIVES_IN]->(:City)and-[:BORN_IN]->beat a generic-[:RELATED {type:'lives'}]->, because traversals filter by type for free. - If a value is shared and queried across entities (a country, a genre, a skill), promote it to a node: "who else has this skill" becomes a one-hop traversal instead of a property scan.
- Use an intermediate node when a relationship needs to connect more than two things or be referenced itself: an
(:Employment)node linking person, company and role, or an(:Order)between customer and products. - Use labels for roles and categories you query by; avoid hundreds of labels with a handful of nodes each.
- Time-based data: link events in order (
:NEXT) or hang them off a time tree when range queries dominate.
Watch for supernodes, nodes with millions of relationships (a :Country node everyone lives in). Traversing through them is expensive; use more specific types, add direction, or remodel.
Refactor freely: Cypher can restructure a model in place, for example turning a property into nodes with MERGE.
Example
// before: skills as a list property, "who shares a skill" needs a scan
// (:Person {name: 'Ann', skills: ['cypher', 'python']})
// refactor: promote each skill to a node
MATCH (p:Person) WHERE p.skills IS NOT NULL
UNWIND p.skills AS skill
MERGE (s:Skill {name: skill})
MERGE (p)-[:HAS_SKILL]->(s)
REMOVE p.skills;
// now a short traversal
MATCH (:Person {name: 'Ann'})-[:HAS_SKILL]->(s)<-[:HAS_SKILL]-(peer)
RETURN peer.name, collect(s.name) AS shared
ORDER BY size(shared) DESC;Remember
- Model from the questions: nodes for things, relationships for verbs
- Specific relationship types make traversals cheap
- Promote shared values to nodes; use intermediate nodes for n-ary facts
- Watch out for supernodes with huge degrees
Watch out for
- Using a single generic relationship type with a 'type' property, losing the free filtering by type.