Chapter 07 · Graph Data Modeling: Aggregates, Relationships, Hyperedges, Hierarchies, and Temporal Graphs

Choose Node vs Property vs Relationship by Query Pattern, Identity, Cardinality, and Evolution Needs

Choose graph structures from workload, identity, degree and lifecycle evidence instead of diagram aesthetics.

Intermediate120–145 minutesModel-choice/refactoring labNeo4j 2026.07.1 Community · Cypher 25Last reviewed: September 2026

Learning outcomes

AtlasMart's original integration team copied fields from several source systems into one graph without deciding which facts deserved independent identity. The result is visually connected but difficult to query or evolve. This lesson makes each modeling choice answerable from workload, identity, cardinality and lifecycle.

01

Choose node, property, or relationship based on independent identity, reuse, traversal and lifecycle needs.

02

Distinguish low-cardinality scalar attributes from entities that deserve graph identity.

03

Use degree/cardinality measurements before accepting a relationship design.

04

Explain when relationship properties are sufficient and when an intermediate fact node is justified.

05

Refactor a generic relationship model into explicit AtlasMart semantics and verify query clarity.

Chapter 07 baseline · reviewed 9 September 2026

The mandatory lab continues the accepted course baseline: Neo4j Community 2026.07.1, database neo4j, explicit CYPHER 25 for version-sensitive examples, authentication enabled, no mandatory APOC/GDS plugin, and stable AtlasMart domain identifiers from Chapters 01–06. Neo4j 5.26.30 remains the LTS comparison line. Modeling examples use only Community-compatible graph and uniqueness features.

Evidence and safety note

This generation environment does not run Neo4j or Docker. Commands were checked against current official documentation but were not executed here. Expected outputs are deterministic fixture invariants, not fabricated captures. All destructive/refactoring steps are scoped to Chapter 07 identifiers or labTag='ch07'; never replace them with unconstrained production matches.

Re-establish the Chapter 07 modeling fixture

The lab deliberately creates a small supply-chain slice whose questions require explicit semantics: two suppliers, one product, one store, dated supply agreements, and a component hierarchy. Stable domain IDs remain the durable identity; internal element IDs are not used as business keys.

Cypher · Community-compatible identity constraints
CYPHER 25CREATE CONSTRAINT supplier_id IF NOT EXISTS FOR (s:Supplier) REQUIRE s.supplierId IS UNIQUE;CREATE CONSTRAINT store_id IF NOT EXISTS FOR (s:Store) REQUIRE s.storeId IS UNIQUE;CREATE CONSTRAINT product_id IF NOT EXISTS FOR (p:Product) REQUIRE p.productId IS UNIQUE;CREATE CONSTRAINT category_id IF NOT EXISTS FOR (c:Category) REQUIRE c.categoryId IS UNIQUE;CREATE CONSTRAINT agreement_id IF NOT EXISTS FOR (a:SupplyAgreement) REQUIRE a.agreementId IS UNIQUE;CREATE CONSTRAINT component_id IF NOT EXISTS FOR (c:Component) REQUIRE c.componentId IS UNIQUE;
Cypher · deterministic supply-agreement fixture
CYPHER 25MERGE (sup1:Supplier {supplierId:'S-7001'}) SET sup1.name='Northstar Components', sup1.labTag='ch07'MERGE (sup2:Supplier {supplierId:'S-7002'}) SET sup2.name='BlueRiver Plastics', sup2.labTag='ch07'MERGE (store:Store {storeId:'ST-7001'}) SET store.name='AtlasMart Central', store.labTag='ch07'MERGE (prod:Product {productId:'P-7001'}) SET prod.name='Trail Camera Kit', prod.labTag='ch07'MERGE (cat:Category {categoryId:'CAT-7001'}) SET cat.name='Outdoor Imaging', cat.labTag='ch07'MERGE (prod)-[:IN_CATEGORY]->(cat)MERGE (a1:SupplyAgreement {agreementId:'SA-7001'})SET a1.validFrom=date('2026-01-01'), a1.validTo=date('2026-07-01'),    a1.recordedFrom=datetime('2026-01-02T09:00:00Z'), a1.recordedTo=datetime('9999-12-31T00:00:00Z'),    a1.unitPrice=84.0, a1.currency='USD', a1.labTag='ch07'MERGE (sup1)-[:PARTY_TO]->(a1)MERGE (a1)-[:SUPPLIES]->(prod)MERGE (a1)-[:DELIVERS_TO]->(store)MERGE (a2:SupplyAgreement {agreementId:'SA-7002'})SET a2.validFrom=date('2026-07-01'), a2.validTo=date('2027-01-01'),    a2.recordedFrom=datetime('2026-06-15T10:00:00Z'), a2.recordedTo=datetime('9999-12-31T00:00:00Z'),    a2.unitPrice=79.0, a2.currency='USD', a2.labTag='ch07'MERGE (sup2)-[:PARTY_TO]->(a2)MERGE (a2)-[:SUPPLIES]->(prod)MERGE (a2)-[:DELIVERS_TO]->(store);
Cypher · deterministic bill-of-materials fixture
CYPHER 25MERGE (kit:Component {componentId:'CMP-KIT'}) SET kit.name='Trail Camera Kit', kit.labTag='ch07'MERGE (cam:Component {componentId:'CMP-CAM'}) SET cam.name='Camera Module', cam.labTag='ch07'MERGE (case:Component {componentId:'CMP-CASE'}) SET case.name='Weather Case', case.labTag='ch07'MERGE (lens:Component {componentId:'CMP-LENS'}) SET lens.name='Lens Assembly', lens.labTag='ch07'MERGE (kit)-[:CONTAINS_COMPONENT {quantity:1}]->(cam)MERGE (kit)-[:CONTAINS_COMPONENT {quantity:1}]->(case)MERGE (cam)-[:CONTAINS_COMPONENT {quantity:1}]->(lens);

Use SHOW CONSTRAINTS and targeted counts before refactoring. A successful query proves only the fixture state, not that the model scales to production degree distributions or workload volume.

1. Start from questions, not shapes

A property graph is not improved merely by drawing more nodes. Model a value as a property when it primarily describes one entity and does not need independent traversal or lifecycle. Model a node when the thing has durable identity, participates in multiple relationships, is shared/reused, or must be independently constrained/evolved. Model a relationship when the connection itself has direct binary semantics between two endpoints.

Question Likely structure Why
What is a product name? Product property Scalar state owned by one product.
Which category classifies a product? Product → IN_CATEGORY → Category Category is shared and independently navigated.
Which supplier currently supplies a product? SupplyAgreement intermediate fact The fact includes supplier, product, store, price and validity.
How many units of component X are inside assembly Y? CONTAINS_COMPONENT relationship property Quantity describes that specific binary edge.

2. Cardinality and degree are design inputs

Before committing to a relationship, measure expected one-to-one, one-to-many and many-to-many behavior. High degree is not automatically wrong, but it changes fan-out, result cardinality and memory. A generic hub node can turn simple business questions into large expansions.

Cypher · inspect degree distribution on the fixture
CYPHER 25MATCH (n)WHERE n.labTag='ch07'OPTIONAL MATCH (n)-[r]-()RETURN labels(n)[0] AS kind, elementId(n) AS localId, count(r) AS degreeORDER BY degree DESC;

elementId() is shown only to distinguish fixture rows during inspection. It is not promoted to a durable business identifier.

3. Deliberately poor model: generic RELATED_TO edges

Wrong · semantics hidden in a string property
CYPHER 25MATCH (s:Supplier {supplierId:'S-7001'}),(p:Product {productId:'P-7001'}),(st:Store {storeId:'ST-7001'})CREATE (s)-[:RELATED_TO {kind:'supplies', productId:p.productId, storeId:st.storeId, validFrom:'2026-01-01'}]->(p);

This edge duplicates foreign-key-like strings, hides the store endpoint, stores a date as text and gives the business fact no identity. The repair is the explicit SupplyAgreement node already in the fixture. That node can be constrained, versioned and connected to all participants without inventing one overloaded relationship type.

4. Relationship properties are for relationship-owned state

A relationship property is appropriate when the value is inseparable from one binary association. quantity on CONTAINS_COMPONENT is a good example. But when a fact involves three or more independent participants, needs its own identifier, has lifecycle/history, or will be referenced by other facts, reification into an intermediate node is usually clearer.

Hands-on refactoring lab

Inventory five AtlasMart questions, map each to graph structures, run the degree query, add the deliberately generic edge, then compare the query needed to answer “who supplies P-7001 to ST-7001 on 2026-05-01?” against the explicit agreement model.

Cypher · explicit model answers the business question
CYPHER 25MATCH (s:Supplier)-[:PARTY_TO]->(a:SupplyAgreement)-[:SUPPLIES]->(p:Product {productId:'P-7001'}),      (a)-[:DELIVERS_TO]->(st:Store {storeId:'ST-7001'})WHERE a.validFrom <= date('2026-05-01') AND date('2026-05-01') < a.validToRETURN s.supplierId, a.agreementId, a.unitPrice, a.currency;

Check your understanding

  1. When should a value usually remain a property?
  2. What makes an intermediate fact node attractive?
  3. Is a high-degree node automatically a modeling error?
  4. Why is RELATED_TO usually weak domain modeling?
  5. Why avoid elementId() as a business key?
Review the answers

1. When it primarily describes one owning entity and does not need independent identity, traversal, constraints or lifecycle.

2. Independent identity, more than two participants, temporal lifecycle, external references, or richer fact-specific state.

3. No. It is a workload/cost signal that must be measured and justified.

4. It hides semantics in properties and forces every query to rediscover what the connection means.

5. It is implementation-facing identity, not a stable cross-environment domain contract.

Summary and next step

Good graph modeling starts from identity and questions. The next lesson focuses on n-ary facts and hyperedge-like concepts, where reification becomes a deliberate semantic tool rather than a workaround.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.