Turn PostgreSQL into a Graph Database with Apache AGE
This guide explains how Apache AGE extends PostgreSQL with OpenCypher‑compatible graph capabilities, covering architecture, installation, storage schema, Cypher‑SQL integration, common graph operations, and a LangChain example that turns natural‑language questions into executable graph queries.
Introduction
Graph databases excel at handling complex relationships, while PostgreSQL provides robust transactional data management. Apache AGE bridges the two by adding graph functionality to PostgreSQL as an extension, preserving its stability and ecosystem while enabling graph modeling and querying.
Architecture
AGE is a PostgreSQL extension that lets users run graph queries inside an existing relational database. It implements an OpenCypher‑compatible parser, translates Cypher statements into PostgreSQL’s internal query representation, generates graph execution plans, and executes them while retaining PostgreSQL’s ACID guarantees.
Key Features
OpenCypher‑compliant query parser.
Cypher statements are converted to PostgreSQL‑recognizable internal queries.
Generates and executes graph query execution plans.
Executes graph queries efficiently according to the plan.
Fully inherits PostgreSQL’s ACID transaction properties.
Installing the AGE Extension
Compile and install the plugin:
# Compile and install the plugin</code>
<code>yum install gcc glibc glib-common readline readline-devel zlib zlib-devel flex bison</code>
<code>make PG_CONFIG=/usr/local/pgsql17/bin/pg_config installLoad the extension in the database:
# Load the extension</code>
<code>create extension age;</code>
<code>LOAD 'age';</code>
<code>SET search_path = ag_catalog, "$user", public;Graph Storage Model in PostgreSQL
When a graph is created, AGE creates a namespace in ag_catalog.ag_graph. Two system tables store vertices and edges: _ag_label_vertex and _ag_label_edge. User‑defined vertex and edge tables inherit from these parent tables.
graphid | name | namespace</code>
<code>---------+-----------+-----------</code>
<code> 17012 | graph_dba | graph_dbaTypical column definitions: name – label name. graph – graph ID. id – label ID. kind – 'v' for vertex, 'e' for edge. relation – underlying PostgreSQL table. seq_name – sequence for auto‑incrementing IDs.
Cypher Syntax Overview
Node : an entity with optional labels and properties.
Relationship : a directed edge between two nodes, also with optional labels and properties.
Core clauses : MATCH (pattern matching), WHERE (filter), RETURN (output).
Combining SQL and Cypher
In AGE, Cypher must be invoked through a SQL function and wrapped in $$ to avoid parser conflicts.
SELECT * FROM cypher('graph_dba', $$</code>
<code> MATCH (n) RETURN n</code>
<code>$$) AS (n agtype);Graph Operations
Create / Drop Graph :
SELECT * FROM ag_catalog.create_graph('graph_dba');</code>
<code>SELECT * FROM ag_catalog.drop_graph('graph_dba', true);Vertex Operations – create multiple vertices :
SELECT * FROM cypher('graph_dba', $$</code>
<code>UNWIND [{name:'zhang3'}, {name:'li4'}] AS row</code>
<code>CREATE (p:Person {name: row.name}) RETURN p</code>
<code>$$) AS (p agtype);Edge Operations – create a relationship :
SELECT * FROM cypher('graph_dba', $$</code>
<code>MATCH (a:Person {name:'zhang3'}), (b:Person {name:'li4'})</code>
<code>MERGE (a)-[r:FRIENDS {since:2001}]->(b) RETURN r</code>
<code>$$) AS (r agtype);Query Example – find friends of a person :
SELECT * FROM cypher('graph_dba', $$</code>
<code>MATCH (a:Person {name:'zhang3'})-[r:FRIENDS]->(b:Person) WHERE r.since = 2001 RETURN b</code>
<code>$$) AS (b agtype);Application Example with LangChain
The following Python snippet shows how to use LangChain’s GraphCypherQAChain together with an AGEGraph instance to translate natural‑language questions into Cypher queries and retrieve results from PostgreSQL + AGE.
from langchain_community.graphs.age_graph import AGEGraph</code>
<code>from langchain.chains import GraphCypherQAChain</code>
<code>from langchain_openai import ChatOpenAI</code>
<code>def main():</code>
<code> conf = {</code>
<code> "database": "postgresDB",</code>
<code> "user": "postgresUser",</code>
<code> "password": "postgresPW",</code>
<code> "host": "localhost",</code>
<code> "port": 5432,</code>
<code> }</code>
<code> graph = AGEGraph(graph_name="age_test", conf=conf)</code>
<code> graph.query("""</code>
<code> MERGE (m:Movie {name: 'Top Gun'})</code>
<code> WITH m</code>
<code> UNWIND ['Tom Cruise', 'Val Kilmer', 'Anthony Edwards', 'Meg Ryan'] AS actor</code>
<code> MERGE (a:Actor {name: actor})</code>
<code> MERGE (a)-[:ACTED_IN]->(m)</code>
<code> """)</code>
<code> llm = ChatOpenAI(temperature=0)</code>
<code> chain = GraphCypherQAChain.from_llm(llm, graph=graph, verbose=True, allow_dangerous_requests=True)</code>
<code> question = "Who played in Top Gun?"</code>
<code> response = chain.invoke(question)</code>
<code> print("Natural language query:", response["query"])</code>
<code> print("Generated answer:", response["result"])</code>
<code>if __name__ == "__main__":</code>
<code> main()Conclusion
Apache AGE brings native graph processing to PostgreSQL without requiring a separate database system. By supporting the Cypher query language and leveraging PostgreSQL’s ACID guarantees, it enables practical graph analytics and knowledge‑graph construction within familiar relational‑database environments.
360 Zhihui Cloud Developer
360 Zhihui Cloud is an enterprise open service platform that aims to "aggregate data value and empower an intelligent future," leveraging 360's extensive product and technology resources to deliver platform services to customers.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
