arango-sparql-py
Status: v0.1 — active development. The translator and HTTP service are working; a W3C-conformant
/sparqlProtocol endpoint is on the v1.0 roadmap. Seedocs/architecture/PRD.mdfor the full v1 spec, scope, and release milestones.
Python-native SPARQL 1.1 → ArangoDB AQL transpiler and FastAPI
microservice. Modernizes the legacy JavaScript Foxx
arango-sparql service and
deliberately mirrors the architecture of its sister project
arango-cypher-py, so a
developer fluent in one repo can read the other immediately.
Why this exists
ArangoDB is a multi-model database with a powerful native query language
(AQL), but lots of teams have invested in SPARQL — for ontology-driven
data, federated knowledge graphs, or simply because their data already
lives as RDF/Turtle. arango-sparql-py lets those teams point a
SPARQL 1.1 query at ArangoDB without first re-modeling their data:
- The query is parsed by
rdflib(W3C-grade SPARQL 1.1 parser). - An OWL ontology describing the physical schema is loaded once at startup; IRIs in the query are resolved to ArangoDB collection / property names.
- The algebra walker emits parameterized AQL with bind variables (no string interpolation, no injection vector).
- Optionally: the AQL is executed against ArangoDB and results are surfaced back as bindings.
Cross-validation against pyoxigraph
(W3C-conformant Rust triplestore via Python bindings) keeps every
translation honest.
Example: SPARQL in → AQL out
PREFIX : <http://ex.org/>
SELECT ?n ?title WHERE {
{ ?p a :Person ; :name ?n }
{ ?prj a :Project ; :title ?title ; :owner ?p }
}
ORDER BY ?n LIMIT 10
translates to:
FOR doc1 IN @@c1_Person
FOR doc2 IN @@c2_Project
FILTER doc2.owner == doc1._uri
SORT doc1.name ASC
LIMIT 10
RETURN { n: doc1.name, title: doc2.title }
with bind_vars = {"@c1_Person": "Person", "@c2_Project": "Project"}.
Architecture at a glance
| Concern | Implementation |
|---|---|
| SPARQL parsing | rdflib.plugins.sparql.parser.parseQuery + Algebra translation |
| AQL emission | Parameterized AQL builder (port of legacy aql-query-builder.js) |
| Schema mapping | MappingBundle / CSI v1 from arango-schema-analyzer is the contract of record; its OWL/Turtle export is one serialisation of it, loaded once into rdflib.Graph |
| HTTP service | FastAPI (arango_sparql.service) — mirror of arango_cypher.service |
| NL → SPARQL | LLM-backed pipeline (arango_sparql.nl2sparql) with cost accounting + repair loop |
| Reference triplestore | pyoxigraph, embedded, W3C-compliant — used as cross-validation gold |
| Test harnesses | pytest + W3C SPARQL 1.1 DAWG runner (tests/w3c/) + cross-validation suite |
| Frontend | Vite + React + TypeScript, CodeMirror SPARQL mode, Cytoscape.js graph view |
Quickstart
git clone https://github.com/arango-solutions/arango-sparql-py.git
cd arango-sparql-py
# 1. Install (works with `uv` or plain `pip install -e ".[dev]"`)
uv sync --all-extras
# 2. Run the smoke tests (no DB needed)
uv run pytest -q -m "not integration and not w3c and not eval"
# 3. Boot the service (defaults to http://localhost:8000)
uv run python main.py
# 4. (optional) Stand up ArangoDB for /execute round-trips
docker compose up -d
# 5. (optional) Translate a query from the CLI
uv run arango-sparql-py translate \
--sparql 'PREFIX : <http://ex.org/> SELECT ?s WHERE { ?s a :Person }' \
--ontology-file my-schema.ttl
Dedicated database (no manual setup). Point
ARANGO_DBat a database other than_system(e.g.ARANGO_DB=sparql-to-aqlin.env) and the service auto-creates it on first boot when it is missing —main.pyruns a best-effort provisioning step outside public mode. ArangoDB never auto-creates databases, so this saves a manual step before/connectworks. To provision out-of-band instead, runuv run python scripts/ensure_database.py; to disable the boot step, setARANGO_SPARQL_SKIP_DB_BOOTSTRAP=1.
Sibling-repo work (porting from the legacy Foxx service or mirroring patterns from
arango-cypher-py)? Run./scripts/setup_references.shto symlink them underreferences/. Symlinks are gitignored — they only matter for AI agents and porting work.
HTTP surface (current)
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Liveness probe |
POST |
/connect / /disconnect |
Open / close an ArangoDB session |
POST |
/translate |
SPARQL → AQL (no DB access) |
POST |
/validate |
SPARQL parse-only validation |
POST |
/execute |
SPARQL → AQL → ArangoDB → bindings |
POST |
/execute-aql |
Pass-through AQL with the same session |
POST |
/explain / /profile |
AQL execution plan / per-stage profile |
POST |
/nl-translate, /nl-explain, /nl-execute |
LLM-backed NL → SPARQL → AQL |
The W3C-conformant GET/POST /sparql Protocol endpoint
(content-negotiated SPARQL Results JSON / XML / CSV / TSV, plus
Service Description) is the headline v1.0 deliverable — see PRD §5.2.
SPARQL conformance
W3C SPARQL 1.1 DAWG translation-only coverage at main:
| Category | Coverage |
|---|---|
| Syntax (positive) | 100.0 % (63/63) |
| Syntax (negative) | 67.4 % (29/43, the 14 xfails are rdflib parser-permissiveness gaps, not translator gaps) |
| Query evaluation | 95.7 % (242/253) — see COVERAGE_REPORT.md for the full XFAIL ledger driving the visitor priority queue |
Visitors shipped today: BGP, Filter, Project, Distinct, Slice,
OrderBy, AskQuery, Extend (BIND), LeftJoin (OPTIONAL),
AggregateJoin (COUNT / SUM / AVG / MIN / MAX /
GROUP_CONCAT + GROUP BY + HAVING), Join (multi-subject BGPs
lowered to AQL equality FILTERs), and property-path expansion
(SequencePath, InvPath, AlternativePath, bounded :p+ /
:p* / :p? via UNION desugaring with nested-modifier collapsing
e.g. ((:p)*)* → :p*, and forward-only NegatedPath via
ATTRIBUTES fan-out with NOT IN guard). FILTER expressions also
cover IN / NOT IN (including the empty-set form) and XSD
constructor casts (xsd:double / xsd:integer / xsd:string / …).
Repository layout
arango_sparql/
api.py # public translate() entry point
errors.py # typed SparqlError hierarchy (E_SPARQL_*, E_SCHEMA_RESOLVE, …)
cli.py # typer CLI
_env.py # central env-var resolver (ARANGO_PASSWORD, …)
translate/
parser.py # rdflib parser wrapper
visitor.py # one visit_<NodeType> per Algebra op
builder.py # parameterized AQL query builder
resolver.py # OWL → ArangoDB collection / property resolver
service/
app.py # FastAPI app + CORS + public-mode guardrails
models.py # pydantic request/response models + _MAX_* limits
security.py # sessions, rate limit, SSRF guard, error redaction
routes/
health.py
connect.py # /connect, /disconnect, /connect/defaults
sparql.py # /translate, /validate, /execute*, /explain, /profile
nl.py # /nl-translate, /nl-explain, /nl-execute
nl2sparql/
pipeline.py # NL → SPARQL pipeline (LLM + repair loop + cost)
prompt.py, client.py, repair.py, cost.py, models.py
tests/
translate/ # parser unit tests + YAML-driven goldens
cross/ # pyoxigraph cross-validation (PG + multi-model PG/LPG/hybrid/RPT + edge-collection traversal + MINUS/EXISTS + MINUS-with-OPTIONAL + RPT cross-subject OPTIONAL)
schema/ # MappingBundle fixtures + §13.3 per-model contracts
w3c/ # W3C SPARQL 1.1 DAWG runner + COVERAGE_REPORT.md
nl2sparql/eval/ # NL eval harness (slow, gated on RUN_EVAL=1)
helpers/oxi.py # pyoxigraph fixtures + binding comparison
helpers/aql_interp.py # shared in-memory AQL-subset interpreter
ui/ # Vite + React + TS frontend
references/ # symlinks to sibling repos (gitignored, recreated locally)
docs/architecture/ # PRD.md = single source of truth (spec + ADRs + vision); vision.md & decisions/ are stubs
.cursor/
rules/ # scoped Cursor rules (000-context, 100-backend, 200-testing, …)
skills/sparql-to-aql/ # SPARQL→AQL porting recipe (read this first)
Documentation
docs/architecture/PRD.md— the single source of truth: HTTP surface, supported physical schema shapes (document, hybrid multi-class, edge traversal, named graphs), NL pipeline, conformance targets, release milestones, plus the folded-in decision records (Appendix B) and the inception narrative (Appendix C).docs/architecture/implementation_plan.md— the living work-tracking plan (WP status), including the chat-first UI shell migration (WP-UI-SHELL). The PRD is the spec; this is status.docs/architecture/vision.mdanddocs/architecture/decisions/— redirect stubs that point into the PRD appendices (kept so old links resolve); do not add content there.CONTRIBUTING.md— dev setup, test gates, porting recipe pointer.SECURITY.md— vulnerability reporting flow.AGENTS.md— shared contract for AI coding agents working on this repo (Cursor, Claude Code, Codex CLI, Copilot Workspace).
License
MIT. Copyright (c) 2026 Arthur Keen.
Release files for arango-sparql-py 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| arango_sparql_py-0.2.0.tar.gz | 2.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| arango_sparql_py-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.7 MB
Release files / arango_sparql_py-0.2.0.tar.gz
| Download URL | arango_sparql_py-0.2.0.tar.gz |
|---|---|
| Size | 2.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
80d59ce08a73328d90edb98a31f44e5de28d13de06ea35e86302c6feb194788c
|
|
BLAKE2b-256 checksum How to use checksums |
c5066f9e7c1103bf71d5bfc5287277a6d0d51368cd1f84f0790cc1edf99c7411
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency logRelease files / arango_sparql_py-0.2.0-py3-none-any.whl
| Download URL | arango_sparql_py-0.2.0-py3-none-any.whl |
|---|---|
| Size | 331.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5a1864ba5c801bbc7773f882eefb84a0b8429d226bf60cd3f632e1314d412d5a
|
|
BLAKE2b-256 checksum How to use checksums |
26b9253077cd03851320015be4eeab197cc53a0067c738e3de93476251644325
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency log