Property Graph Modeling with Python — Core Concepts
What makes a property graph
A property graph has four building blocks:
- Nodes — Entities like people, products, or events.
- Labels — Categories for nodes. A node can have multiple labels (
Person,Employee). - Relationships — Directed connections between nodes with a single type (
WORKS_AT,PURCHASED). - 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.
See Also
- Python Knowledge Graph Construction How Python builds a web of facts about the world — connecting people, places, and ideas so computers can answer real questions.
- Python Neo4j Integration How Python talks to a database that thinks in connections instead of rows and columns.
- Python Rdf Sparql Queries How Python reads and asks questions about the web's universal language for describing things and their connections.
- Python Arima Forecasting How ARIMA models use patterns in past numbers to predict the future, explained like a bedtime story.
- Python Autocorrelation Analysis How today's number is connected to yesterday's, and why that connection is the secret weapon of time series analysis.