NeoSQLite - NoSQL for SQLite with PyMongo-like API
NeoSQLite (new + nosqlite) is a pure Python library that provides a schemaless, PyMongo-like wrapper for interacting with SQLite databases. The API is designed to be familiar to those who have worked with PyMongo, providing a simple and intuitive way to work with document-based data in a relational database.
Keywords: NoSQL, NoSQLite, SQLite NoSQL, PyMongo alternative, SQLite document database, Python NoSQL, schemaless SQLite, MongoDB-like SQLite
Features
PyMongo-like API: A familiar interface for developers experienced with MongoDB.- NX-27017: MongoDB Wire Protocol Server — Use PyMongo with SQLite backend
- Schemaless Documents: Store flexible JSON-like documents.
- Lazy Cursor:
find()returns a memory-efficient cursor for iterating over results. - Raw Batch Support:
find_raw_batches()returns raw JSON data in batches for efficient processing. - Advanced Indexing: Single-key, compound-key, nested-key indexes, and FTS5 text search.
- ACID Transactions: Full
ClientSessionAPI with PyMongo 4.x parity using SQLite SAVEPOINTs. - Change Streams: Native SQLite triggers for
watch()— no replica set required. - Advanced Aggregation:
$setWindowFields,$graphLookup,$fill, streaming$facet, and more. - Tier-1 SQL Optimization: Dozens of operators translated to native SQL for 10-100x speedup.
- Native
$jsonSchema: Query filtering and write-time validation via SQLite CHECK constraints. - Window Functions: Complete MongoDB 5.0+ suite (
$rank,$top,$bottom, math operators). - MongoDB-compatible ObjectId: Full 12-byte specification with automatic generation.
- Full GridFS Support: Modern
GridFSBucketAPI plus legacyGridFScompatibility. - Binary Data: PyMongo-compatible
Binaryclass with UUID support. - AutoVacuum & compact: Reclaim disk space with incremental or full VACUUM.
- dbStats Command: MongoDB-compatible statistics with accurate index sizes.
- SQL Translation Caching: 10-30% faster for repeated aggregation pipelines and
$exprqueries. - Configurable Journal Mode: WAL (default), DELETE, MEMORY, and more.
- Security Hardening: Built-in SQL injection protection via centralized identifier quoting.
See Release Notes for the full history.
Latest Release: v1.15.2
NeoSQLite v1.15.2 is a correctness, stability, and documentation release fixing SQL-tier bugs in temporary table $lookup and negative $slice array updates, hardening the NX-27017 wire protocol server (v0.6.3), and updating dependencies. All fixes are backward compatible.
Key Highlights:
$lookupString Foreign Field Fix — Tier-2 hash table creation on string foreign fields no longer fails with SQLitemalformed JSON.- Negative
$sliceUpdate Ordering —$pushupdates with negative$slicenow reliably preserve array ordering without relying on invalid subquery rowids. - NX-27017 Server Hardening (v0.6.3) — Serialized thread access to shared SQLite connections, eliminated change-stream memory leaks, and preserved Decimal128 precision.
- Pinned Container Testing — Pinned differential API test runner to
mongo:8.2.12for reliable testing on Linux kernels $\ge$ 6.19. - Knowledge Base Migration — Fully migrated documentation to Google Open Knowledge Format (OKF) v0.2.
For full details, see documents/releases/v1.15.2.md. Previous release highlights are in documents/releases/v1.15.1.md.
Installation
pip install neosqlite
Optional Extras
# Enhanced JSON/JSONB support (only needed if your SQLite lacks JSON functions)
pip install neosqlite[jsonb]
# Memory-constrained processing for large result sets
pip install neosqlite[memory-constrained]
# NX-27017 MongoDB Wire Protocol Server
pip install "neosqlite[nx27017]" # Core
pip install "neosqlite[nx27017-speed]" # With uvloop (Linux/macOS)
Quickstart
import neosqlite
with neosqlite.Connection(':memory:') as conn:
users = conn.users
# Insert
users.insert_one({'name': 'Alice', 'age': 30})
users.insert_many([
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}
])
# Find
alice = users.find_one({'name': 'Alice'})
for user in users.find():
print(user)
# Update
users.update_one({'name': 'Alice'}, {'$set': {'age': 31}})
# Delete & Count
result = users.delete_many({'age': {'$gt': 30}})
print(f"Remaining: {users.count_documents({})}")
Drop-in Replacement for PyMongo
1. Direct API (No MongoDB)
import neosqlite
client = neosqlite.Connection('mydatabase.db')
collection = client.mycollection
collection.insert_one({"name": "test"})
2. Wire Protocol (NX-27017) — Zero Code Changes
# Start server
nx-27017 --db ./myapp.db
# Then use PyMongo normally — no code changes!
from pymongo import MongoClient
client = MongoClient('mongodb://localhost:27017/')
collection = client.mydatabase.mycollection
collection.insert_one({"name": "test"}) # Works!
PyMongo Compatibility
| Metric | Result |
|---|---|
| Total Tests | 381 |
| Passed | 363 |
| Skipped | 18 (architectural differences) |
| Failed | 0 |
| Compatibility | 100% |
Skipped tests are due to MongoDB requiring a replica set (change streams, transactions) or NeoSQLite extensions ($log2, $contains). All comparable APIs pass. Tested against MongoDB 8.x (mongo:8.2.12 pinned container image).
Run the comparison yourself: ./scripts/run-api-comparison.sh
_id Field Behavior
NeoSQLite uses a strict MongoDB-compatible _id column:
- Integer
_idvalues are stored and queried in the dedicated_idcolumn — not redirected to the auto-incrementidrowid. - String
_idvalues are kept verbatim (e.g.,"5"is a string, not converted to integer5). - Auto-generated
_idproduces MongoDB-compatible 12-byte ObjectIds stored in the_idcolumn. - Legacy collections (created before the
_idcolumn existed) fall back to theidcolumn via an automatic resolver. - Range queries (
$gt,$lt, etc.) on_iduse Python evaluation to preserve correct cross-type comparison semantics (MongoDB BSON ordering).
Key APIs
Indexes
# Single-key, compound, nested
users.create_index('age')
users.create_index([('name', neosqlite.ASCENDING), ('age', neosqlite.DESCENDING)])
users.create_index('profile.followers')
# FTS5 text search
users.create_search_index('bio')
Query Operators
$eq, $gt, $gte, $lt, $lte, $ne, $in, $nin, $and, $or, $not, $nor,
$exists, $type, $regex, $elemMatch, $size, $mod,
$bitsAllSet, $bitsAllClear, $bitsAnySet, $bitsAnyClear,
$text (FTS5), $jsonSchema, and more.
Aggregation Stages
$match, $project, $group, $sort, $skip, $limit, $unwind,
$lookup, $facet, $bucket, $bucketAuto, $sample, $merge,
$setWindowFields, $graphLookup, $fill, $densify, $unionWith,
$replaceRoot, $replaceWith, $unset, $count, $redact, $addFields, $switch.
Transactions
with client.start_session() as session:
with session.start_transaction():
users.insert_one({"name": "Alice"}, session=session)
orders.insert_one({"user": "Alice"}, session=session)
# Commits on success, rolls back on exception
Change Streams
# Native SQLite triggers — no replica set needed
stream = collection.watch()
for change in stream:
print(change)
Journal Mode
from neosqlite import Connection, JournalMode
db = Connection("app.db", journal_mode=JournalMode.WAL) # Default
| Mode | Use Case |
|---|---|
| WAL | Best concurrency (default) |
| DELETE | Single-file distribution |
| MEMORY | Maximum speed, no crash recovery |
Documentation
| Topic | Link |
|---|---|
| Knowledge Base / Docs | documents/ |
| Release Notes | documents/releases/ |
| GridFS | documents/gridfs.md |
| Text Search | documents/text-search.md |
| Aggregation Optimization | documents/aggregation-pipeline-optimization.md |
| NX-27017 Server | packages/nx_27017/README.md |
| API Comparison | examples/api_comparison/README.md |
Contributing
Clone the repository:
git clone https://github.com/cwt/neosqlite.git
cd neosqlite
Create and activate a virtual environment:
python3 -m venv .venv
source .venv/bin/activate
Then run the test script, which installs all required dependencies automatically:
./scripts/runtest.sh
Shell Script Compatibility
All shell scripts in this project target bash 3.2+ for compatibility with macOS, which still ships with bash 3.2.x. Please ensure any contributions to shell scripts remain compatible.
Contribution and License
This project was originally developed as shaunduncan/nosqlite and was later forked as plutec/nosqlite before becoming NeoSQLite. It is now maintained by Chaiwat Suttipongsakul and is licensed under the MIT license.
Contributions are highly encouraged. If you find a bug, have an enhancement in mind, or want to suggest a new feature, please feel free to open an issue or submit a pull request.
Metadata
Release files for neosqlite 1.15.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| neosqlite-1.15.2.tar.gz | 289.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| neosqlite-1.15.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 639.5 kB
Release files / neosqlite-1.15.2.tar.gz
| Download URL | neosqlite-1.15.2.tar.gz |
|---|---|
| Size | 289.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f6ef15cf64f47b4a7ac0b2b2e91286e5106408d98c3df010f4374a9452c34d65
|
|
BLAKE2b-256 checksum How to use checksums |
ddc1bf471dda483230888324904b0e18b5e0bed69975293706e5f86358255f48
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.14.7 Linux/7.1.13-200.fc44.x86_64
|
Release files / neosqlite-1.15.2-py3-none-any.whl
| Download URL | neosqlite-1.15.2-py3-none-any.whl |
|---|---|
| Size | 350.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c9b39c72f17fc1d3fefa8c1454cf5a9925dea1882df326895b52e08921ab6412
|
|
BLAKE2b-256 checksum How to use checksums |
bf07cb7b9c4ec404e478dca124e397c11bf783ae06eb25c06aface72a7690c41
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.4.1 CPython/3.14.7 Linux/7.1.13-200.fc44.x86_64
|