Neo4j in Practice: When a Graph Beats a Regular Database
Neo4j is a graph database where data lives as nodes and relationships, not as rows in tables. A regular relational DB (PostgreSQL, MySQL) is excellent for transactions, reports, and CRUD. But when product value sits in paths, chains, and multi-hop links, SQL with JOINs and recursive CTEs quickly becomes heavy and fragile. Below - practical signals for when Neo4j wins over a "regular" database, and when a graph is still premature.
- Graph - nodes (entities) + edges (typed relationships) + properties
- Neo4j strength - relationship traversal at variable depth
- SQL weak spot - deep JOINs, "friends of friends", fraud/access chains
- Not a replacement - transactional CRUD, inventory, classic analytics
- Rule - choose Neo4j when the question sounds like "through whom / through what / within N hops"
How a graph differs from tables
In PostgreSQL you model relationships with foreign keys and link tables (user_roles, follows, order_items). A fixed-depth query is still readable. A query like "find all nodes within 3-5 hops with filters by relationship type" turns into a recursive CTE or many JOINs - and the execution plan grows with depth.
In Neo4j a relationship is a first-class citizen. The model is closer to how people describe the domain:
(:Person)-[:WORKS_AT]->(:Company)(:Account)-[:TRANSFERED_TO]->(:Account)(:User)-[:HAS_ROLE]->(:Role)-[:ALLOWS]->(:Permission)
A Cypher query describes a pattern, not a "glueing of tables". That helps when path depth is unknown upfront or changes often.
When Neo4j beats a regular database
Choose a graph when requirements regularly include:
- Variable-depth traversal - "friends of friends", supply chain, path to the ultimate owner.
- Relationship pattern matching - find subgraphs like "A linked to B via C under condition D".
- Recommendations and similar entities via the graph - not only by text, but by shared neighbors and paths.
- Fraud and risk - rings, shared devices, short paths between "unrelated" accounts.
- Permissions and dependencies - who can access what through roles, groups, and inheritance.
- Knowledge graph - products, documents, people, systems, and explicit relationships between them.
Signals that SQL already "hurts":
- recursive CTEs get longer with every release;
- the team fears changing the relationship schema;
- latency grows on deep JOINs, not on simple SELECTs;
- the business asks "show the chain", not "give me the row by id".
Practical example: path and shared neighbors
Suppose you need companies linked to a person within at most two hops, filtered by relationship type.
MATCH path = (p:Person {id: $personId})-[:WORKS_AT|KNOWS*1..2]-(c:Company)
WHERE c.country = $country
RETURN DISTINCT c.name AS company, length(path) AS hops
ORDER BY hops, company
LIMIT 50;
The same intent in SQL usually needs recursion, careful deduplication, and depth limits. In a graph the formulation is closer to the business question.
Shared-neighbors example for recommendations:
MATCH (u:User {id: $userId})-[:BOUGHT]->(:Product)<-[:BOUGHT]-(other:User)
WHERE u <> other
WITH other, count(*) AS shared
ORDER BY shared DESC
LIMIT 20
RETURN other.id, shared;
When a regular database is still better
Neo4j is not a universal PostgreSQL replacement.
Keep a relational DB (or start with one) if:
- the main flow is CRUD, orders, payments, inventory, billing;
- relationships have fixed depth (1-2 JOINs) and stay stable for years;
- you need heavy aggregates, window functions, BI-style reporting;
- the team is strong in SQL and graph queries are rare;
- "network" logic is small and one CTE handles it without pain.
Typical mistake: move the whole product into Neo4j "because graphs are trendy". You get extra ops load and a weak fit for the transactional core.
Hybrid: PostgreSQL + Neo4j
In practice a split often works:
| Layer | Where to store | Why |
|---|---|---|
| Orders, users, payments | PostgreSQL / MySQL | Transactions, integrity, reports |
| Relationships, paths, roles, dependencies | Neo4j | Traversal and pattern match |
| Meaning-based text search | Vector store | RAG / semantic search |
Source of truth for money and statuses - the relational DB. Sync the graph via events (outbox, CDC, queue) or batches. That way you do not duplicate billing in Neo4j and do not build a fraud graph on JOINs alone.
How to validate the hypothesis in 1-2 weeks
- Write down 15-20 real user/analyst questions.
- Mark how many are about paths, hops, and "through whom".
- If that share is above ~30-40%, build a Neo4j pilot on a trimmed subgraph.
- Compare with SQL: query readability, p95 latency, cost of schema changes.
- Estimate the cost of syncing data with the primary DB.
If the pilot does not win on latency/clarity - stay on PostgreSQL. A graph should fix pain, not decorate the architecture.
Bottom Line
Neo4j beats a regular DB where the product thinks in relationships: chains, roles, dependencies, fraud, knowledge graphs, graph-based recommendations. A regular DB wins where the core is transactions, fixed JOINs, and reports. A hybrid is often optimal: SQL for facts and money, a graph for structure and paths.
If you need to decide whether to move relationships into Neo4j, or design a pilot for your domain - contact me.
Frequently Asked Questions
How is Neo4j fundamentally better than PostgreSQL for relationships?
Speed and clarity of variable-depth traversal. In a graph, edges are indexed as relationships, not assembled each time via table JOINs. For 1-2 fixed JOINs PostgreSQL is often enough; for "N hops with relationship-type filters" Neo4j is usually simpler and more stable.
Can recursive CTEs in PostgreSQL replace Neo4j?
Sometimes yes, at the start. If depth is small, the graph is moderate, and queries are few - CTEs work. As depth, branching, and pattern-query count grow, CTEs become hard to maintain and expensive in the execution plan.
Do we need to move the whole product into Neo4j?
No. Move the layer where relationships are product value. Leave orders, payments, inventory, and classic CRUD in a relational DB. Otherwise you get a weak fit and extra operational complexity.
Is Cypher harder than SQL?
A different mental model, not necessarily harder. For paths and patterns Cypher is shorter and more readable. For aggregates, reporting, and window functions SQL is more familiar. The team needs a short onboarding and 5-10 reference queries for the domain.
How should we start adopting Neo4j in an existing project?
With a pilot on one scenario: for example access graphs, fraud rings, or a service dependency map. Define the source of truth, sync approach, and 10 control questions. Only after measuring latency and hit-rate decide whether to expand to other domains.