# Graph Database Feature Parity Chart

**Date:** 2026-03-01
**Status:** Current as of March 2026
**Purpose:** Transparent, detailed comparison of graph database capabilities to validate
Piggie's core thesis -- that AGE + pgvector in a single PostgreSQL instance can replace
separate graph and vector databases for modern AI and data workloads.

---

## Databases Compared

| Database | Version | Install Model | Query Language | License |
|----------|---------|---------------|---------------|---------|
| Apache AGE (via Piggie SDK) | 1.7.0 on PG18 | PostgreSQL extension | Cypher (subset) | Apache 2.0 |
| Neo4j Community | 5.x (2026.01) | Standalone JVM server | Full Cypher | GPLv3 (modified AGPL) |
| Kuzu | 0.8.x (embedded) | pip install kuzu | Cypher (subset) | MIT (archived Oct 2025) |
| NebulaGraph Community | 3.8.0 | Docker (3 services) | nGQL (openCypher-compatible reads) | Apache 2.0 |

**Critical status notes:**

- **Kuzu** was archived on GitHub on October 10, 2025. Kuzu Inc. announced they were
  "working on something new" (widely speculated to be an Apple acquisition). Community
  forks exist (RyuGraph by Predictable Labs, Ladybug as a direct replacement), but the
  original project is no longer maintained. Data in this chart reflects the last released
  version (0.8.2) before archival.

- **NebulaGraph** distinguishes Community (Apache 2.0, nGQL-only) from Enterprise
  (commercial license). Enterprise v5.2 adds vector search, full-text indexing, native
  GQL support, and in-database graph algorithms. This chart evaluates only the free
  Community 3.x edition unless noted otherwise.

- **Neo4j** Community Edition uses GPLv3 (modified AGPL for server). Enterprise features
  include clustering, key constraints, existence constraints, property type constraints,
  and advanced GDS algorithms. This chart evaluates only Community Edition unless noted.

---

## Summary Matrix

| Dimension | AGE 1.7.0 (Piggie) | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x CE |
|-----------|-------------------|-------------|------------|-------------------|
| Cypher dialect coverage | Moderate (~55%) | **Strong** (~95%) | Strong (~80%) | Moderate (nGQL + partial openCypher) |
| Vector search | **Strong** (pgvector) | **Strong** (native HNSW) | Moderate (HNSW ext.) | None (Enterprise only) |
| Graph algorithms | Weak (client-side) | **Strong** (GDS, 60+ algos) | Moderate (extensions) | Weak (external Spark) |
| Bulk operations | Weak | Strong | **Strong** (columnar COPY) | Moderate |
| Schema & constraints | Moderate (via PG) | **Strong** (native) | Moderate (DDL-required) | Moderate (DDL-required) |
| Replication & HA | **Strong** (PG ecosystem) | None (Enterprise only) | None (embedded) | **Strong** (Raft consensus) |
| Language drivers | Moderate (PG-based) | **Strong** (7+ official) | Strong (embedded APIs) | Moderate |
| Licensing | **Strong** (Apache 2.0) | Weak (GPLv3/AGPL) | **Strong** (MIT, unmaintained) | **Strong** (Apache 2.0) |
| Operational simplicity | **Strong** | Moderate | **Strong** (embedded) | Weak (3-service cluster) |

---

## Dimension 1: Cypher Dialect Coverage

This is the most nuanced comparison because NebulaGraph uses nGQL rather than Cypher,
and AGE implements a subset of the openCypher specification. The table maps equivalent
functionality where applicable.

### Read Clauses

| Feature | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|---------|-----------|-------------|------------|----------------|
| MATCH | Yes | Yes | Yes | Yes (nGQL + openCypher) |
| OPTIONAL MATCH | Yes | Yes | Yes | Yes (openCypher syntax) |
| WHERE | Yes | Yes | Yes | Yes |
| RETURN | Yes | Yes | Yes | Yes |
| ORDER BY / SKIP / LIMIT | Yes | Yes | Yes | Yes |
| WITH | Yes | Yes | Yes | Yes |
| UNWIND | Yes | Yes | Yes | Yes |
| UNION / UNION ALL | **No** | Yes | Yes | Yes |
| CALL subquery | **No** (issue #1690) | Yes | **No** | **No** |
| CALL procedure | **No** | Yes (APOC, GDS) | Yes (built-in functions) | Yes (nGQL procedures) |
| EXISTS subquery | **No** | Yes | **No** | **No** |

### Write Clauses

| Feature | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|---------|-----------|-------------|------------|----------------|
| CREATE | Yes | Yes | Yes | INSERT VERTEX/EDGE (nGQL) |
| DELETE / DETACH DELETE | Yes | Yes | Yes | DELETE VERTEX/EDGE (nGQL) |
| SET | Yes | Yes | Yes | UPDATE ... SET (nGQL) |
| REMOVE (properties) | Yes | Yes | Yes | **No** (use UPDATE SET NULL) |
| REMOVE (labels) | **No** (issue #717) | Yes | **No** | N/A (uses tags) |
| MERGE (basic) | Yes | Yes | Yes | **No** (UPSERT VERTEX/EDGE) |
| MERGE ON CREATE SET | **No** (PR #2347 open) | Yes | Yes | N/A |
| MERGE ON MATCH SET | **No** (PR #2347 open) | Yes | Yes | N/A |
| FOREACH | **No** | Yes | **No** (use UNWIND) | **No** |
| LOAD CSV | **No** | Yes | Yes (as LOAD FROM) | **No** (use nebula-importer) |

### Expressions and Predicates

| Feature | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|---------|-----------|-------------|------------|----------------|
| CASE / WHEN | Yes | Yes | Yes | Yes (CASE syntax) |
| starts with | **No** (issue #552-556) | Yes | Yes | Yes (starts with) |
| ends with | **No** | Yes | Yes | Yes (ends with) |
| contains | **No** | Yes | Yes | Yes (contains) |
| =~ (regex) | **No** | Yes | Yes | **No** |
| List comprehensions | **No** | Yes | Yes | **No** |
| Pattern comprehensions | **No** | Yes | **No** | **No** |
| List predicates (all/any/none/single) | **No** (issues #552-556) | Yes | Partial | **No** |
| WHERE pattern matching | **No** (issue #1577) | Yes | Yes | Yes |
| IN (list membership) | Yes | Yes | Yes | Yes |

### Path Features

| Feature | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|---------|-----------|-------------|------------|----------------|
| Variable-length paths (VLE) | Yes | Yes | Yes (walk semantic) | Yes (GO N STEPS / VLE) |
| shortestPath() | Yes | Yes | Yes | FIND SHORTEST PATH |
| allShortestPaths() | **Not confirmed** | Yes | Yes | FIND ALL PATH |
| Multiple labels | **No** (PR #2082 open) | Yes | Yes | N/A (TAG system) |
| Named paths | Yes | Yes | Yes | **No** |

### Schema Operations

| Feature | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|---------|-----------|-------------|------------|----------------|
| CREATE INDEX | Via PG SQL only | Yes (Cypher) | Yes (DDL) | Yes (nGQL) |
| CREATE CONSTRAINT | Via PG SQL only | Yes (Cypher, partial CE) | Via DDL | Via DDL |
| DROP INDEX/CONSTRAINT | Via PG SQL only | Yes (Cypher) | Yes (DDL) | Yes (nGQL) |
| SHOW INDEXES | Via pg_indexes | Yes | Yes (function call) | SHOW TAG INDEXES |

### Summary

**AGE coverage: ~55%** of the openCypher specification. The most impactful gaps for
production use are MERGE ON CREATE/MATCH SET, string predicates (starts with, ends with,
contains), list predicates, UNION, and CALL subqueries. We are actively contributing
upstream to close these gaps -- PR #2347 addresses MERGE ON SET, and predicate functions
(issues #552-556) are the next target.

**Neo4j** is the reference implementation at ~95% coverage, with additional proprietary
extensions (APOC, GDS procedures).

**Kuzu** reached approximately 80% coverage before archival. It notably supports MERGE ON
CREATE/MATCH SET, list comprehensions, and WHERE pattern matching, but lacks CALL
subqueries, FOREACH, and pattern comprehensions. Kuzu uses walk semantics for VLE (with
configurable upper bound), unlike Neo4j's trail semantics.

**NebulaGraph** uses nGQL natively, with partial openCypher compatibility for read
operations (MATCH, OPTIONAL MATCH, WHERE). Write operations use distinct nGQL syntax
(INSERT VERTEX/EDGE, UPDATE, UPSERT). There is no MERGE equivalent -- UPSERT is the
closest analog but lacks ON CREATE/MATCH SET semantics. nGQL is SQL-like rather than
Cypher-like for mutations.

---

## Dimension 2: Vector Search

| Capability | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x CE |
|------------|-----------|-------------|------------|-------------------|
| Native vector type | Via pgvector (VECTOR) | Yes (LIST<FLOAT>) | Yes (FLOAT[] via ext.) | **None** (Enterprise only) |
| Index: HNSW | Yes (pgvector) | Yes (native) | Yes (disk-based HNSW) | **None** (Enterprise only) |
| Index: IVFFlat | Yes (pgvector) | **No** | **No** | **None** |
| Similarity: cosine | Yes | Yes | Yes | **None** |
| Similarity: L2 (euclidean) | Yes | Yes (since 2026.01) | Yes | **None** |
| Similarity: inner product | Yes | **No** | **No** | **None** |
| Pre-filtering | Yes (SQL WHERE) | Yes (Cypher WHERE, since 2026.01) | Yes | **None** |
| Multi-vector index | Yes (one per column) | Yes (since 2026.01, multi-label) | One per table | **None** |
| Hybrid graph+vector query | Yes (SQL-level CTE bridge) | Yes (native Cypher SEARCH) | Yes (Cypher + vector) | **None** |
| Dimensions limit | 16,000 (pgvector 0.8.x) | 4,096 (as of 2025) | Configurable | N/A |

### Analysis

**AGE (via pgvector)** provides the broadest similarity function support (L2, cosine,
inner product) and the most index options (HNSW + IVFFlat). The key limitation is that
vector operations happen at the SQL level, not inside Cypher. The Piggie SDK bridges this
gap with `hybrid_search()` which combines `ag_catalog.cypher()` results with pgvector
`<=>` operators using CTEs. The co-tenancy advantage is real: graph traversal and vector
similarity happen in the same PostgreSQL process with zero network hops.

**Neo4j** added native vector indexes in 5.11 (HNSW only) and has been rapidly improving
the integration. As of 2026.01, vector search uses a native `SEARCH` clause integrated
directly into Cypher, with multi-label indexes and pre-filtering support. This is the most
ergonomic graph+vector experience from a query language perspective.

**Kuzu** added vector search via an HNSW extension with disk-based indexes. This is
functional but was still maturing when the project was archived.

**NebulaGraph Community** has no vector search capability. Vector search, full-text
indexing, and hybrid retrieval are Enterprise v5.2 features behind a commercial license.
This is a significant limitation for AI/RAG workloads on the free tier.

---

## Dimension 3: Graph Algorithms

| Algorithm Category | AGE 1.7.0 | Neo4j 5.x CE (GDS) | Kuzu 0.8.x | NebulaGraph 3.x CE |
|-------------------|-----------|-------------------|------------|-------------------|
| **Centrality** | | | | |
| PageRank | No (via SDK/NetworkX) | Yes | Yes (algo ext.) | Via nebula-algorithm (Spark) |
| Betweenness centrality | No (via SDK/NetworkX) | Yes | Via NetworkX export | Via Spark GraphX |
| Degree centrality | No (via SDK/NetworkX) | Yes | No | Via Spark GraphX |
| Closeness centrality | No (via SDK/NetworkX) | Yes | No | Via Spark GraphX |
| Eigenvector centrality | No | Yes | No | No |
| **Community Detection** | | | | |
| Louvain | No (via SDK/NetworkX) | Yes | Yes (algo ext.) | Via Spark GraphX |
| Label propagation | No (via SDK/NetworkX) | Yes | No | Via Spark GraphX |
| Weakly connected components | No (via SDK/NetworkX) | Yes | Yes (algo ext.) | Via Spark GraphX |
| Strongly connected components | No (via SDK/NetworkX) | Yes | No | Via Spark GraphX |
| Triangle counting | No | Yes | No | Via Spark GraphX |
| **Pathfinding** | | | | |
| Shortest path (Dijkstra) | VLE shortestPath() | Yes (weighted) | Built into VLE | FIND SHORTEST PATH |
| All shortest paths | Not confirmed | Yes | Yes | FIND ALL PATH |
| A* | No | Yes | No | No |
| Random walk | No | Yes | No | No |
| **Similarity** | | | | |
| Node similarity (Jaccard) | No | Yes | No | No |
| Cosine similarity | No | Yes | No | No |
| **Link Prediction** | No | Yes | No | No |
| **Graph embeddings** | | | | |
| Node2Vec | No | Yes | No | No |
| FastRP | No | Yes | No | No |
| **Execution Model** | Export to NetworkX/PyG | In-memory graph projection | In-process (C++ ext.) | External Spark job |

### Analysis

**Neo4j GDS** is the clear winner with 60+ algorithms organized into centrality,
community detection, pathfinding, similarity, link prediction, and embedding categories.
The Community Edition includes all algorithms (the Enterprise edition provides optimized
memory usage for graph projections, not additional algorithms). GDS uses an in-memory
graph projection model: you project a subgraph into memory, run algorithms against it,
then write results back or stream them.

**AGE** has no built-in graph algorithms at the database level. The Piggie SDK compensates
by providing a NetworkX integration layer that exports AGE graph data to NetworkX for
algorithm execution, then writes results back. The SDK also provides a PyG (PyTorch
Geometric) integration for GNN-based algorithms. This is a viable pattern for datasets
that fit in application memory but cannot compete with GDS for very large graphs.

**Kuzu** offered an algorithm extension with PageRank, Louvain, and WCC before being
archived. Its columnar storage and in-process execution model made algorithm execution
fast for embedded use cases.

**NebulaGraph Community** provides graph algorithms only through the external
`nebula-algorithm` package, which is a Spark application based on GraphX. This requires
a separate Spark cluster, making it operationally heavy. The Enterprise v5.2 edition adds
a lightweight in-database compute engine that avoids the Spark dependency.

---

## Dimension 4: Bulk Operations

| Capability | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|------------|-----------|-------------|------------|----------------|
| **Import from CSV** | `load_labels_from_file()` (server-side only) | `LOAD CSV` (Cypher), `neo4j-admin import` (offline) | `COPY FROM` (CSV, blazing fast) | nebula-importer tool |
| **Import from Parquet** | Via PG FDW or external tool | **No** | Yes (`COPY FROM`) | **No** |
| **Import from JSON** | Via PG FDW or external tool | APOC procedures | Yes (`COPY FROM`) | **No** |
| **Client-side bulk load** | **No** native support (issue #432); `agefreighter` third-party tool | `LOAD CSV` from URL, APOC load procedures | N/A (embedded, local file access) | nebula-importer, Exchange (Spark) |
| **Batch write via query** | UNWIND + CREATE (slow beyond ~10K) | `CALL {} IN TRANSACTIONS` (batched commits), UNWIND + CREATE | UNWIND + CREATE (fast, columnar) | Batch INSERT (multi-row) |
| **PG COPY protocol** | Yes (SQL-level, bypassing Cypher) | N/A | N/A | N/A |
| **Scale ceiling** | Degrades severely after ~200K edges via Cypher (#2198) | Millions via `neo4j-admin import`; hundreds of thousands via `LOAD CSV` | Millions via `COPY FROM` (columnar storage) | Billions of edges (distributed) |
| **Offline import tool** | None | `neo4j-admin database import` (fastest, requires DB offline) | N/A (embedded) | nebula-importer |
| **Streaming import** | No | No | No | Spark Connector (via Exchange) |

### Analysis

**Kuzu** had the fastest bulk import before archival. Its columnar storage format and
native `COPY FROM` command (supporting CSV, Parquet, JSON, and NumPy) achieved import
rates that far exceeded the competition for embedded workloads. This is a significant
loss for the graph database ecosystem.

**Neo4j** provides multiple import paths at different trade-off points. `neo4j-admin
database import` is the fastest (offline, requires database shutdown). `LOAD CSV` works
online but is single-threaded. `CALL {} IN TRANSACTIONS` enables batched commits to avoid
transaction memory pressure. APOC procedures add JSON and JDBC import capabilities.

**AGE** has the weakest bulk loading story. The `load_labels_from_file()` and
`load_edges_from_file()` functions require server-side file access (impractical for
cloud/managed PG deployments). Cypher-based batch inserts via UNWIND + CREATE degrade
severely after ~200K edges (issue #2198). The third-party `agefreighter` tool provides
a workaround using direct table inserts via PG COPY protocol. The Piggie SDK's
`load_vertices()` and `load_edges()` methods currently use row-at-a-time Cypher. A
COPY-based bulk loader is a planned P1 enhancement.

**NebulaGraph** handles bulk loading through external tools (`nebula-importer` for CSV,
Exchange for Spark-based ETL). Its distributed architecture can ingest billions of edges
across partitions.

---

## Dimension 5: Schema and Constraints

| Capability | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|------------|-----------|-------------|------------|----------------|
| **Schema model** | Schema-free (labels created on first use) | Schema-free (labels created on first use) | **Schema-required** (DDL before data) | **Schema-required** (DDL before data) |
| **Typed properties** | No (agtype stores all types) | No (schemaless by default) | **Yes** (strict types in DDL) | **Yes** (strict types in DDL) |
| **Primary key** | No (internal graph IDs only) | No (use element_id or unique constraint) | **Required** (one PK per node table) | **Required** (VID per vertex) |
| **Unique property constraint** | Via PG (manual, on underlying tables) | **Yes** (native Cypher DDL) | **No** | **No** |
| **Existence constraint** | Via PG NOT NULL (manual) | **Enterprise only** | Implicit (typed schema, nullable by default) | Implicit (typed schema) |
| **Node key constraint** | Via PG (manual) | **Enterprise only** | Via primary key | Via VID |
| **Property type constraint** | Via PG CHECK (manual) | **Enterprise only** | Inherent (typed schema) | Inherent (typed schema) |
| **Property indexes** | GIN on JSONB (manual PG SQL) | Range, Text, Point, Token (native Cypher DDL) | Implicit on PK; no manual secondary indexes | Native TAG/EDGE property indexes |
| **Composite indexes** | Via PG (manual) | Yes (native) | **No** | Yes |
| **Full-text indexes** | Via PG tsvector (SQL) | Yes (native) | Yes (fts extension, BM25) | **Enterprise only** |
| **Vector indexes** | Via pgvector HNSW/IVFFlat | Yes (native HNSW) | Yes (HNSW extension) | **Enterprise only** |
| **TTL (time-to-live)** | Via PG partitioning / cron job | **No** | **No** | **Yes** (native TTL on TAG/EDGE TYPE) |

### Analysis

**Neo4j Community Edition** provides the most integrated constraint system accessible
from Cypher DDL, but only unique property constraints are available in Community Edition.
Existence constraints, node key constraints, and property type constraints are Enterprise-
only. This is a significant limitation for production deployments on the free tier.

**AGE** has no Cypher-level DDL for constraints or indexes. However, because AGE stores
graph data in PostgreSQL tables (one table per label), the full PostgreSQL constraint
and index ecosystem is available at the SQL level. You can add unique constraints, NOT
NULL constraints, CHECK constraints, and any PG index type (B-tree, GIN, GiST, BRIN)
directly on the underlying label tables. The limitation is that these must be set up
manually via SQL, not through Cypher, and require knowledge of AGE's internal table
structure.

**Kuzu** and **NebulaGraph** both require schema definition before data insertion.
Kuzu enforces a primary key on every node table, which provides inherent uniqueness
guarantees on the key property. NebulaGraph requires VIDs (vertex IDs) and supports
typed properties and TTL configuration, which is unique among the four databases.

---

## Dimension 6: Replication and High Availability

| Capability | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|------------|-----------|-------------|------------|----------------|
| **Streaming replication** | **Yes** (PG native) | **Enterprise only** | None (embedded) | Built-in (Raft) |
| **Logical replication** | **Yes** (PG native) | **Enterprise only** | None | **No** |
| **Read replicas** | **Yes** (PG hot standby) | **Enterprise only** | None | **Yes** (configurable replicas per partition) |
| **Synchronous replication** | **Yes** (PG synchronous_commit) | **Enterprise only** | None | **Yes** (Raft quorum) |
| **Async replication** | **Yes** (PG async standby) | **Enterprise only** | None | **Yes** (Raft follower lag) |
| **Auto failover** | Via Patroni, pg_auto_failover | **Enterprise only** | None | **Built-in** (Raft leader election) |
| **Clustering** | Patroni, Citus, pgpool-II | **Enterprise only** (causal clustering) | None | **Native distributed** (partition + replicate) |
| **Horizontal write scaling** | Via Citus (sharding) | **Enterprise only** | None | **Yes** (partitioned writes) |
| **Backup** | pg_dump, pg_basebackup, WAL archiving, pgBackRest | `neo4j-admin dump/backup` (single node) | File copy | Built-in snapshot + WAL |
| **Point-in-time recovery** | **Yes** (WAL + pg_basebackup) | **No** (Community) | **No** | **No** |
| **Managed cloud offerings** | Azure Database for PG, AWS RDS/Aurora, GCP Cloud SQL | Neo4j Aura (hosted, separate pricing) | None | NebulaGraph Cloud |

### Analysis

**AGE** inherits PostgreSQL's battle-tested replication and HA ecosystem, which is
arguably the strongest operational story in the comparison. Streaming replication, logical
replication, hot standby read replicas, synchronous commits, point-in-time recovery, and
automated failover (via Patroni) are all production-proven at massive scale. This works
out of the box because AGE is just an extension -- PostgreSQL replicates AGE's label
tables exactly like any other table.

**Neo4j Community Edition** is strictly single-instance. No replication, no clustering,
no read replicas. If the instance goes down, the database is unavailable until recovery.
If the disk is lost, you must restore from the latest backup. All clustering and HA
features require the Enterprise Edition with a commercial license. This is the most
significant operational limitation of Neo4j CE.

**Kuzu** is an embedded database with no replication or HA. It runs in the application
process. Backup is a file copy.

**NebulaGraph** has the most sophisticated built-in distributed architecture. The
three-service model (metad for metadata, graphd for query processing, storaged for data)
uses Multi-Group Raft consensus for replication. Data is hash-partitioned across storage
nodes, with each partition maintaining configurable replicas. Leader election, quorum
writes, and automatic failover are built into the system. The trade-off is operational
complexity: a minimum viable NebulaGraph deployment requires at least three processes.

---

## Dimension 7: Language Drivers

| Language | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|----------|-----------|-------------|------------|----------------|
| Python | Official (psycopg3-based, in `apache-age/drivers/python`) | **Official** (neo4j PyPI, mature) | Official (pybind11 embedding) | Official (nebula3-python) |
| Java | Community (JDBC-based) | **Official** (neo4j-java-driver) | Official (JNI embedding) | Official (nebula-java) |
| JavaScript/Node.js | Community (node-postgres) | **Official** (neo4j-driver) | Official (NAPI embedding) | Community (nebula-node) |
| Go | Official (apache-age-go) | **Official** (neo4j-go-driver) | **No** | Official (nebula-go) |
| Rust | **No** | Community | Official (native embedding) | Official (nebula-rust) |
| C/C++ | Via libpq (direct PG) | **No** official | Official (native C++ API) | Official (nebula-cpp) |
| .NET | Community (Npgsql-based) | **Official** (Neo4j.Driver) | **No** | Community (nebula-net) |
| PHP | Community (php-pgsql) | Community | **No** | Community (nebula-php) |
| **Driver maturity** | Thin wrappers around PG drivers | Production-grade with connection pooling, retry logic, routing | Native embedded APIs | fbthrift-based RPC clients |
| **Async support** | Yes (psycopg3 async) | Yes (full async mirror API) | N/A (embedded, no I/O) | Yes (async clients) |
| **Connection pooling** | Via PG ecosystem (psycopg_pool, pgBouncer) | Built-in (configurable per driver) | N/A (embedded) | Via connection pool in clients |
| **Transaction management** | PG native BEGIN/COMMIT/ROLLBACK | Managed transactions with automatic retry | ACID transactions (embedded) | nGQL transactions |

### Analysis

**Neo4j** has the broadest and most mature official driver ecosystem, with 7+ officially
maintained drivers, comprehensive documentation, and consistent APIs across languages.
The Python driver (v6.1.0) is the gold standard -- it provides two-tier APIs
(simple + session-based), streaming result cursors, DataFrame integration, async
mirroring, and managed transactions with automatic retry on transient failures.

**AGE** drivers are thin wrappers around PostgreSQL client libraries. Any language with
a PG driver can connect to AGE -- the AGE-specific layer handles `ag_catalog.cypher()`
SQL wrapping and agtype parsing. This is architecturally lean (leverage the PG ecosystem
rather than building custom protocol drivers) but means the AGE-specific driver features
(agtype parsing, graph type mapping) are less mature than Neo4j's purpose-built drivers.
The Piggie SDK is our answer to this gap: a production-grade Python SDK with rich types,
DataFrame integration, and hybrid query support.

**Kuzu** provided native embedded APIs via language bindings (pybind11 for Python, JNI
for Java, NAPI for Node.js, native Rust). These are in-process APIs with no network
overhead, which is a fundamentally different model from client-server drivers.

**NebulaGraph** uses fbthrift as its RPC framework, with official clients in Python,
Java, Go, C++, and Rust. The driver layer is functional but less polished than Neo4j's.

---

## Dimension 8: Licensing

| Aspect | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x CE |
|--------|-----------|-------------|------------|-------------------|
| **License** | Apache 2.0 | GPLv3 (modified AGPL for server) | MIT (archived) | Apache 2.0 |
| **Truly open source** | Yes | Restricted (AGPL + CLA) | Yes (unmaintained) | Yes |
| **Feature gating** | None -- all features in one edition | Clustering, HA, key/existence/type constraints, causal clustering | None | Vector, full-text, in-DB algorithms, GQL |
| **Can embed in proprietary software** | Yes | **No** (GPL/AGPL) | Yes (MIT) | Yes |
| **Can offer as hosted service (SaaS)** | Yes | **No** (without commercial agreement) | Yes | Yes |
| **Patent grant** | Yes (Apache 2.0 includes patent clause) | **No** | Yes (MIT, implicit) | Yes (Apache 2.0) |
| **Contributor agreement** | Apache ICLA | Neo4j CLA | None required | DCO |
| **Governance** | Apache Software Foundation | Neo4j Inc. (single vendor) | Kuzu Inc. (defunct) | vesoft Inc. |

### Analysis

**Apache AGE** and **NebulaGraph Community** are the most permissively licensed. Apache
2.0 provides explicit patent grants, allows embedding in proprietary software, and
permits offering as a hosted service without additional agreements. AGE benefits from
Apache Software Foundation governance, which prevents single-vendor lock-in.

**Neo4j** Community Edition uses GPLv3 (with modified AGPL terms for the server
component), which imposes copyleft obligations. Any modifications to Neo4j must be
released under the same license. The AGPL provision means that offering Neo4j as a
network service (SaaS) triggers the copyleft requirement. Running Neo4j CE in production
for internal use is fine; building a commercial graph-as-a-service product on it is not
(without a commercial license from Neo4j Inc.).

**Kuzu** used the MIT license, the most permissive option. However, with the project
archived and the company apparently pivoting, the practical value is limited. Forks
(RyuGraph, Ladybug) inherit the MIT license but face the challenge of maintaining a
complex C++ codebase without the original team.

The licensing dimension is particularly relevant for the Piggie thesis: AGE's Apache 2.0
license means there are zero licensing barriers to building and offering Piggie as a
commercial platform, unlike Neo4j where the AGPL creates significant friction for SaaS
deployment models.

---

## Dimension 9: Operational Model

| Aspect | AGE 1.7.0 | Neo4j 5.x CE | Kuzu 0.8.x | NebulaGraph 3.x |
|--------|-----------|-------------|------------|----------------|
| **Deployment model** | PG extension (`CREATE EXTENSION age`) | Standalone JVM process | Embedded library (in-process) | 3-service cluster (metad, graphd, storaged) |
| **Minimum viable deployment** | Existing PG instance + `CREATE EXTENSION` | 1 JVM process (~1-2GB heap) | `import kuzu` (zero infra) | 3 Docker containers minimum |
| **Additional infrastructure** | None (reuses PG) | Separate server, ports, config | None | Meta service, Graph service, Storage service |
| **Backup strategy** | PG ecosystem (pg_dump, pg_basebackup, WAL, pgBackRest) | `neo4j-admin dump` (offline) | File copy | Built-in snapshot commands |
| **Monitoring** | PG ecosystem (pg_stat_*, pgwatch2, Datadog PG integration) | Built-in metrics + JMX | None (embedded) | Built-in dashboard, Prometheus exporter |
| **Resource sharing** | Shares PG memory, connections, CPU | Dedicated JVM resources | Shares application process | Dedicated cluster resources |
| **Co-tenancy** | Full PG ecosystem: PostGIS, TimescaleDB, pgvector, JSONB, FDW, pg_cron | APOC procedures, GDS plugin | Extensions (algo, vector, fts) | NebulaGraph tools only |
| **Configuration complexity** | PG config (postgresql.conf) + `LOAD 'age'` | neo4j.conf (JVM tuning, heap, pagecache) | Zero config | Per-service config files + cluster topology |
| **Managed cloud options** | Any managed PG: Azure, AWS RDS/Aurora, GCP Cloud SQL, Supabase | Neo4j Aura | None | NebulaGraph Cloud |
| **Container support** | PG Docker images | Official Neo4j Docker image | pip install (no container needed) | Official Docker Compose |
| **Upgrade path** | PG major version upgrade + `ALTER EXTENSION UPDATE` | Rolling upgrade (Enterprise only); stop/upgrade/start (CE) | Library version bump | Rolling upgrade across services |

### Analysis

**AGE's operational model is its strongest differentiator.** Because AGE is a PostgreSQL
extension, it inherits everything PostgreSQL provides: battle-tested backup/recovery,
replication, monitoring, connection pooling, managed cloud hosting, and decades of
operational tooling. There is no additional infrastructure to manage. An organization
already running PostgreSQL can add graph capabilities with a single `CREATE EXTENSION age`
command.

The co-tenancy story is uniquely powerful. In a single PostgreSQL instance, you can run:
- Graph queries (AGE)
- Vector similarity search (pgvector)
- Geographic queries (PostGIS)
- Time-series data (TimescaleDB)
- Full-text search (tsvector)
- Document storage (JSONB)
- Scheduled jobs (pg_cron)
- Foreign data federation (FDW)
- Star schema analytics (standard SQL)

No other database in this comparison can offer this breadth of capability in a single
process with zero-hop cross-domain queries.

**Neo4j CE** requires a dedicated JVM process with its own configuration, monitoring,
and backup tooling. It's a well-documented and mature operational model, but it's
separate infrastructure that must be managed independently from the rest of your data
stack.

**Kuzu** had the simplest operational model: `import kuzu` in Python and you have a graph
database. No server, no configuration, no network. The trade-off is no multi-process
access, no replication, and no separation of application and database concerns.

**NebulaGraph** has the most complex operational model. A minimum deployment requires
three service types (metad, graphd, storaged), typically run as three or more Docker
containers. This complexity buys you horizontal scalability and built-in HA, but the
operational overhead is substantial for small-to-medium deployments.

---

## Piggie Thesis Implications

**The thesis:** AGE + pgvector in one PostgreSQL instance replaces Neo4j + Pinecone
for graph + vector workloads.

### Where AGE Wins

1. **Co-tenancy and zero-hop hybrid queries.** This is the fundamental advantage. Graph
   traversal + vector similarity + SQL analytics in a single process, sharing the same
   transaction context. No network hops, no data synchronization, no eventual consistency
   between separate systems. No other database in this comparison can do this.

2. **Licensing and SaaS viability.** Apache 2.0 with no feature gating means Piggie can
   be offered as a commercial platform without licensing friction. Neo4j's AGPL and
   NebulaGraph's Enterprise gating both create barriers for SaaS deployment.

3. **Operational simplicity.** For organizations already running PostgreSQL (which is most
   organizations), AGE adds graph capabilities with zero additional infrastructure.
   Monitoring, backup, replication, connection pooling -- it's all already there.

4. **Replication and HA.** PostgreSQL's replication ecosystem is more mature and flexible
   than anything Neo4j Community offers (which is nothing). Patroni-managed clusters with
   automatic failover are production-proven at massive scale.

5. **Vector search breadth.** pgvector provides more index types (HNSW + IVFFlat), more
   similarity functions (L2, cosine, inner product), and higher dimension limits (16,000)
   than Neo4j's native vector indexes.

### Where AGE Loses

1. **Cypher dialect coverage (~55% vs Neo4j's ~95%).** The most visible gap. Missing
   MERGE ON SET, string predicates, list predicates, UNION, CALL subqueries, and pattern
   comprehensions. Every Cypher tutorial and Neo4j migration guide will hit these gaps.
   This is the primary barrier to AGE adoption.

2. **Graph algorithms (none built-in vs Neo4j's 60+).** GDS is a genuine competitive
   moat. The SDK's NetworkX integration is a viable workaround for datasets that fit in
   memory, but it cannot compete with GDS for large-scale in-database computation.

3. **Bulk loading performance.** Cypher-based inserts degrade severely beyond ~200K edges.
   Neo4j's `neo4j-admin import` and Kuzu's `COPY FROM` are dramatically faster. AGE's
   `load_labels_from_file()` requires server-side file access, which is impractical for
   cloud deployments.

4. **Constraint integration.** While PostgreSQL constraints are available at the SQL
   level, they are not integrated into Cypher DDL. Users must understand AGE's internal
   table structure to set up constraints. Neo4j's `CREATE CONSTRAINT` is more ergonomic.

5. **Driver ecosystem maturity.** AGE's drivers are thin wrappers around PG drivers.
   Neo4j's purpose-built drivers provide richer features (managed transactions, automatic
   retry, streaming cursors, routing). The Piggie SDK is our answer to this gap.

### What the SDK Can Compensate For

The Piggie SDK can bridge several of AGE's gaps without requiring upstream AGE changes:

- **Graph algorithms:** NetworkX and PyG integration (already implemented in SDK v0.5.0)
- **Bulk loading:** COPY-based bulk loader that bypasses Cypher (planned P1)
- **Hybrid queries:** CTE-based graph+vector queries (already implemented)
- **Rich driver features:** Typed results, DataFrame integration, connection pooling,
  transaction management (already implemented)
- **Schema introspection:** Catalog-based schema discovery (already implemented)
- **Constraint helpers:** SDK methods that apply PG constraints to AGE label tables (planned)

### What Requires Upstream AGE Work

These gaps cannot be solved at the SDK level and require contributions to Apache AGE core:

- **MERGE ON CREATE SET / ON MATCH SET** -- PR #2347 submitted, awaiting review
- **String predicates** (starts with, ends with, contains, =~) -- issues #552-556
- **List predicates** (all, any, none, single) -- issues #552-556
- **UNION / UNION ALL** -- no issue filed yet
- **CALL subqueries** -- issue #1690
- **WHERE pattern matching** -- issue #1577
- **Multiple labels** -- PR #2082 exists, needs champion
- **Unique constraints at Cypher level** -- issue #45
- **agtype to JSONB casting** -- issue #350
- **Cypher-level vector functions** -- issue #1121

### Strategic Conclusion

The thesis holds for workloads that prioritize operational simplicity, licensing freedom,
and hybrid graph+vector+SQL capabilities over Cypher completeness and built-in graph
algorithms. The SDK layer can compensate for algorithm and bulk loading gaps. The Cypher
dialect gaps are the hardest to solve and require sustained upstream contribution -- but
the openCypher specification provides a clear roadmap, and our active PR pipeline (#2347
and related work) is closing the most impactful gaps.

The competitive landscape also shifts in AGE's favor with Kuzu's archival (one fewer
Cypher-native embedded alternative) and NebulaGraph's enterprise gating of vector and
algorithm features (making the free tier less competitive for AI workloads).

---

## Kuzu Migration Path

Kuzu's archival in October 2025 left a community of graph database users without a
maintained project. The community forks (RyuGraph, Ladybug) face a significant challenge:
*"there's probably six people that actually understand the codebase"* (community member
on Discord). Maintaining a complex C++ graph engine without the original team is a
fundamentally different problem than contributing to an actively-maintained project with
institutional support (which is AGE's situation under the Apache Software Foundation).

### Why Kuzu Users Should Consider Piggie + AGE

| Kuzu Strength | Piggie + AGE Equivalent |
|---|---|
| Cypher queries (~80% coverage) | Cypher queries (~55%, closing fast via upstream PRs) |
| Embedded, zero-config | `CREATE EXTENSION age` on existing PG (near-zero config) |
| MIT license | Apache 2.0 (equally permissive for commercial use) |
| COPY FROM CSV/Parquet (fast bulk load) | PG COPY protocol (planned COPY-based SDK loader) |
| HNSW vector extension | pgvector HNSW + IVFFlat (more index types, more similarity functions) |
| Algorithm extensions (PageRank, Louvain, WCC) | SDK-level NetworkX/PyG integration (same algorithms, different execution model) |
| Typed schema with primary keys | PG-level constraints on AGE label tables |

### What Kuzu Users Gain by Switching

1. **Active maintenance.** AGE is maintained under the Apache Software Foundation with
   regular releases and an active contributor community. Piggie is actively developed with
   upstream AGE contributions (8 PRs submitted, 4 merged as of March 2026).

2. **Vector search.** Kuzu's vector extension was functional but immature at archival.
   pgvector is production-proven with HNSW + IVFFlat indexes, three similarity functions,
   and 16,000-dimension support.

3. **Replication and HA.** Kuzu was embedded-only with no replication. AGE inherits
   PostgreSQL's streaming replication, hot standby, Patroni failover -- the full production
   HA stack.

4. **Co-tenancy.** Graph + vector + geospatial + time-series + full-text + JSONB + SQL
   analytics in one PostgreSQL instance. Kuzu was graph-only.

5. **Managed cloud.** Any managed PostgreSQL service (AWS RDS, Azure, GCP Cloud SQL,
   Supabase) can run AGE. Kuzu had no cloud offering.

6. **Ecosystem longevity.** PostgreSQL has a 30-year track record. AGE's data lives in
   standard PG tables -- even if AGE development stopped, the data remains accessible via
   SQL. Kuzu's proprietary binary format is now maintained by volunteer forks.

### Migration Considerations

**Cypher compatibility:** Most Kuzu Cypher queries will work in AGE with minor
modifications. Key differences:

- Kuzu uses `$param` syntax; AGE uses `%s` parameter markers (the Piggie SDK handles this
  transparently)
- Kuzu supports `MERGE ON CREATE SET / ON MATCH SET`; AGE does not yet (PR #2347 pending)
- Kuzu supports `starts with`, `ends with`, `contains`; AGE does not yet (issues #552-556)
- Kuzu requires schema DDL before data insertion; AGE creates labels on first use
- Kuzu's `COPY FROM` has no direct Cypher equivalent in AGE; use the Piggie SDK's bulk
  loader or PG COPY

**Data migration:** Export from Kuzu using `COPY TO` (CSV/Parquet), then import into AGE
using the Piggie SDK's bulk loader or direct PG COPY to AGE label tables.

**Performance expectations:** AGE will be slower than Kuzu for VLE and bulk load
operations, comparable for point lookups and pattern matching, and faster for vector
search. The benchmark suite in this repository provides exact numbers at 10K and 100K
scale for transparent comparison.

---

## Sources

- [Apache AGE GitHub](https://github.com/apache/age) and issue tracker
- [Neo4j Cypher Manual](https://neo4j.com/docs/cypher-manual/current/)
- [Neo4j GDS Manual v2.26](https://neo4j.com/docs/graph-data-science/current/)
- [Neo4j Community Edition](https://neo4j.com/product/community-edition/)
- [Neo4j Vector Indexes Documentation](https://neo4j.com/docs/cypher-manual/current/indexes/semantic-indexes/vector-indexes/)
- [Neo4j Constraints Documentation](https://neo4j.com/docs/cypher-manual/current/constraints/)
- [Neo4j Clustering Documentation](https://neo4j.com/docs/operations-manual/current/clustering/)
- [Kuzu Documentation](https://docs.kuzudb.com/) (archived)
- [Kuzu GitHub](https://github.com/kuzudb/kuzu) (archived Oct 2025)
- [Kuzu Cypher Differences from Neo4j](https://docs.kuzudb.com/cypher/difference/)
- [Kuzu MERGE Documentation](https://docs.kuzudb.com/cypher/data-manipulation-clauses/merge/)
- [Kuzu Vector Extension](https://docs.kuzudb.com/extensions/vector/)
- [RyuGraph Fork](https://github.com/predictable-labs/ryugraph)
- [NebulaGraph 3.8 Manual](https://docs.nebula-graph.io/3.8.0/)
- [NebulaGraph nGQL Overview](https://docs.nebula-graph.io/3.0.0/3.ngql-guide/1.nGQL-overview/1.overview/)
- [NebulaGraph Architecture Overview](https://www.nebula-graph.io/posts/nebula-graph-architecture-overview)
- [NebulaGraph Storage Engine](https://www.nebula-graph.io/posts/nebula-graph-storage-engine-overview)
- [NebulaGraph Algorithm (Spark-based)](https://github.com/vesoft-inc/nebula-algorithm)
- [NebulaGraph Clients](https://github.com/vesoft-inc/nebula-clients)
- [Piggie SDK Vision](../SDK-VISION.md)
- [AGE Feature Prioritization](age-feature-prioritization.md)
- [Neo4j Python SDK Analysis](neo4j-python-sdk-analysis.md)
