Property Graph Modeling with Python — Core Concepts

What makes a property graph

A property graph has four building blocks:

  1. Nodes — Entities like people, products, or events.
  2. Labels — Categories for nodes. A node can have multiple labels (Person, Employee).
  3. Relationships — Directed connections between nodes with a single type (WORKS_AT, PURCHASED).
  4. Properties — Key-value pairs on both nodes and relationships.

Unlike RDF (which uses triples), property graphs allow rich attributes directly on relationships. A REVIEWED relationship between a User and a Product can carry rating: 4.5, text: "Great product", and timestamp: 2025-03-15 — all as first-class properties.

Modeling principles

Nouns become nodes

Identify the core entities in your domain. For an e-commerce platform: Customer, Product, Order, Category. Each gets a label and a set of identifying properties.

Verbs become relationships

The actions or connections between entities become relationship types. “Customer places an Order” becomes a PLACED relationship from Customer to Order. “Product belongs to a Category” becomes BELONGS_TO.

Adjectives become properties

Descriptive attributes sit on nodes or relationships. Customer’s name and email are node properties. The quantity and unit_price on an INCLUDES relationship between Order and Product are relationship properties.

When to promote a property to a node

A common design decision: should “Berlin” be a property on a Person node (city: "Berlin") or a separate City node connected by a LIVES_IN relationship?

Promote to a node when:

  • Multiple entities share the value (many people live in Berlin).
  • You want to traverse through it (“find all people in the same city”).
  • The value has its own properties (Berlin has a population, coordinates).

Keep as a property when:

  • It’s rarely used in queries.
  • It’s unique to each entity (like an internal ID).
  • It’s a simple scalar with no relationships of its own.

Common modeling patterns

Intermediate nodes for events

Instead of a direct PURCHASED relationship between Customer and Product, model the purchase as a node:

(Customer)-[:PLACED]->(Order)-[:INCLUDES]->(Product)

The Order node carries date, total, status. This pattern naturally supports multiple items per order, order history, and analytics.

Hyperedges via intermediate nodes

When a relationship involves more than two entities — “Alice recommended Product X to Bob” — create an intermediate node:

(Alice)-[:MADE]->(Recommendation)-[:OF]->(ProductX)
(Recommendation)-[:TO]->(Bob)

Property graphs only support binary relationships, so intermediate nodes handle n-ary cases.

Temporal modeling

To track how things change over time, add valid_from and valid_to properties to relationships:

(Alice)-[:WORKS_AT {from: 2020, to: 2023}]->(CompanyA)
(Alice)-[:WORKS_AT {from: 2023}]->(CompanyB)

Alternatively, create Employment nodes for richer temporal data.

Modeling in Python

Use Python classes to prototype your model before loading data:

from dataclasses import dataclass, field

@dataclass
class Node:
    labels: list[str]
    properties: dict

@dataclass
class Relationship:
    type: str
    start: Node
    end: Node
    properties: dict = field(default_factory=dict)

# Define the model
alice = Node(labels=["Person", "Employee"], properties={"name": "Alice", "age": 30})
acme = Node(labels=["Company"], properties={"name": "Acme Corp", "industry": "Tech"})
works = Relationship(type="WORKS_AT", start=alice, end=acme, properties={"since": 2020})

This approach lets you validate the model logic in Python before committing it to a database.

Common misconception

“Model your graph the same way you’d model a relational database.” Graph models should be query-driven, not normalization-driven. In a relational database, you normalize to reduce redundancy. In a graph, you model for traversal efficiency — sometimes duplicating data or adding redundant relationships to make common queries fast.

One thing to remember: The best graph model mirrors the questions you’ll ask. Start with your queries, then design nodes and relationships that make those queries natural single-traversal operations.

pythongraph-databasesdata-modeling

See Also