All writing2026.09.22 · CS 411 · Database Systems · 2 min

Graph Data Modeling and Cypher: Make Relationships First-Class

Model nodes, relationships, direction, and traversal cost in Neo4j without turning every dataset into a graph.

A graph database is useful when relationships are part of the question, not merely a way to decorate entities with arrows.

Property graph building blocks

Neo4j’s property graph model uses:

  • nodes for entities;
  • labels for node roles;
  • directed, typed relationships;
  • properties on nodes and relationships.
(:Researcher)-[:AUTHORED {position: 1}]->(:Paper)
(:Paper)-[:CITES]->(:Paper)
(:Paper)-[:ABOUT]->(:Topic)

Author position belongs on AUTHORED because it describes the connection, not either endpoint.

In plain terms: a relational junction row and a graph relationship often represent the same fact. The graph model makes following chains of those facts the central operation.

Start a traversal selectively

Create an identity constraint so the planner can locate the starting node:

CREATE CONSTRAINT researcher_id IF NOT EXISTS
FOR (r:Researcher)
REQUIRE r.id IS UNIQUE;

Then traverse from a selective anchor:

MATCH (r:Researcher {id: $researcherId})-[:AUTHORED]->(p:Paper)
      -[:ABOUT]->(t:Topic)
RETURN t.name, count(*) AS papers
ORDER BY papers DESC;

An index helps find r. It does not make an unlimited traversal cheap. Cost then depends on relationship degree, path length, and filtering.

Variable-length paths need boundaries

MATCH path = (a:Researcher {id: $id})-[:COAUTHORED_WITH*1..3]-(other)
WHERE other <> a
RETURN DISTINCT other.id, length(path)
LIMIT 100;

The 1..3, relationship type, selective start, and result limit all constrain work. An unbounded pattern over a dense graph can expand combinatorially.

In plain terms: “friends of friends” sounds small until each person has hundreds of neighbors. Path length is a performance parameter, not only a business requirement.

Read the plan

Prefix a read query with PROFILE to execute it and collect runtime statistics, or EXPLAIN to inspect the plan without running it.

Look for:

  • NodeIndexSeek rather than scanning every node label at the start;
  • unexpectedly large row multiplication after Expand operators;
  • filters applied only after a large traversal;
  • repeated expansions caused by an overly broad pattern.

Do not force an index hint first. Fix identity constraints, query predicates, and statistics before overriding the planner.

When a graph is the wrong model

Use a relational model when the workload is dominated by tabular reporting, stable joins, aggregates, and strong multi-row constraints. Use a document model when data is normally read as bounded aggregates. A graph is strongest when variable relationship paths and neighborhood questions dominate.

Review card

  • Model relationship attributes on relationships.
  • Use constraints and indexes to find selective starting nodes.
  • Traversal cost depends on degree and path length.
  • Bound variable-length paths deliberately.
  • EXPLAIN plans; PROFILE executes and measures.
  • Choose graph storage for graph-shaped questions, not graph-shaped diagrams.

Sources

當 relationship 本身就是問題的一部分,而不只是 entity 之間的裝飾箭頭時,graph database 才真正有價值。

Property graph 的基本組件

Neo4j property graph model 使用:

  • Node 表示 entity;
  • Label 表示 node role;
  • 有方向、有型別的 relationship;
  • Node 與 relationship 都可以有 property。
(:Researcher)-[:AUTHORED {position: 1}]->(:Paper)
(:Paper)-[:CITES]->(:Paper)
(:Paper)-[:ABOUT]->(:Topic)

Author position 放在 AUTHORED 上,因為它描述的是 connection,不是任一 endpoint。

白話來說: Relational junction row 和 graph relationship 常常代表同一個 fact;graph model 只是把沿著這些 fact 走訪,變成最主要的操作。

從選擇性高的起點開始

先用 identity constraint 幫 planner 找到起始 node:

CREATE CONSTRAINT researcher_id IF NOT EXISTS
FOR (r:Researcher)
REQUIRE r.id IS UNIQUE;

再從明確 anchor traversal:

MATCH (r:Researcher {id: $researcherId})-[:AUTHORED]->(p:Paper)
      -[:ABOUT]->(t:Topic)
RETURN t.name, count(*) AS papers
ORDER BY papers DESC;

Index 幫忙找到 r,不代表後面無上限的 traversal 會自動便宜。後續成本取決於 relationship degree、path length 與 filter。

Variable-length path 一定要有邊界

MATCH path = (a:Researcher {id: $id})-[:COAUTHORED_WITH*1..3]-(other)
WHERE other <> a
RETURN DISTINCT other.id, length(path)
LIMIT 100;

1..3、relationship type、selective start 與 result limit 都在限制工作量。Dense graph 上的 unbounded pattern 可能產生組合爆炸。

白話來說: 「朋友的朋友」聽起來很小,但每人有數百個鄰居時會立刻膨脹。Path length 不只是產品需求,也是效能參數。

讀懂 plan

Read query 前加 PROFILE 會實際執行並收集 runtime statistics;EXPLAIN 則只產生 plan。

優先檢查:

  • 起點是否使用 NodeIndexSeek,而不是掃描整個 label;
  • Expand 後 row 數是否意外放大;
  • Filter 是否等大量 traversal 完成後才執行;
  • Pattern 太寬造成的重複 expansion。

不要一開始就強制 index hint。先修正 identity constraint、predicate 與 statistics,再考慮 override planner。

什麼時候不需要 graph

Workload 主要是 tabular report、穩定 join、aggregate 與強 multi-row constraint 時,relational model 通常比較合適。資料通常以 bounded aggregate 一起讀取時,document model 可能更自然。Variable relationship path 和 neighborhood question 才是 graph 的強項。

複習卡

  • Relationship attribute 放在 relationship。
  • Constraint 與 index 用來找到 selective starting node。
  • Traversal cost 取決於 degree 與 path length。
  • Variable-length path 要刻意設限。
  • EXPLAIN 看 plan;PROFILE 實際執行並量測。
  • 因為問題是 graph-shaped 才選 graph,不是因為 diagram 有很多箭頭。

資料來源