Back openDesk Edu for a sovereign, open-source education β every vote counts.
Vote nowMost software engineers are trained to think in tables. Relational databases, spreadsheets, ORMs β rows and columns are the default mental model for data. But the world doesn't organise itself into tables. Relationships between entities are often more important than the entities themselves.
A knowledge graph is a data structure that puts relationships first. Instead of a users table and a purchases table joined by a foreign key, a knowledge graph represents everything as interconnected nodes and edges.
Nodes represent entities β people, places, concepts, events. Each node has a unique identifier and can carry properties:
Node: Paris
type: City
population: 2.1M
country: France
Edges represent connections between nodes. In a property graph, edges are directional and can carry their own properties:
(Paris) -[capital_of]-> (France)
established: 508 AD
Nodes and edges are typed. This is what separates a knowledge graph from a generic graph database:
City, Company, Person, Technologycapital_of, employs, developed_by, depends_onTwo dominant models exist:
The semantic web standard. Everything is a triple: subject β predicate β object.
@prefix ex: <http://example.org/> .
ex:Paris ex:capitalOf ex:France .
ex:France ex:hasPopulation 67M .
The model used by Neo4j, Amazon Neptune, and ArangoDB:
CREATE (p:City {name: "Paris", population: 2100000})
CREATE (f:Country {name: "France", population: 67000000})
CREATE (p)-[:CAPITAL_OF]->(f)
The best way to understand knowledge graphs is to build one. Let's model a small technology ecosystem β companies, their products, and the technologies those products depend on β and run meaningful queries against it.
You need a running Neo4j instance. The quickest path:
docker run --publish=7474:7474 --publish=7687:7687 \
-e NEO4J_AUTH=neo4j/password neo4j:2025
Install the Neo4j Python driver and connect:
pip install neo4j
from neo4j import GraphDatabase
driver = GraphDatabase.driver(
"bolt://localhost:7687",
auth=("neo4j", "password")
)
def seed_graph(tx):
# Create companies
tx.run("""
MERGE (a:Company {name: "Acme Corp", founded: 2015, region: "EU"})
MERGE (b:Company {name: "Beta Inc", founded: 2018, region: "NA"})
""")
# Create products with dependencies
tx.run("""
MATCH (a:Company {name: "Acme Corp"})
MATCH (b:Company {name: "Beta Inc"})
MERGE (p1:Product {name: "AcmeCloud", version: "3.2"})
MERGE (p2:Product {name: "BetaSync", version: "1.9"})
MERGE (a)-[:DEVELOPS]->(p1)
MERGE (b)-[:DEVELOPS]->(p2)
""")
# Create technology nodes and dependencies
tx.run("""
MERGE (neo:Technology {name: "Neo4j", type: "Database"})
MERGE (py:Technology {name: "Python", type: "Language"})
MERGE (k8s:Technology {name: "Kubernetes", type: "Orchestration"})
MATCH (p1:Product {name: "AcmeCloud"})
MATCH (p2:Product {name: "BetaSync"})
MERGE (p1)-[:DEPENDS_ON]->(neo)
MERGE (p1)-[:DEPENDS_ON]->(py)
MERGE (p1)-[:DEPENDS_ON]->(k8s)
MERGE (p2)-[:DEPENDS_ON]->(py)
""")
with driver.session() as session:
session.execute_write(seed_graph)
Once seeded, find every product that depends on Python and the company behind it:
MATCH (tech:Technology {name: "Python"})<-[:DEPENDS_ON]-(prod:Product)<-[:DEVELOPS]-(company:Company)
RETURN company.name AS Company,
prod.name AS Product,
prod.version AS Version
ORDER BY company.name
| Company | Product | Version |
|---|---|---|
| Acme Corp | AcmeCloud | 3.2 |
| Beta Inc | BetaSync | 1.9 |
This single traversal crosses three node labels and two relationship types β in a relational database it would require at least three JOIN operations. The graph model makes such multi-hop queries natural.
A more powerful pattern: find all technologies reached through a product's dependency chain:
MATCH (company:Company {name: "Acme Corp"})-[:DEVELOPS]->(:Product)-[:DEPENDS_ON*1..3]->(tech:Technology)
RETURN DISTINCT tech.name AS Technology,
tech.type AS Type
ORDER BY tech.name
This is the same blast-radius traversal used in dependency graph analysis, as explored in detail in Knowledge Graphs for Vulnerability Prioritisation.
Choosing the right platform depends on your query patterns, deployment model, and budget:
| Platform | Model | Query Language | Cloud | Self-Hosted | Best For |
|---|---|---|---|---|---|
| Neo4j | Property Graph | Cypher | Aura | Yes | General-purpose graph apps, AI context layer |
| Amazon Neptune | Property Graph + RDF | Gremlin + SPARQL | AWS only | No | AWS-native infrastructure |
| ArangoDB | Multi-model (Graph + Doc + KV) | AQL | ArangoDB Cloud | Yes | Polyglot persistence |
| TigerGraph | Property Graph | GSQL | TigerGraph Cloud | Yes | Large-scale graph analytics |
| FlureeDB | RDF (semantic graph) | SPARQL + FlureeQL | Fluree Cloud | Yes | Data provenance, access control |
| Apache Jena | RDF | SPARQL | No | Yes (library) | Semantic web research, small-scale RDF |
| Apache TinkerPop | Property Graph (framework) | Gremlin | No | Yes (library) | Polyglot graph backends |
Neo4j is the most popular choice for AI-augmented applications because its Cypher query language integrates natively with vector and full-text search, enabling the hybrid retrieval patterns that power modern GraphRAG β covered in Introduction to GraphRAG.
Large language models are stateless pattern matchers. They don't know things β they predict tokens. A knowledge graph provides:
Combining retrieval-augmented generation (RAG) with knowledge graphs produces GraphRAG β a pattern where:
This is explored in depth in Introduction to GraphRAG, but the key insight is: graphs give LLMs a reliable memory. Unlike vector embeddings, which capture semantic similarity, a graph captures explicit facts and their relationships β so when an LLM needs to answer "What products does Acme Corp own?" the answer is deterministic, not probabilistic.
The context-layer approach is now being adopted by major cloud providers. As discussed in Knowledge Graphs as Context Layer for AI Agents, AWS Context, Neo4j Document Intelligence, and Databricks Genie Ontology all launched within weeks of each other in June 2026 β each combining vector search with graph traversal to give AI agents structured context.
Consider a customer support agent that needs to answer: "Which of our products were affected by the log4j vulnerability?" A pure vector RAG pipeline retrieves chunks mentioning "log4j vulnerability." A graph-backed retriever starts with those chunks, then traverses from Vulnerability β Component β Product β Customer to produce a structured answer. The difference is the difference between finding relevant text and answering the question.
| Domain | Application | Graph Pattern |
|---|---|---|
| Fraud detection | Identify money laundering rings | Multi-hop traversal across accounts and transactions |
| Recommendations | "Customers who bought X also bought Y" | Collaborative filtering via shared-purchase paths |
| Supply chain | Identify single points of failure | Dependency-chain traversal, blast-radius analysis |
| Healthcare | Drug-target interaction discovery | Bipartite graph of compounds and proteins |
| Security | Vulnerability prioritisation | CVE β Component β Application β Exposure path |
| Enterprise search | Query-aware document retrieval | Hybrid vector + graph traversal (GraphRAG) |
A knowledge graph is only as useful as its schema β called an ontology. An ontology defines:
Company, Product, Technology)version, region, founded)DEVELOPS, DEPENDS_ON)normalizedName IS UNIQUE)A well-designed ontology separates a queryable knowledge graph from a tangled mess of nodes. For a deep dive into ontology design patterns β including the signal normalisation pattern, the entity-vs-value rule, and how to test your ontology β see Ontology in Graph Databases.
Here is a structured path from zero to your first knowledge graph:
Run Neo4j locally:
docker run --publish=7474:7474 --publish=7687:7687 -e NEO4J_AUTH=neo4j/password neo4j:2025
Open http://localhost:7474 in your browser to access the Neo4j Browser β an interactive console for writing and visualising Cypher queries.
Learn the Cypher essentials:
MATCH finds patterns in the graph β MATCH (n:Person) RETURN n.nameCREATE adds nodes and relationships β CREATE (p:Person {name: "Alice"})MERGE creates only if it doesn't already exist β prevents duplicatesWHERE filters β WHERE n.age > 30MATCH with variable-length paths β (a)-[:KNOWS*1..3]->(b) for multi-hop queriesModel a small domain: Start with something you know well β your music library (artists, albums, tracks, genres), a project dependency tree, or a customer journey. The smaller the domain, the faster you can iterate on the ontology.
Connect it to an LLM: Use the Neo4j Python driver as shown above. Query the graph, format the results as context, and feed them into any LLM. The neo4j-graphrag Python library provides pre-built retrievers for hybrid (vector + graph) search patterns.
Iterate on the ontology: Your first schema will be wrong. That is normal. Graph databases support schema evolution more gracefully than relational databases β you can add new labels, relationship types, and properties without migrating existing data.
A knowledge graph is not a silver bullet. For simple CRUD apps that never query across relationships, PostgreSQL is the right tool. But when your data's value comes from how things connect, a knowledge graph becomes indispensable. Start small, iterate on the ontology, and let the structure of your domain guide the graph.