neo4j-modeling-skill
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhen 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
- 从零开始设计图模型(领域 → 节点、关系、属性)
- 评审现有模型,检测反模式
- 决定使用节点、属性、关系还是标签
- 将关系型或文档型schema迁移为图模型
- 为n元或复杂关系设计中间节点
- 检测并缓解超级节点/高扇出问题
- 为模型选择并创建约束与索引
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
- 编写或优化Cypher →
neo4j-cypher-skill - Spring Data Neo4j(@Node、@Relationship) →
neo4j-spring-data-skill - GraphQL类型定义 →
neo4j-graphql-skill - 数据导入(LOAD CSV、APOC import) →
neo4j-import-skill
Inspect Before Designing
设计前的检查
On existing database, run first — never propose changes without current state:
cypher
CALL db.schema.visualization() YIELD nodes, relationships RETURN nodes, relationships;
SHOW CONSTRAINTS YIELD name, type, labelsOrTypes, properties RETURN name, type, labelsOrTypes, properties;
SHOW INDEXES YIELD name, type, labelsOrTypes, state WHERE state = 'ONLINE' RETURN name, type, labelsOrTypes;If APOC available:
cypher
CALL apoc.meta.schema() YIELD value RETURN value;MCP tool map:
| Operation | Tool |
|---|---|
| Inspect schema | |
| |
| |
对于现有数据库,先执行以下操作——绝不要在不了解当前状态的情况下提出更改:
cypher
CALL db.schema.visualization() YIELD nodes, relationships RETURN nodes, relationships;
SHOW CONSTRAINTS YIELD name, type, labelsOrTypes, properties RETURN name, type, labelsOrTypes, properties;
SHOW INDEXES YIELD name, type, labelsOrTypes, state WHERE state = 'ONLINE' RETURN name, type, labelsOrTypes;如果APOC可用:
cypher
CALL apoc.meta.schema() YIELD value RETURN value;MCP工具映射:
| 操作 | 工具 |
|---|---|
| 检查schema | |
| |
| |
Defaults — Apply to Every Model
默认规则——适用于所有模型
- Use-case first — list 5+ queries the model must answer before designing
- Nodes = entities (nouns) with identity; rels = connections (verbs) with direction
- Labels PascalCase; rel types SCREAMING_SNAKE_CASE; properties camelCase
- Every node type used in MERGE has a uniqueness constraint on its key property
- No generic labels (,
:Entity,:Node); no generic rel types (:Thing,:RELATED_TO):HAS - Rel direction encodes semantic meaning — not arbitrary
- Inspect schema before proposing any change on an existing database
- All constraint/index DDL uses — safe to rerun
IF NOT EXISTS
- 以用例为先——设计前列出模型必须支持的5个以上查询
- 节点 = 具有标识的实体(名词);关系 = 具有方向的连接(动词)
- 标签采用PascalCase;关系类型采用SCREAMING_SNAKE_CASE;属性采用camelCase
- 所有用于MERGE的节点类型,其关键属性都要有唯一性约束
- 禁止使用通用标签(、
:Entity、:Node);禁止使用通用关系类型(:Thing、:RELATED_TO):HAS - 关系方向需体现语义——不可随意设定
- 对现有数据库提出任何更改前,先检查schema
- 所有约束/索引DDL语句都使用——可安全重复执行
IF NOT EXISTS
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 |
| 问题 | 答案 | 建模为 |
|---|---|---|
| 它是具有标识、可作为查询入口的事物吗? | 是 | 节点 |
| 它是两个事物之间带有方向的连接吗? | 是 | 关系 |
| 该连接有自己的属性或多个目标吗? | 是 | 中间节点 |
| 它是始终随父节点返回、不会单独被过滤的标量值吗? | 是 | 父节点的属性 |
| 它是用于基于类型过滤或路径遍历的分类吗? | 是 | 标签(而非属性) |
| 同一属性值在多个节点中重复出现(低基数)吗? | 是 | 标签,而非属性节点 |
| 它是连接2个以上实体的事实吗? | 是 | 中间节点 |
Property vs Label — Decision Table
属性与标签的决策表
| Use label when | Use property when |
|---|---|
Values are few, fixed, used as traversal filters ( | Values are many, dynamic, or unique per node |
You traverse by type ( | You filter by value ( |
| Category drives index selection | Fine-grained value drives range scans |
Example: | Example: |
Rule: adding a label is cheap; scanning all nodes is fast. Never model high-cardinality values as labels.
:Label| 使用标签的场景 | 使用属性的场景 |
|---|---|
值数量少、固定,用于遍历过滤( | 值数量多、动态,或每个节点唯一 |
按类型遍历( | 按值过滤( |
| 分类决定索引选择 | 细粒度值驱动范围扫描 |
示例: | 示例: |
规则:添加标签成本低;扫描所有节点速度快。绝不要将高基数值建模为标签。
:LabelIntermediate Node Pattern
中间节点模式
Use when a relationship needs its own properties, connects >2 entities, or is independently queryable.
Before (relationship with property — limited):
(Person)-[:ACTED_IN {role: "Neo"}]->(Movie)
// Cannot query roles independent of moviesAfter (intermediate node — queryable, extensible):
(Person)-[:PLAYED]->(Role {name: "Neo"})-[:IN]->(Movie)
// MATCH (r:Role) WHERE r.name STARTS WITH 'Neo' RETURN rEmployment overlap example:
cypher
// Find colleagues who overlapped at same company
MATCH (p1:Person)-[:WORKED_AT]->(e1:Employment)-[:AT]->(c:Company)<-[:AT]-(e2:Employment)<-[:WORKED_AT]-(p2:Person)
WHERE p1 <> p2
AND e1.startDate <= e2.endDate AND e2.startDate <= e1.endDate
RETURN p1.name, p2.name, c.namePromote 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
当关系需要拥有自己的属性、连接2个以上实体,或可被独立查询时使用。
优化前(带属性的关系——局限性大):
(Person)-[:ACTED_IN {role: "Neo"}]->(Movie)
// 无法独立于电影查询角色优化后(中间节点——可查询、可扩展):
(Person)-[:PLAYED]->(Role {name: "Neo"})-[:IN]->(Movie)
// MATCH (r:Role) WHERE r.name STARTS WITH 'Neo' RETURN r就业重叠示例:
cypher
// 查找在同一家公司有工作重叠的同事
MATCH (p1:Person)-[:WORKED_AT]->(e1:Employment)-[:AT]->(c:Company)<-[:AT]-(e2:Employment)<-[:WORKED_AT]-(p2:Person)
WHERE p1 <> p2
AND e1.startDate <= e2.endDate AND e2.startDate <= e1.endDate
RETURN p1.name, p2.name, c.name当出现以下情况时,将关系升级为中间节点:
- 关系有2个以上属性
- 关系是其他查询的主体
- 多个实体共享相同的连接上下文
- 需要在一个事实中连接2个以上实体
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 |
| 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 | |
| Audit/history columns | Intermediate versioning node | See References for versioning pattern |
| 关系型结构 | 图模型等价物 | 说明 |
|---|---|---|
| 表行 | 节点 | 每个表对应一个标签(可按需添加更多) |
| 列(标量) | 节点属性 | |
| 主键 | 带唯一性约束的属性 | 使用 |
| 外键 | 关系 | 方向:从依赖方→被引用方 |
| 多对多连接表 | 中间节点 | 尤其当连接表有自己的列时 |
| 连接表(无自有列) | 直接关系 | 更简单;后续可升级为中间节点 |
| NULL外键(可选关系) | 不存在的关系 | 不创建节点;缺失即为信号 |
| 多态外键(Rails风格) | 多个标签或关系类型 | 拆分为特定类型的关系 |
| 自引用外键 | 同标签关系 | |
| 审计/历史列 | 中间版本节点 | 参考版本控制模式(见参考文献) |
Supernode Detection and Mitigation
超级节点检测与缓解
Detect:
cypher
// Find top-10 highest-degree nodes
MATCH (n)
RETURN labels(n) AS labels, elementId(n) AS id, count{ (n)--() } AS degree
ORDER BY degree DESC LIMIT 10Node 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 | |
| Label segregation | Supernode conflates entity types | |
| Bucket pattern | Time-series or high-volume event nodes | See below |
| Avoid modeling | Low-cardinality categoricals | Use label instead of node ( |
| Join hint | Query tuning last resort | |
Bucket pattern (time-series / high-volume):
cypher
// Instead of: (:User)-[:VIEWED]->(:Page) (millions of rels per user)
// Bucket by hour:
(u:User)-[:VIEWED_IN]->(b:ViewBucket {userId: u.id, hour: '2025-04-28T14'})-[:VIEWED]->(p:Page)
// Query last hour's views without traversing full history:
MATCH (u:User {id: $uid})-[:VIEWED_IN]->(b:ViewBucket {hour: $hour})-[:VIEWED]->(p)
RETURN p.url检测:
cypher
// 查找度数最高的前10个节点
MATCH (n)
RETURN labels(n) AS labels, elementId(n) AS id, count{ (n)--() } AS degree
ORDER BY degree DESC LIMIT 10度数远高于其标签中位数的节点即为超级节点候选。任何关系数超过10万的节点都会降低经过它的遍历查询性能。
原因:
- 领域超级节点:机场、名人、热门标签——不可避免
- 建模导致的超级节点:将性别、国家、状态建模为节点,产生数百万条边——可避免
缓解策略(按优先级排序):
| 策略 | 适用场景 | 实现方式 |
|---|---|---|
| 查询方向调整 | 存在方向不对称性 | 从低度数侧发起查询;利用关系方向 |
| 关系类型拆分 | 超级节点承担多种角色 | 用 |
| 标签隔离 | 超级节点混淆了实体类型 | |
| 分桶模式 | 时间序列或高容量事件节点 | 见下文 |
| 避免建模为节点 | 低基数分类 | 使用标签替代节点( |
| 连接提示 | 查询调优的最后手段 | 在Cypher中使用 |
分桶模式(时间序列/高容量):
cypher
// 替代方案:(:User)-[:VIEWED]->(:Page)(每个用户有数百万条关系)
// 按小时分桶:
(u:User)-[:VIEWED_IN]->(b:ViewBucket {userId: u.id, hour: '2025-04-28T14'})-[:VIEWED]->(p:Page)
// 查询最近一小时的浏览记录,无需遍历完整历史:
MATCH (u:User {id: $uid})-[:VIEWED_IN]->(b:ViewBucket {hour: $hour})-[:VIEWED]->(p)
RETURN p.urlNaming Conventions
命名规范
| Element | Convention | Good | Bad |
|---|---|---|---|
| Node label | PascalCase, singular noun | | |
| Relationship type | SCREAMING_SNAKE_CASE, verb phrase | | |
| Property key | camelCase | | |
| Constraint name | snake_case descriptive | | |
| Index name | snake_case descriptive | | |
| 元素 | 规范 | 示例(正确) | 示例(错误) |
|---|---|---|---|
| 节点标签 | PascalCase,单数名词 | | |
| 关系类型 | SCREAMING_SNAKE_CASE,动词短语 | | |
| 属性键 | camelCase | | |
| 约束名称 | snake_case,描述性命名 | | |
| 索引名称 | snake_case,描述性命名 | | |
Schema Enforcement — What to Create for Each Element
Schema强制实施——为各元素创建的内容
Run all DDL with . Apply before importing data.
IF NOT EXISTScypher
// 1. Uniqueness constraint — every node type used in MERGE
CREATE CONSTRAINT person_id_unique IF NOT EXISTS
FOR (p:Person) REQUIRE p.id IS UNIQUE;
// 2. Existence constraint (Enterprise) — mandatory properties
CREATE CONSTRAINT person_name_exists IF NOT EXISTS
FOR (p:Person) REQUIRE p.name IS NOT NULL;
// 3. Property type constraint (Enterprise) — enforce data type
CREATE CONSTRAINT person_born_integer IF NOT EXISTS
FOR (p:Person) REQUIRE p.born IS :: INTEGER;
// 4. Key constraint (Enterprise) — unique + exists in one
CREATE CONSTRAINT movie_tmdbid_key IF NOT EXISTS
FOR (m:Movie) REQUIRE m.tmdbId IS NODE KEY;
// 5. Range index — equality and range filters on properties
CREATE INDEX person_name_idx IF NOT EXISTS
FOR (p:Person) ON (p.name);
// 6. Fulltext index — CONTAINS, STARTS WITH, free text search
CREATE FULLTEXT INDEX person_fulltext IF NOT EXISTS
FOR (n:Person) ON EACH [n.name, n.bio];
// 7. Vector index — embedding similarity search
CREATE VECTOR INDEX chunk_embedding_idx IF NOT EXISTS
FOR (c:Chunk) ON (c.embedding)
OPTIONS { indexConfig: { `vector.dimensions`: 1536, `vector.similarity_function`: 'cosine' } };
// 8. Relationship index — filter on rel properties
CREATE INDEX acted_in_year_idx IF NOT EXISTS
FOR ()-[r:ACTED_IN]-() ON (r.year);After creating indexes, poll until ONLINE:
cypher
SHOW INDEXES YIELD name, state WHERE state <> 'ONLINE' RETURN name, state;Do NOT use an index until state = .
ONLINE所有DDL语句都使用。在导入数据前应用。
IF NOT EXISTScypher
// 1. 唯一性约束——所有用于MERGE的节点类型
CREATE CONSTRAINT person_id_unique IF NOT EXISTS
FOR (p:Person) REQUIRE p.id IS UNIQUE;
// 2. 存在性约束(企业版)——必填属性
CREATE CONSTRAINT person_name_exists IF NOT EXISTS
FOR (p:Person) REQUIRE p.name IS NOT NULL;
// 3. 属性类型约束(企业版)——强制数据类型
CREATE CONSTRAINT person_born_integer IF NOT EXISTS
FOR (p:Person) REQUIRE p.born IS :: INTEGER;
// 4. 键约束(企业版)——唯一性+存在性二合一
CREATE CONSTRAINT movie_tmdbid_key IF NOT EXISTS
FOR (m:Movie) REQUIRE m.tmdbId IS NODE KEY;
// 5. 范围索引——属性的等值与范围过滤
CREATE INDEX person_name_idx IF NOT EXISTS
FOR (p:Person) ON (p.name);
// 6. 全文索引——CONTAINS、STARTS WITH、自由文本搜索
CREATE FULLTEXT INDEX person_fulltext IF NOT EXISTS
FOR (n:Person) ON EACH [n.name, n.bio];
// 7. 向量索引——嵌入向量相似度搜索
CREATE VECTOR INDEX chunk_embedding_idx IF NOT EXISTS
FOR (c:Chunk) ON (c.embedding)
OPTIONS { indexConfig: { `vector.dimensions`: 1536, `vector.similarity_function`: 'cosine' } };
// 8. 关系索引——基于关系属性过滤
CREATE INDEX acted_in_year_idx IF NOT EXISTS
FOR ()-[r:ACTED_IN]-() ON (r.year);创建索引后,轮询直到状态变为ONLINE:
cypher
SHOW INDEXES YIELD name, state WHERE state <> 'ONLINE' RETURN name, state;不要在状态变为前使用索引。
ONLINEVector / Embedding Property Modeling
向量/嵌入属性建模
Store embeddings on dedicated nodes, never on business nodes:
:Chunk(:Document)-[:HAS_CHUNK]->(c:Chunk {text: "...", embedding: [...]})Rules:
- Chunk node: (source text),
text(float array),embedding(int)chunkIndex - Parent document: metadata only (title, url, createdAt)
- Vector index on only
c.embedding - Chunk size 200–500 tokens with 20% overlap is production default [field]
- Do NOT put embedding on — makes the node too large and pollutes traversal
:Document
将嵌入向量存储在专用的节点上,绝不要存储在业务节点上:
:Chunk(:Document)-[:HAS_CHUNK]->(c:Chunk {text: "...", embedding: [...]})规则:
- Chunk节点:包含(源文本)、
text(浮点数组)、embedding(整数)chunkIndex - 父文档:仅存储元数据(标题、url、createdAt)
- 仅在上创建向量索引
c.embedding - 生产环境默认的分块大小为200–500 tokens,重叠率20% [field]
- 不要将嵌入向量放在节点上——会导致节点过大,拖慢遍历速度
:Document
Anti-Patterns Table
反模式对照表
| Anti-pattern | Problem | Fix |
|---|---|---|
Generic labels | No filtering benefit; all nodes scan | Use domain labels |
Generic rel types | Can't filter by relationship type | Use semantic types |
| Low-cardinality value as node | Supernode ( | Use label |
Property as label ( | Inconsistency, duplication | Pick one; prefer label if used in traversal |
| Storing embeddings on business node | Node bloat, slow traversal | Dedicated |
| 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 |
| Conflicts with driver internal | Use |
| All dates as strings | No range queries; no temporal operators | Use Neo4j |
| 反模式 | 问题 | 修复方案 |
|---|---|---|
通用标签 | 无过滤优势;需扫描所有节点 | 使用领域标签 |
通用关系类型 | 无法按关系类型过滤 | 使用语义化类型 |
| 低基数值建模为节点 | 产生超级节点( | 使用标签 |
属性与标签重复(同时存在 | 不一致、重复 | 二选一;若用于遍历则优先选择标签 |
| 嵌入向量存储在业务节点上 | 节点膨胀,遍历缓慢 | 使用专用 |
| MERGE操作无唯一性约束 | 会静默创建重复节点 | 在执行任何MERGE前添加约束 |
| 关系方向无语义 | 方向随意;模型易混淆 | 方向 = 动作的语义流向 |
| 连接表建模为普通属性 | 丢失历史信息与扩展性 | 使用带自有属性的中间节点 |
使用 | 与驱动内部 | 使用 |
| 所有日期存储为字符串 | 无法执行范围查询;无法使用时间运算符 | 使用Neo4j的 |
Output Format — Schema Assessment
输出格式——Schema评估
When reviewing an existing model:
undefined评审现有模型时:
undefinedSchema Assessment
Schema评估
Compliant
合规项
- [constraint / pattern that is correct]
- [正确的约束/模式]
Issues Found
发现的问题
[Title] — Severity: ERROR / WARNING / INFO
[标题] — 严重程度:ERROR / WARNING / INFO
- Current: what the model does
- Problem: why it is an issue
- Fix: specific Cypher DDL or model change
- 当前状态:模型的现有做法
- 问题:为何这是一个问题
- 修复方案:具体的Cypher DDL语句或模型更改
Recommended Schema
推荐的Schema
Node Labels
节点标签
- :Label {key: TYPE, prop: TYPE, ...} → constraints: [list]
- :Label {key: TYPE, prop: TYPE, ...} → 约束:[列表]
Relationships
关系
- (:LabelA)-[:TYPE {prop: TYPE}]->(:LabelB)
- (:LabelA)-[:TYPE {prop: TYPE}]->(:LabelB)
Constraints to Create
需创建的约束
[CREATE CONSTRAINT ... statements]
[CREATE CONSTRAINT ... 语句]
Indexes to Create
需创建的索引
[CREATE INDEX ... statements]
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 |
---[CREATE INDEX ... 语句]
严重程度语义:
| 严重程度 | 含义 | 操作 |
|---|---|---|
| `ERROR` | 模型正确性问题(可能产生重复数据、存在数据丢失风险) | 停止操作;修复后再继续 |
| `WARNING` | 存在性能或扩展性风险 | 上报;继续前需询问用户 |
| `INFO` | 风格或规范偏差 | 告知用户;继续操作 |
---Provenance Labels
来源标签
- — stated directly in Neo4j docs
[official] - — follows from documented behavior
[derived] - — community heuristic; treat as default but validate
[field]
- — 直接来自Neo4j官方文档
[official] - — 由文档记录的行为推导而来
[derived] - — 社区经验法则;作为默认方案但需验证
[field]
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 nodes, not business nodes
:Chunk - 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
- 建模前已定义用例(≥5个查询)
- 对现有数据库提出更改前已检查schema
- 所有MERGE目标节点标签都有唯一性约束
- 无通用标签(、
:Entity、:Node):Thing - 无通用关系类型(、
:RELATED_TO、:HAS):CONNECTED_TO - 关系方向体现语义
- n元或带属性的关系使用中间节点
- 高基数值存储为属性,而非节点
- 低基数分类使用标签,而非属性节点
- 嵌入向量存储在专用节点上,而非业务节点
:Chunk - 已识别并缓解超级节点候选
- 所有DDL语句使用
IF NOT EXISTS - 索引在状态变为ONLINE后才使用
- 评估输出遵循上述结构化格式
- 每个禁止项都配有具体修复方案
References
参考文献
Load on demand:
- references/modeling-patterns.md — time-series, versioning, multi-tenancy, linked list, access control patterns
- Neo4j Data Modeling Guide
- Neo4j Modeling Tips
- GraphAcademy: Graph Data Modeling Fundamentals
- Super Nodes — All About Super Nodes (David Allen)
按需加载:
- references/modeling-patterns.md — 时间序列、版本控制、多租户、链表、访问控制模式
- Neo4j Data Modeling Guide
- Neo4j Modeling Tips
- GraphAcademy: Graph Data Modeling Fundamentals
- Super Nodes — All About Super Nodes (David Allen)