Skip to main content

A lightweight NoSQL-like database in Python

Project description

nosqlpy - Async, NoSQL, MQL-style embedded document store for Python

nosqlpy logo

⚠️ Warning: This is the alpha version

nosqlpy is a lightweight, async-first, file-backed document database for Python that speaks a MongoDB-like Query Language (MQL). It’s designed for small-to-medium apps, dev tooling, prototyping, CLI tools, and apps that want a simple embedded DB with async/await, durability options, indexes, and a clean API - without running a separate database server.

  • ✅ Async-first
  • ✅ Mongo-like queries
  • ✅ Append-only op-log
  • ✅ Secondary equality indexes
  • ✅ Compaction & durability modes

Table of contents


Why nosqlpy?

Many small Python apps need an embedded document store that:

  • Is async-native (fits FastAPI / aiohttp / asyncio apps)
  • Uses a familiar query language (MongoDB-style filters)
  • Provides durable writes and safe recovery
  • Enables fast reads using secondary indexes

Most small DB options are either synchronous (TinyDB), or require wrapping sync code into thread pools. nosqlpy is built async-first and offers MQL-style queries, an append-only op-log for safe durability, optional indexes, and compaction for production-ish workloads - all in a single small dependency set.


Key features

  • Async API (async/await) - designed for asyncio apps
  • MongoDB-style query language (subset): $and, $or, $not, $gt, $gte, $lt, $lte, $in, $nin, $exists, simple dot-notation for nested fields
  • Update operators: $set, $unset, $inc, $push, $pull
  • Append-only operation log (oplog) with replayable history
  • Durability modes: fast, safe, durable (fsync)
  • Secondary equality indexes (create and use for fast field == value)
  • Compaction (background-friendly pattern) to shrink logs and produce snapshots
  • Bulk ops: insert_many, update_many, delete_many
  • find support: projection, sort, skip, limit, find_one
  • TinyDB migration helper / compatibility shim (to ease switching)

Installation

# install from PyPI (when published)
pip install nosqlpy

# or during development (from repo)
git clone https://github.com/viehoang/nosqlpy.git
cd nosqlpy
pip install -e ".[dev]"

Dependencies: aiofiles (async file I/O). Dev/test extras include pytest, pytest-asyncio.


Quickstart

import asyncio
from nosqlpy import Database

async def main():
    db = Database("data_dir", durability="safe")
    users = await db.collection("users")

    # insert
    uid = await users.insert_one({
        "name": "alice", 
        "age": 30})
        
    print("inserted id:", uid)

    # find (Mongo-like query)
    results = await users.find(
        {"age": {"$gte": 25}, 
        "name": "alice"})
    print("found:", results)

    # update operators
    await users.update_many(
        {"name": "alice"}, 
        {"$inc": {"age": 1}, 
        "$push": {"tags": "admin"}})

    # create index on email for fast equality lookups
    await users.create_index("email")

    # compact (shrink the op-log)
    await users.compact()

asyncio.run(main())

Core concepts & API reference

Collection - a single named dataset backed by an append-oplog file.

Document - a JSON-like dict with an _id field (string) as the primary key.

Op-log - append-only lines describing insert, update, replace, delete operations.

All calls are async.

Database & Collection

from nosqlpy import Database
db = Database("my_data_dir", durability="safe")
users = await db.collection("users")

Common operations

List of operations

Operator Description
open Asynchronously open and load the collection.
insert_one Insert a single document into the collection.
insert_many Insert multiple documents into the collection.
find Find documents matching the query, with options for projection, sort, skip, and limit.
find_one Find a single document matching the query.
count Count the number of documents matching the query.
delete_one Delete a single document matching the query.
delete_many Delete multiple documents matching the query.
update_one Update a single document matching the query, with optional upsert.
update_many Update multiple documents matching the query, with optional upsert (upserts one if no matches).
replace_one Replace a single document matching the query, with optional upsert.
create_index Create a secondary hash index on a field.
drop_index Drop the index on a field.
compact Compact the op-log by replacing it with current state as inserts.

Examples

# insert one
_id = await users.insert_one({"name": "Bob", "age": 22})

# insert many
ids = await users.insert_many([{"name":"A"},{"name":"B"}])

# find (MQL filter)
docs = await users.find(
    {"age": {"$gte": 18}}, 
    projection=["name","age"], 
    sort=[("age", -1)], 
    skip=0, 
    limit=50)

# find one
doc = await users.find_one({"name": "Bob"})

# count
n = await users.count({"age": {"$gte": 30}})

# update many (supports $set/$inc/$push/$pull)
updated = await users.update_many(
    {"name": "Bob"}, 
    {"$inc": {"age": 1}})

# replace one
ok = await users.replace_one(
    {"name": "Bob"}, 
    {"name": "Robert", "age": 23})

# delete many
deleted = await users.delete_many({"age": {"$lt": 18}})

# compact (rewrite op-log as current-state snapshot)
await users.compact()

Query language (MQL subset)

Supported query operators (subset):

  • Comparison: $eq (or plain value), $ne, $gt, $gte, $lt, $lte
  • Membership: $in, $nin
  • Existence: $exists
  • Logical: $and, $or, $not
  • Dot-notation: "user.age" for nested fields

Examples:

# age >= 30 AND (country == "US" OR country == "JP")
q = {"$and": [{"age": {"$gte": 30}}, {"$or": [{"country": "US"}, {"country": "JP"}]}]}

# membership
q2 = {"status": {"$in": ["active", "pending"]}}

# nested field
q3 = {"profile.email": {"$exists": True}}

Update operators

Supported update operators:

  • $set: set field(s) to value
  • $unset: remove field(s)
  • $inc: increment numeric field
  • $push: append to array field
  • $pull: remove value(s) from array field

Example:

await users.update_many(
    {"_id": some_id}, 
    {
        "$set": {"name": "Alice"}, 
        "$inc": {"score": 10}
    })

Indexing

Equality secondary index (hash-based) to accelerate queries like { "email": "a@x.com" }:

await users.create_index("email")

Notes:

  • Indexes are in-memory by default and rebuilt on create; consider snapshotting indexes for very large DBs (TODO/roadmap).
  • Planner currently optimizes single-field equality queries; range indexes are a planned feature.

Compaction & durability

  • Op-log (append-only) is crash friendly: each write is a single line append.

  • Durability modes:

    • fast: minimal overhead, no explicit flush (best throughput, less durable)
    • safe: flush after write (file.flush()) - good compromise
    • durable: fsync() after each op (strong durability, slower)

Compaction rewrites the current state as a compact snapshot (series of insert ops) and atomically replaces the op-log. For large DBs we recommend:

  • Run compact() occasionally (or use background segmented compaction; see Roadmap).
  • Use durable for critical writes, safe for normal persistence.

Performance tips

  • Use indexes for frequent equality lookups.
  • Use durability="safe" for most apps; switch to durable only if you need fsync-level guarantees on every op.
  • For very large data (>100k docs), enable segmented compaction or index snapshots (planned).

License

nosqlpy is released under the MIT License. See LICENSE for details.


Keywords

nosqlpy, nosqlite, nosql lite, async nosql, python async database, embedded document store, mongo query language python, mql python, asyncio database, append log database, python nosql lite, tinydb alternative, aiosqlite alternative, lightweight mongodb, file-backed document store

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nosqlpy-0.1.1.tar.gz (5.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nosqlpy-0.1.1-py3-none-any.whl (4.9 kB view details)

Uploaded Python 3

File details

Details for the file nosqlpy-0.1.1.tar.gz.

File metadata

  • Download URL: nosqlpy-0.1.1.tar.gz
  • Upload date:
  • Size: 5.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for nosqlpy-0.1.1.tar.gz
Algorithm Hash digest
SHA256 df7a5736e8b9f4062d9553600f011bf89eace34c3d7ab69b1b8d9985de45ca58
MD5 6b3117232d4fc482f8b05bf55a80a426
BLAKE2b-256 edb5f3cd5b272c680bb51133263d08ce2c69ce1f80f8ae905abde786445f31bd

See more details on using hashes here.

File details

Details for the file nosqlpy-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: nosqlpy-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 4.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for nosqlpy-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 67af607379277bb85c7ef75c53400a7cbabff2466095fa44dfe1d378a1345fcb
MD5 e03322db6e05ac8ff140bed808db3460
BLAKE2b-256 598810345ad138246ee23e5aa3c819f812dd05142ea3617bc59448a7dceb46bf

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page