DBAPI-style adapter that translates SQL (JOIN/DDL/aggregations) into MongoDB operations
Project description
dbapi-mongodb
DBAPI-style adapter that lets you execute a limited subset of SQL against MongoDB by translating SQL to Mongo queries. Built on pymongo (3.13.x for MongoDB 3.6 compatibility) and SQLGlot.
Purpose: let existing DB-API / SQLAlchemy Core / FastAPI code treat MongoDB as “just another dialect.”
- PyPI package name:
dbapi-mongodb - Module import name:
mongo_dbapi
Release Policy (Join-first / MongoDB 4.4)
- This release locks on practical JOIN compatibility first, targeting MongoDB 4.4.
- Guaranteed JOIN scope: INNER/LEFT (up to 3 hops, composite ON, non-equi ON), RIGHT OUTER (single JOIN, equality ON), FULL OUTER (single JOIN, equality ON), JOIN with
ORDER BY/LIMIT/OFFSET, and JOIN +GROUP BY/HAVING(A27 pattern:COALESCE(...) + COUNT(*)). - Other advanced SQL features remain available where implemented, but further hardening is treated as future work. Unsupported cases return
[mdb][E2].
Features
-
DBAPI-like
Connection/Cursor -
SQL → Mongo:
SELECT/INSERT/UPDATE/DELETE,CREATE/DROP TABLE/INDEX(ASC/DESC, UNIQUE, composite),WHERE(comparisons/AND/OR/IN/BETWEEN/LIKE/ILIKE/regex literal),ORDER BY,LIMIT/OFFSET, JOIN (INNER/LEFT up to 3 hops with composite/non-equi ON, RIGHT/FULL OUTER for single JOIN with equality ON),GROUP BY+ aggregates +HAVING(including FULL OUTER A27 pattern), simple CASE aggregates (SUM(CASE WHEN ... THEN ... ELSE ... END)),UNION ALL/UNION(multi-branch), subqueries (WHERE IN/EXISTS/NOT EXISTS, scalar subquery in comparisons,FROM (SELECT ...)), non-recursiveWITHCTE -
%spositional and%(name)snamed parameters; unsupported constructs raise Error IDs (e.g.[mdb][E2]) -
Error IDs for common failures: invalid URI, unsupported SQL, unsafe DML without WHERE, parse errors, connection/auth failures
-
DBAPI fields:
rowcount,lastrowid,description(column order: explicit order, or alpha forSELECT *; JOIN uses left→right) -
Transactions:
begin/commit/rollbackwrap Mongo sessions; MongoDB 3.6 and other unsupported envs are treated as no-op success -
Async dialect (thread-pool backed) for Core CRUD/DDL/Index with FastAPI-friendly usage; minimal ORM CRUD for single-table entities (relationships out of scope)
-
Use cases
- Swap in Mongo as “another dialect” for existing SQLAlchemy Core–based infra (Engine/Connection + Table/Column)
- Point existing Core-based batch/report jobs at Mongo data with minimal changes
- Minimal ORM CRUD for single-table entities (PK →
_id) - Async dialect for FastAPI/async stacks (thread-pool implementation; native async later)
Requirements
- Python 3.10+
- MongoDB 3.6 (bundled
mongodb-3.6binary) or later (note: bundled binary is 3.6, so transactions are unsupported) - Virtualenv at
.venv(already present); dependencies are managed viapyproject.toml
Installation
pip install dbapi-mongodb
# (optional) with a virtualenv: python -m venv .venv && . .venv/bin/activate && pip install dbapi-mongodb
Start local MongoDB (bundled 3.6)
# Default port 27017; override with PORT
PORT=27018 ./startdb.sh
Start local MongoDB 4.4 (replica set, bundled)
# Default port 27019; uses bundled libssl1.1. LD_LIBRARY_PATH is set inside the script for mongod.
PORT=27019 ./start4xdb.sh
# Run tests against 4.x
MONGODB_URI=mongodb://127.0.0.1:27019 MONGODB_DB=mongo_dbapi_test .venv/bin/pytest -q
Usage example
from mongo_dbapi import connect
conn = connect("mongodb://127.0.0.1:27018", "mongo_dbapi_test")
cur = conn.cursor()
cur.execute("INSERT INTO users (id, name) VALUES (%s, %s)", (1, "Alice"))
cur.execute("SELECT id, name FROM users WHERE id = %s", (1,))
print(cur.fetchall()) # [(1, 'Alice')]
print(cur.rowcount) # 1
Supported SQL
- Statements:
SELECT,INSERT,UPDATE,DELETE,CREATE/DROP TABLE,CREATE/DROP INDEX - WHERE: comparisons (
=,<>,>,<,<=,>=),AND,OR,IN,BETWEEN,LIKE(%/_→$regex),ILIKE, regex literal/.../ - JOIN: INNER/LEFT (composite ON, non-equi ON, up to 3 joins), RIGHT OUTER (single JOIN, equality ON), FULL OUTER (single JOIN, equality ON)
- Aggregation:
GROUP BYwith COUNT/SUM/AVG/MIN/MAX andHAVING - Subqueries:
WHERE IN/EXISTS/NOT EXISTS(correlated EXISTS in limited simple form), scalar non-correlated subqueries in comparisons, andFROM (SELECT ...) - Set ops:
UNION ALL/UNION(supports 3+ branches; mixedUNION+UNION ALLis rejected) - CTE: non-recursive
WITH - Window:
ROW_NUMBER/RANK/DENSE_RANKon MongoDB 5.x+ ([mdb][E2]on 4.4) - ORDER/LIMIT/OFFSET
- Unsupported (4.4 target):
WITH RECURSIVE, RIGHT/FULL OUTER with non-equality ON, RIGHT/FULL join chains, complex FULL OUTER aggregate shapes (outsideCOALESCE(...) + COUNT(*)), complex correlated subqueries, mixedUNION+UNION ALL, ORM relationships
SQLAlchemy
- DBAPI module attributes:
apilevel="2.0",threadsafety=1,paramstyle="pyformat". - Scheme:
mongodb+dbapi://...dialect provided (sync + async/thread-pool). - Scope: Core text()/Table/Column CRUD/DDL/Index、ORM 最小 CRUD(単一テーブル)、JOIN/UNION ALL/HAVING/subquery/ROW_NUMBER を実通信で確認済み。async dialect は Core CRUD/DDL/Index のラップで、ネイティブ async は今後検討。
Async (FastAPI/Core) - beta
- Current implementation wraps the sync driver in a thread pool (native async driver is planned). Provided via
mongo_dbapi.async_dbapi.connect_async. API mirrors sync Core: awaitable CRUD/DDL/Index, JOIN/UNION ALL/HAVING/IN/EXISTS/FROM subquery. - Transactions: effective on MongoDB 4.x+ only; 3.6 is no-op. Be mindful that MongoDB transactions differ from RDBMS in locking/perf; avoid heavy transactional workloads.
- Window:
ROW_NUMBER/RANK/DENSE_RANKare available on MongoDB 5.x+; earlier versions return[mdb][E2] Unsupported SQL construct: WINDOW_FUNCTION. - FastAPI example:
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import create_async_engine, AsyncConnection
from sqlalchemy import text
engine = create_async_engine("mongodb+dbapi://127.0.0.1:27019/mongo_dbapi_test")
app = FastAPI()
async def get_conn() -> AsyncConnection:
async with engine.connect() as conn:
yield conn
@app.get("/users/{user_id}")
async def get_user(user_id: str, conn: AsyncConnection = Depends(get_conn)):
rows = await conn.execute(text("SELECT id, name FROM users WHERE id = :id"), {"id": user_id})
row = rows.fetchone()
return dict(row) if row else {}
- Limitations: async ORM/relationship and statement cache are out of scope; heavy concurrency uses a thread pool under the hood.
Support levels
- Stable guarantee (Join-first): JOIN-heavy paths (INNER/LEFT up to 3 hops, RIGHT/FULL single JOIN constraints, JOIN + ORDER/LIMIT/OFFSET, JOIN + GROUP BY/HAVING in supported shapes) and single-collection CRUD.
- Available as best effort:
UNION/UNION ALL, non-recursive CTE, scalar subqueries, limited correlated EXISTS, MongoDB 5.x+ window functions. - Not supported / constraints:
WITH RECURSIVE, RIGHT/FULL OUTER with non-equality ON, RIGHT/FULL join chains, complex FULL OUTER aggregate shapes, complex correlated subqueries, mixedUNION+UNION ALL, ORM relationships; async remains thread-pool based.
Running tests
PORT=27018 ./startdb.sh # if 27017 is taken
MONGODB_URI=mongodb://127.0.0.1:27018 MONGODB_DB=mongo_dbapi_test .venv/bin/pytest -q
Tutorials
- English:
docs/tutorial.md - 日本語:
docs/tutorial_ja.md
Notes
- Transactions on MongoDB 3.6 are treated as no-op; 4.x+ (replica set) uses real sessions and the bundled 4.4 binary passes all tests.
- Error messages are fixed strings per
docs/spec.md. Keep logs at DEBUG only (default INFO is silent).
Roadmap (SQL support prioritization)
- Complex correlated subqueries and FULL OUTER aggregate generalization (beyond A27 shape)
WITH RECURSIVEand mixedUNION+UNION ALLchains- Additional window functions beyond ROW_NUMBER/RANK/DENSE_RANK (
LAG/LEAD/NTILE, etc.) - Performance guidance for large JOIN workloads (recommended indexes and slow-query thresholds, especially FULL OUTER)
If you need one of these sooner, please open an issue and share your use case.
License
MIT License (see LICENSE). Provided as-is without warranty; commercial use permitted.
GitHub Sponsors
Maintained in personal time. If this helps you run MongoDB from DB-API/SQLAlchemy stacks, consider supporting via GitHub Sponsors to keep fixes and version updates coming.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dbapi_mongodb-0.2.0.tar.gz.
File metadata
- Download URL: dbapi_mongodb-0.2.0.tar.gz
- Upload date:
- Size: 42.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d69bcbdb36ca438133070bff3d17ef56e9aac27d77cc2fffde8f694b160067e0
|
|
| MD5 |
700eea394519016321532edb0954d3cf
|
|
| BLAKE2b-256 |
6e607a7606be3ca9dd17a350a19402cb7f471a9ec17a31939bac8bdf9125864b
|
File details
Details for the file dbapi_mongodb-0.2.0-py3-none-any.whl.
File metadata
- Download URL: dbapi_mongodb-0.2.0-py3-none-any.whl
- Upload date:
- Size: 33.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d709fff79730da98a4ff017c5a56a076f38cec85b4cde4600acf88349bfa0f54
|
|
| MD5 |
5f8cb02e66c076a9acd115ff083fbf88
|
|
| BLAKE2b-256 |
dc7e29475abda3b7130cc522d5d556e571768ac17fffea20c19352ec0ae73670
|