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:
NodeIndexSeekrather than scanning every node label at the start;- unexpectedly large row multiplication after
Expandoperators; - 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.
EXPLAINplans;PROFILEexecutes 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 有很多箭頭。