Add peo4c: neo4j-browser on PostgreSQL + Apache AGE
A single container image with: - neo4j-browser built from source and served on :7474 - PostgreSQL with Apache AGE (based on apache/age) - a Python bridge speaking Bolt 4.4/5.0-5.4 over TCP and WebSocket on :7687 that runs Cypher through AGE's cypher() function, emulates the Neo4j procedures neo4j-browser relies on, and maps databases to AGE graphs Includes workarounds for AGE limitations (parameters are inlined as literals, `id(x) IN [...]` and the browser's expand-node query are rewritten) and an end-to-end smoke test using the Neo4j Python driver. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
47b8f5f746
15 files changed
+1850
No files matched your search
@@ -0,0 +1,99 @@
|
||||
# peo4c
|
||||
|
||||
[neo4j-browser](https://github.com/neo4j/neo4j-browser) running on PostgreSQL, using
|
||||
[Apache AGE](https://github.com/apache/age) as the graph engine.
|
||||
|
||||
Everything ships in one container image:
|
||||
|
||||
```
|
||||
neo4j-browser (static files) peo4c bridge (Python) PostgreSQL + AGE
|
||||
┌──────────────────────┐ Bolt ┌──────────────────────────┐ SQL ┌────────────────┐
|
||||
│ in your web browser │───────▶│ Bolt server (TCP + WS) │──────▶│ cypher('g',$$ │
|
||||
│ served on :7474 │ :7687 │ Cypher → AGE translation │ :5432 │ ... $$) │
|
||||
└──────────────────────┘ │ emulated Neo4j procedures │ └────────────────┘
|
||||
└──────────────────────────┘
|
||||
```
|
||||
|
||||
## Quick start
|
||||
|
||||
```sh
|
||||
podman build -t peo4c .
|
||||
podman run -d --name peo4c -e POSTGRES_PASSWORD=secret \
|
||||
-p 7474:7474 -p 7687:7687 -p 5432:5432 \
|
||||
-v peo4c-data:/var/lib/postgresql peo4c
|
||||
```
|
||||
|
||||
Open <http://localhost:7474/> and log in with your **PostgreSQL** credentials:
|
||||
user `postgres`, password the value of `POSTGRES_PASSWORD`. Bolt clients (the Neo4j
|
||||
drivers, cypher-shell) can connect to `neo4j://localhost:7687` the same way, and
|
||||
`psql` works on port 5432.
|
||||
|
||||
`docker` works as a drop-in replacement for `podman` in the commands above.
|
||||
|
||||
## How it works
|
||||
|
||||
- **Databases are AGE graphs.** Every AGE graph shows up as a Neo4j database, and
|
||||
`:use <name>` switches between them. A graph named `neo4j` is created on first start
|
||||
(`PEO4C_DEFAULT_GRAPH`). `CREATE DATABASE x` and `DROP DATABASE x` on the `system`
|
||||
database call `create_graph` / `drop_graph`; AGE requires names of at least 3 characters.
|
||||
- **Queries** are sent to AGE as `SELECT * FROM cypher('<graph>', $$ <query> $$) AS (...)`.
|
||||
The bridge works out the result columns from the final `RETURN` clause and inlines
|
||||
query parameters as Cypher literals.
|
||||
- **Neo4j procedures** that neo4j-browser relies on (`dbms.components`, `SHOW DATABASES`,
|
||||
`db.labels`, `db.schema.visualization`, `SHOW PROCEDURES`, ...) are emulated from the
|
||||
AGE catalog.
|
||||
- **Workarounds for AGE**: neo4j-browser's "expand node" query uses a pattern
|
||||
comprehension, and its relationship lookup uses `id(n) IN [...]`, which AGE rejects
|
||||
for lists of more than one element. The bridge rewrites both into equivalent Cypher
|
||||
that AGE accepts.
|
||||
- **Authentication** is passed straight through: every Bolt connection opens its own
|
||||
PostgreSQL connection with the credentials you log in with.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Variable | Default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `POSTGRES_PASSWORD` | (required) | Password of the PostgreSQL superuser, see the [postgres image docs](https://hub.docker.com/_/postgres). `POSTGRES_USER` and `POSTGRES_DB` work too. |
|
||||
| `PEO4C_DEFAULT_GRAPH` | `neo4j` | Graph created at initialisation and used when a client does not pick a database. |
|
||||
| `PEO4C_PG_DATABASE` | `$POSTGRES_DB` | PostgreSQL database holding the graphs. |
|
||||
| `PEO4C_ADVERTISED_BOLT_PORT` | `7687` | Bolt port advertised to the browser; set it when you publish 7687 on another host port. |
|
||||
| `PEO4C_ADVERTISED_HOST` | `localhost` | Host reported in `SHOW DATABASES` and to clients that do not send a routing address. |
|
||||
| `PEO4C_LOG_LEVEL` | `INFO` | `DEBUG` logs every generated SQL statement. |
|
||||
|
||||
Build arguments: `AGE_IMAGE` (base image, default `apache/age:release_PG18_1.8.0`) and
|
||||
`NEO4J_BROWSER_REF` (neo4j-browser commit to build).
|
||||
|
||||
## Limitations
|
||||
|
||||
This is a first version. Things that do not work (yet):
|
||||
|
||||
- `RETURN *`, `EXPLAIN` and `PROFILE`.
|
||||
- Write statistics: write queries report "(no changes, no records)" even when they
|
||||
changed the graph.
|
||||
- Anything AGE does not support, for example multiple labels per node, pattern
|
||||
comprehensions, temporal and spatial types, indexes and constraints through Cypher.
|
||||
- User and role management, `SHOW TRANSACTIONS`, `:sysinfo`.
|
||||
- TLS (`bolt+s://`, HTTPS).
|
||||
- Each query's full result is fetched from PostgreSQL before it is streamed to the
|
||||
client, so very large results use memory in the bridge.
|
||||
|
||||
## Development
|
||||
|
||||
The bridge is a dependency-light Python package in [`bridge/peo4c`](bridge/peo4c)
|
||||
(only `psycopg` 3). To run it outside the container against any AGE-enabled
|
||||
PostgreSQL:
|
||||
|
||||
```sh
|
||||
pip install 'psycopg[binary]'
|
||||
cd bridge
|
||||
PEO4C_PG_HOST=localhost PEO4C_PG_DATABASE=postgres PEO4C_BROWSER_DIR=/path/to/neo4j-browser/dist \
|
||||
python -m peo4c
|
||||
```
|
||||
|
||||
[`tests/smoke_test.py`](tests/smoke_test.py) runs end-to-end checks through the official
|
||||
Neo4j Python driver against a running instance:
|
||||
|
||||
```sh
|
||||
pip install neo4j
|
||||
python tests/smoke_test.py neo4j://localhost:7687 postgres secret
|
||||
```
|
||||
Reference in new issue
Block a user