mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-30 04:40:16 +00:00
- Migrate from mint.json to docs.json (Mintlify v4) - Theme: maple, emerald green + near-black dark / cream light palette (#059669 primary, #0A0A0A dark bg, #FAF7F0 light bg) - Typography: Lexend headings, Inter body - 5-tab navigation: Documentation, Quick Start, API Reference, Cookbook, FAQ - Homepage: removed badge stickers, redundant h2, added blockquote tagline, full 27-module reference table with semantica.mcp_server added - quickstart.md: CodeGroup per pipeline step, pattern vs LLM options, AccordionGroup for patterns and troubleshooting - faq.md: full AccordionGroup structure across 5 sections - reference/explorer.md: NEW — FastAPI explorer, Ontology Hub, Distance Intelligence, CLI reference, REST API endpoints - reference/mcp_server.md: NEW — MCP stdio server, 12 tools with I/O examples, 3 resources, Claude Desktop/VS Code/Windsurf/Cline config - docs.json: explorer added to Output group, mcp_server to Utilities group - Chat, feedback (thumbs/suggest/raise), OG/Twitter metadata, search topbar - All reference pages reformatted with Mintlify JSX components Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
248 lines
6.8 KiB
Markdown
248 lines
6.8 KiB
Markdown
---
|
|
title: "Apache AGE Graph Store"
|
|
description: "PostgreSQL + Apache AGE backend for openCypher queries alongside standard SQL."
|
|
icon: "database"
|
|
---
|
|
|
|
**Backend**: PostgreSQL + [Apache AGE](https://age.apache.org/)
|
|
**Driver**: `psycopg2`
|
|
|
|
Apache AGE is a PostgreSQL extension that adds graph database functionality, enabling you to run openCypher queries alongside traditional SQL. This backend lets Semantica use AGE as a property graph store with the same interface as Neo4j and FalkorDB.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
| Component | Version |
|
|
|-----------|---------|
|
|
| PostgreSQL | 12+ |
|
|
| Apache AGE | 1.4+ (compiled and installed) |
|
|
| psycopg2 | 2.9+ |
|
|
|
|
```bash
|
|
pip install psycopg2-binary
|
|
```
|
|
|
|
<Note>Apache AGE must be compiled and installed into your PostgreSQL instance. See the [AGE installation guide](https://age.apache.org/age-manual/master/intro/setup.html).</Note>
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
```python
|
|
from semantica.graph_store import GraphStore
|
|
|
|
# Using the unified GraphStore facade
|
|
store = GraphStore(
|
|
backend="age",
|
|
connection_string="host=localhost dbname=agedb user=postgres password=secret",
|
|
graph_name="semantica",
|
|
)
|
|
store.connect()
|
|
|
|
# Create nodes
|
|
alice = store.create_node(labels=["Person"], properties={"name": "Alice", "age": 30})
|
|
bob = store.create_node(labels=["Person"], properties={"name": "Bob", "age": 25})
|
|
|
|
# Create relationship
|
|
rel = store.create_relationship(alice["id"], bob["id"], "KNOWS", {"since": 2023})
|
|
|
|
# Query
|
|
result = store.execute_query("MATCH (p:Person) RETURN p", cols="p agtype")
|
|
print(result["records"])
|
|
|
|
store.close()
|
|
```
|
|
|
|
### Direct Usage (without facade)
|
|
|
|
```python
|
|
from semantica.graph_store.age_store import ApacheAgeStore
|
|
|
|
store = ApacheAgeStore(
|
|
connection_string="host=localhost dbname=agedb user=postgres password=secret",
|
|
graph_name="my_graph",
|
|
)
|
|
store.connect()
|
|
|
|
node = store.create_node(["Entity"], {"semantica_id": "ent-001", "value": "test"})
|
|
print(node)
|
|
# {"id": 844424930131969, "labels": ["Entity"], "properties": {"semantica_id": "ent-001", "value": "test"}}
|
|
|
|
store.close()
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
### Environment Variables
|
|
|
|
| Variable | Description | Default |
|
|
|----------|-------------|---------|
|
|
| `GRAPH_STORE_AGE_CONNECTION_STRING` | PostgreSQL connection string | `host=localhost dbname=agedb user=postgres password=postgres` |
|
|
| `GRAPH_STORE_AGE_GRAPH_NAME` | AGE graph name | `semantica` |
|
|
|
|
### Programmatic Configuration
|
|
|
|
```python
|
|
from semantica.graph_store.config import graph_store_config
|
|
|
|
graph_store_config.set("age_connection_string", "host=db.example.com dbname=prod_age user=app")
|
|
graph_store_config.set("age_graph_name", "production")
|
|
```
|
|
|
|
---
|
|
|
|
## Connection & Initialization
|
|
|
|
On `connect()`, the store performs idempotent setup:
|
|
|
|
1. `CREATE EXTENSION IF NOT EXISTS age;`
|
|
2. `LOAD 'age';`
|
|
3. `SET search_path = ag_catalog, "$user", public;`
|
|
4. Creates the named graph if it does not already exist.
|
|
|
|
This is safe to call repeatedly.
|
|
|
|
---
|
|
|
|
## ID Handling
|
|
|
|
Apache AGE auto-generates internal vertex/edge IDs (large integers). These are **not** the same as any semantic or application-level ID you may want to assign.
|
|
|
|
| Concept | Description |
|
|
|---------|-------------|
|
|
| **AGE internal ID** | Auto-generated by AGE. Exposed as `"id"` in all returned dicts. Used in `delete_node()`, `get_node()`, etc. |
|
|
| **Semantic ID** | Application-level identifier. Store it in the `semantica_id` property. |
|
|
|
|
```python
|
|
node = store.create_node(
|
|
labels=["Document"],
|
|
properties={"semantica_id": "doc-abc-123", "title": "My Doc"},
|
|
)
|
|
# node["id"] → AGE internal ID (e.g., 844424930131969)
|
|
# node["properties"]["semantica_id"] → "doc-abc-123"
|
|
```
|
|
|
|
<Warning>Never mix AGE internal IDs with semantic IDs. Use `node["id"]` for graph operations (delete, update, traverse) and `node["properties"]["semantica_id"]` for application-level lookups.</Warning>
|
|
|
|
---
|
|
|
|
## Label Handling
|
|
|
|
AGE supports exactly **one label per vertex**. Semantica handles this transparently:
|
|
|
|
- `labels[0]` → used as the primary AGE vertex label.
|
|
- `labels[1:]` → stored in a `labels` property array on the vertex.
|
|
|
|
When reading nodes, the store reconstructs the full label list automatically.
|
|
|
|
```python
|
|
node = store.create_node(
|
|
labels=["Person", "Employee", "Admin"],
|
|
properties={"name": "Alice"},
|
|
)
|
|
# In AGE: vertex with label "Person" and property labels=["Employee", "Admin"]
|
|
# Returned: {"id": ..., "labels": ["Person", "Employee", "Admin"], "properties": {"name": "Alice"}}
|
|
```
|
|
|
|
---
|
|
|
|
## Cypher Query Execution
|
|
|
|
All Cypher queries are executed via AGE's SQL wrapper:
|
|
|
|
```sql
|
|
SELECT * FROM cypher('graph_name', $$ <cypher_query> $$) AS (col1 agtype, ...);
|
|
```
|
|
|
|
### Parameter Substitution
|
|
|
|
AGE does not support `$param` style binding inside `cypher()` calls. The store safely converts parameters to Cypher literals with proper escaping:
|
|
|
|
```python
|
|
result = store.execute_query(
|
|
"MATCH (p:Person) WHERE p.age > $min_age RETURN p",
|
|
parameters={"min_age": 25},
|
|
cols="p agtype",
|
|
)
|
|
```
|
|
|
|
### Column Specification
|
|
|
|
For custom queries, pass the `cols` option to specify the `AS` clause:
|
|
|
|
```python
|
|
result = store.execute_query(
|
|
"MATCH (a)-[r]->(b) RETURN a, r, b",
|
|
cols="a agtype, r agtype, b agtype",
|
|
)
|
|
```
|
|
|
|
If omitted, the store attempts to infer columns from the `RETURN` clause.
|
|
|
|
---
|
|
|
|
## Transactions
|
|
|
|
The store uses explicit PostgreSQL transactions:
|
|
|
|
- **Success** → `COMMIT`
|
|
- **Exception** → `ROLLBACK`, then re-raise as `ProcessingError`
|
|
- No silent failures
|
|
|
|
---
|
|
|
|
## API Reference
|
|
|
|
All methods match the standard Semantica graph store backend interface:
|
|
|
|
| Method | Description |
|
|
|--------|-------------|
|
|
| `connect(**options)` | Connect and initialize AGE |
|
|
| `close()` | Close the connection |
|
|
| `create_node(labels, properties)` | Create a vertex |
|
|
| `create_nodes(nodes)` | Batch create vertices |
|
|
| `get_node(node_id)` | Get vertex by AGE ID |
|
|
| `get_nodes(labels, properties, limit)` | Query vertices |
|
|
| `update_node(node_id, properties, merge)` | Update vertex properties |
|
|
| `delete_node(node_id, detach)` | Delete a vertex |
|
|
| `create_relationship(start_id, end_id, type, properties)` | Create an edge |
|
|
| `get_relationships(node_id, rel_type, direction, limit)` | Query edges |
|
|
| `delete_relationship(rel_id)` | Delete an edge |
|
|
| `execute_query(query, parameters)` | Run arbitrary Cypher |
|
|
| `get_neighbors(node_id, rel_type, direction, depth)` | Graph traversal |
|
|
| `shortest_path(start_id, end_id, rel_type, max_depth)` | Path finding |
|
|
| `create_index(label, property_name, index_type)` | Create a PostgreSQL index |
|
|
| `get_stats()` | Graph statistics |
|
|
|
|
---
|
|
|
|
## Docker Setup
|
|
|
|
```yaml
|
|
services:
|
|
age:
|
|
image: apache/age:latest
|
|
ports:
|
|
- "5432:5432"
|
|
environment:
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: secret
|
|
POSTGRES_DB: agedb
|
|
```
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
Then connect:
|
|
|
|
```python
|
|
store = GraphStore(
|
|
backend="age",
|
|
connection_string="host=localhost port=5432 dbname=agedb user=postgres password=secret",
|
|
)
|
|
```
|