neo4j-modeling-skill
Design, review, and refactor Neo4j graph data models. Use when choosing node labels vs relationship types vs properties, migrating relational/document schemas to graph, detecting anti-patterns (generic labels, supernodes, missing constraints), designing intermediate nodes for n-ary relationships, en
By neo4j-contrib · 810 installs
npx skills add neo4j-contrib/neo4j-skills --skill neo4j-modeling-skill
Source repository · Upstream listing
When to Use
Designing graph model from scratch (domain → nodes, rels, props)
Reviewing existing model for anti patterns
Deciding node vs property vs relationship vs label
Migrating relational or document schema to graph
Designing intermediate nodes for n ary or complex relationships
Detecting and mitigating supernode / high fanout problems
Choosing and creating constraints + indexes for a model
When NOT to Use
Writing or optimizing Cypher → neo4j cypher skill
Spring Data Neo4j (@Node, @Relationship) → neo4j spring data skill
GraphQL type definitions → neo4j graphql skill
Importing data (LOAD CSV, APOC import) → neo4j import skill
Inspect Before Designing
On existing database, run first — never propose changes without current state:
If APOC available:
MCP tool map:
Operation Tool
Inspect schema get schema
SHOW CONSTRAINTS , SHOW INDEXES read cypher
CREATE CONSTRAINT ... IF NOT EXISTS write cypher (show + confirm first)
Defaults — Apply to Every Model
1. Use case first — list 5+ queries the model must answer before designing
2. Nodes = entities (nouns) with identity; rels = connections (verbs) with direction
3. Labels PascalCase; rel types SCREAMING SNAKE CASE; properties camelCase
4. Every node type used in MERGE has a uniqueness constraint on its key property
5. Add property type constraints ( REQUIRE n.prop IS :: STRING ) where the type is known — helps the query planner and catches bad writes early
6. No generic labels ( :Entity , :Node , :Thing ); no generic rel types ( :RELATED TO , :HAS )
7. Security labels (used for row level access control) should start with a common prefix (e.g. Sec ) so application code can reliably filter them out of the domain schema
8. Rel direction encodes semantic meaning — not arbitrary
9. Inspect schema before proposing any change on an existing database
10. All constraint/index DDL uses IF NOT EXISTS — safe to rerun
11. On Neo4j 2026.06+ (Enterprise/Aura, GA): declare the full model in one block with ALTER CURRENT GRAPH TYPE SET { … } , extend it with ALTER CURRENT GRAPH TYPE ADD { … } , instead of individual CREATE CONSTRAINT statements — see neo4j cypher skill/references/graph type.md . On 2026.02–2026.05 the same syntax is preview.
Key Patterns
Node vs Relationship vs Property — Decision Table
Question Answer Model as
Is it a thing with identity, queried as entry point? Yes Node
Is it a connection between two things with direction? Yes Relationship
Does the connection have its own properties or multiple targets? Yes Intermediate node
Is it a scalar always returned with its parent, never filtered alone? Yes Property on parent
Is it a category used for type based filtering or path traversal? Yes Label (not a property)
Does the same attribute value repeat across many nodes (low cardinality)? Yes Label, not a property node
Is it a fact connecting 2 entities? Yes Intermediate node
Property vs Label — Decision Table
Use label when Use property when
Values are few, fixed, used as traversal filters ( WHERE n:Active ) Values are many, dynamic, or unique per node
You traverse by type ( MATCH (n:VIPCustomer) ) You filter by value ( WHERE n.tier = 'vip' )
Category drives index selection Fine grained value drives range scans
Example: :Active , :Verified , :Premium Example: status , score , email
Rule: adding a label is cheap; scanning all :Label nodes is fast. Never model high cardinality values as labels.
Intermediate Node Pattern
Use when a relationship needs its own properties, connects 2 entities, or is independently queryable.
Before (relationship with property — limited):
After (intermediate node — queryable, extensible):
Employment overlap example:
Promote relationship to intermediate node when:
Relationship has 2 properties
Relationship is the subject of another query
Multiple entities share the same connection context
You need to connect 2 entities in one fact
Relational → Graph Migration Table
Relational construct Graph equivalent Notes
Table row Node One label per table (add more as needed)
Column (scalar) Node property
Primary key Uniqueness constraint property Use tmdbId , not id (too generic)
Foreign key Relationship Direction: from dependent → referenced
Many to many junction table Intermediate node Especially if junction has own columns
Junction table (no own columns) Direct relationship Simpler; upgrade to intermediate node later
NULL FK (optional relation) Absent relationship No node created; absence is the signal
Polymorphic FK (Rails style) Multiple labels or relationship types Split into type specific rels
Self referential FK Same label relationship :Employee {managerId} → (e) [:REPORTS TO] (m)
Audit/history columns Intermediate versioning node See References for versioning pattern
Supernode Detection and Mitigation
Detect:
Node with degree median for its label = supernode candidate. Any node with 100K relationships will degrade traversal queries that pass through it.
Causes:
Domain supernodes: airports, celebrities, popular hashtags — unavoidable
Modeling supernodes: gender, country, status modeled as nodes with millions of edges — avoidable
Mitigation strategies (in priority order):
Strategy When to use Implementation
Query direction Directional asymmetry exists Query from low degree side; exploit direction
Relationship type split Supernode serves multiple roles :FOLLOWS + :FAN instead of single :RELATED TO
Label segregation Supernode conflates entity types :Celebrity vs :User → query only relevant subtype
Bucket pattern Time series or high volume event nodes See below
Avoid modeling Low cardinality categoricals Use label instead of node ( :Active not (:Status {name:"Active"}) )
Join hint Query tuning last resort USING JOIN ON n in Cypher
Bucket pattern (time series / high volume):
Naming Conventions
Element Convention Good Bad
Node label PascalCase, singular noun :Person , :BlogPost :person , :blog posts , :Entity
Relationship type SCREAMING SNAKE CASE, verb phrase :ACTED IN , :WORKS FOR :actedin , :relatedTo , :HAS
Property key camelCase firstName , createdAt FirstName , first name
Constraint name snake case descriptive person id unique constraint1
Index name snake case descriptive person name idx index2
Schema Enforcement — What to Create for Each Element
Run all DDL with IF NOT EXISTS . Apply before importing data.
After creating indexes, poll until ONLINE:
Do NOT use an index until state = ONLINE .
Vector / Embedding Property Modeling
Store embeddings on dedicated :Chunk nodes, never on business nodes:
Rules:
Chunk node: text (source text), embedding (float array), chunkIndex (int)
Parent document: metadata only (title, url, createdAt)
Vector index on c.embedding only
Chunk size 200–500 tokens with 20% overlap is production default [field]
Do NOT put embedding on :Document — makes the node too large and pollutes traversal
Anti Patterns Table
Anti pattern Problem Fix
Generic labels :Entity , :Node No filtering benefit; all nodes scan Use domain labels :Person , :Product
Generic rel types :RELATED TO , :HAS Can't filter by relationship type Use semantic types :PURCHASED , :AUTHORED
Low cardinality value as node Supernode ( :Status {name:"active"} → millions of edges) Use label :Active instead
Property as label ( n.type = 'VIP' + :VIP label both exist) Inconsistency, duplication Pick one; prefer label if used in traversal
Storing embeddings on business node Node bloat, slow traversal Dedicated :Chunk node
MERGE without uniqueness constraint Duplicate nodes silently created Add constraint before any MERGE
Missing relationship direction meaning Arbitrary direction; confusing model Direction = semantic flow of action
Junction table modeled as bare property Loses history and extensibility Intermediate node with its own properties
id as property name id(n) is a deprecated Cypher function (use elementId(n) ); bare id is fine as a property name in practice, but domain qualified names ( personId , movieId ) are clearer and avoid any future ambiguity Prefer personId , movieId , tmdbId where it aids readability
All dates as strings No range queries; no temporal operators Use Neo4j date() or datetime() type
Output Format — Schema Assessment
When reviewing an existing model:
Severity semantics:
Severity Meaning Action
ERROR Model correctness failure (duplicates possible, data loss risk) Stop; fix before proceeding
WARNING Performance or extensibility risk Report; ask user before proceeding
INFO Style or convention deviation Surface; continue
Provenance Labels
[official] — stated directly in Neo4j docs
[derived] — follows from documented behavior
[field] — community heuristic; treat as default but validate
Checklist
[ ] Use cases (≥5 queries) defined before modeling
[ ] Schema inspected on existing database before changes proposed
[ ] Every MERGE target node label has a uniqueness constraint
[ ] No generic labels ( :Entity , :Node , :Thing )
[ ] No generic relationship types ( :RELATED TO , :HAS , :CONNECTED TO )
[ ] Relationship direction encodes semantic meaning
[ ] N ary or propertied relationships use intermediate nodes
[ ] High cardinality values stored as properties, not nodes
[ ] Low cardinality categoricals used as labels, not property nodes
[ ] Embeddings on dedicated :Chunk nodes, not business nodes
[ ] Supernode candidates identified and mitigated
[ ] All DDL uses IF NOT EXISTS
[ ] Indexes polled to ONLINE before use
[ ] Assessment output follows the structured format above
[ ] Every prohibition paired with a concrete fix
References
Load on demand:
[references/modeling patterns.md](references/modeling patterns.md) — time series, versioning, multi tenancy, linked list, access control patterns
[Neo4j Data Modeling Guide](https://neo4j.com/docs/getting started/data modeling/guide data modeling/)
[Neo4j Modeling Tips](https://neo4j.com/docs/getting started/data modeling/modeling tips/)
[GraphAcademy: Graph Data Modeling Fundamentals](https://graphacademy.neo4j.com/courses/modeling fundamentals/)
[Super Nodes — All About Super Nodes (David Allen)](https://medium.com/neo4j/graph modeling all about super nodes d6ad7e11015b)