ultraminimalistic Python library for simple object-document mappint for asyncronous mongodb
Project description
uodm: Ultraminimalistic Object-Document Mapper for async mongodb and file storage
Welcome to uodm
, the fun-sized, feature-packed async Object-Document Mapper that supports both MongoDB and file-based storage! Get your documents dancing around faster than you can say "NoSQL nirvana"! 🕺💾
Features
- 🐍 Pythonic to the core: Designed with the grace and grit of modern Python.
- 📚 Asynchronous bliss: Leveraging
asyncio
for non-blocking database operations. - 🎳 Simple and expressive models: Define your models and let uodm handle the rest.
- 🛠️ Auto-index management: Because you've got better things to do than managing indexes manually.
- 🔄 Change streams: Monitor collection changes in real-time with a consistent API across all backends.
- 🌍 Global database access: Connect once, use everywhere (kinda like your Netflix subscription but for databases).
- 🗄️ Multiple storage backends: Choose the right storage for your needs:
- MongoDB: For production-grade applications with scalability requirements
- SQLite: For embedded applications or when you need more robust local storage
- File-based: For development, testing, or simple applications
Quick Start
Here's how you can jump into the action:
MongoDB Storage
import uodm
class Books(uodm.Collection):
title: str
author: str
year: int = uodm.Field(default=2021)
__collection__ = "mybooks"
__indexes__ = [
uodm.Idx("title", unique=True),
uodm.Idx(["title", "author"], sparse=True),
]
async def mongodb_example():
# Connect to MongoDB
db = uodm.UODM("mongodb://localhost:27017/test")
await db.setup()
w = Books(title="War and Peace", author="Tolstoy", year=1869)
try:
await w.save()
except uodm.DuplicateKeyError as e:
print("this book already exists")
res = await Books.get(title="War and Peace")
print(res)
books = await Books.find(year={"$gt": 1900})
for book in books:
print(book)
SQLite Storage
UODM now supports SQLite as a robust local database option that bridges the gap between file-based storage and MongoDB:
async def sqlite_example():
# Connect to SQLite database
# File-based SQLite database
db = uodm.UODM("sqlite:///path/to/database.db")
# Or use in-memory SQLite database for testing
# db = uodm.UODM("sqlite://")
await db.setup()
# Same API as MongoDB and file storage!
w = Books(title="War and Peace", author="Tolstoy", year=1869)
await w.save()
# All MongoDB-style queries work
book = await Books.get(title="War and Peace")
books = await Books.find(year={"$gt": 1900})
# Remember to close connections when done (important for SQLite)
await db.close()
SQLite storage is perfect for:
- Embedded applications requiring a local database
- Applications needing better transactional support than file-based storage
- Higher performance with larger datasets than file-based storage
- Development and testing without MongoDB infrastructure
SQLite data is stored in a single database file, with each collection as a separate table. Indexes are stored both as SQLite indexes (for better query performance) and as metadata for compatibility with the ODM layer.
File-based Storage
UODM supports file-based storage as a drop-in replacement for MongoDB. This is perfect for:
- Development and testing
- Simple applications that don't need a full database
- Offline-first applications
- Data portability scenarios
You can choose from three serialization formats:
- JSON (default) - Human readable, widely compatible
- ORJSON - Faster JSON processing (requires
pip install orjson
) - Pickle - For Python-specific data types
async def file_example():
# Basic file storage with JSON
db = uodm.UODM("file:///path/to/data")
# Or specify a format:
db = uodm.UODM("file:///path/to/data#json") # explicit JSON
db = uodm.UODM("file:///path/to/data#orjson") # faster JSON
db = uodm.UODM("file:///path/to/data#pickle") # Python pickle format
await db.setup()
# Same API as MongoDB!
w = Books(title="1984", author="Orwell", year=1949)
await w.save()
# All MongoDB-style queries work
res = await Books.get(title="1984")
books = await Books.find(year={"$gt": 1900})
count = await Books.count(author="Orwell")
# Sorting and pagination
recent = await Books.find(
sort="-year", # descending sort by year
limit=10, # only 10 results
skip=20 # skip first 20 matches
)
if __name__ == "__main__":
print("\nStarting GT operator test...")
asyncio.run(test_gt_operator())
print("Test completed.\n")
File storage creates a directory structure that mirrors MongoDB:
/path/to/data/
└── default/ # database name
└── books/ # collection name
├── _indexes.json # index definitions
├── 507f1f77bcf86cd799439011.json # document
└── 507f1f77bcf86cd799439012.json # document
Each document is stored in its own JSON file (or pickle/orjson), named by its ID.
Indexes are maintained in a special _indexes.json
file.
Change Streams
UODM now supports change streams across all backends, allowing you to monitor and react to changes in your collections in real-time. MongoDB uses native change streams, while SQLite and file-based backends use a polling-based implementation that mimics the same API.
import asyncio
import uodm
class Books(uodm.Collection):
title: str
author: str
year: int
__collection__ = "books"
async def handle_change(change):
"""React to changes in the books collection"""
op_type = change.operation_type
if op_type == 'insert':
doc = change.full_document
print(f"New book added: '{doc.get('title')}'")
elif op_type == 'update':
doc = change.full_document
print(f"Book updated: '{doc.get('title')}'")
elif op_type == 'delete':
doc_id = change.document_key.get('_id')
print(f"Book deleted: ID {doc_id}")
async def change_stream_example():
# Connect to any supported backend
db = uodm.UODM("file:///path/to/data")
# Or use MongoDB: db = uodm.UODM("mongodb://localhost:27017/test")
# Or use SQLite: db = uodm.UODM("sqlite:///path/to/database.db")
await db.setup()
# Start watching for changes
stream = await Books.watch_changes(handle_change)
# Make some changes
book = Books(title="War and Peace", author="Tolstoy", year=1869)
await book.save()
# Update the book
book.year = 1870
await book.save()
# Delete the book
await book.delete()
# When done, close the change stream
await stream.close()
# Close the database connection
await db.close()
For MongoDB, this uses native change streams. For file and SQLite backends, UODM automatically falls back to a polling-based implementation that provides the same consistent API.
Limitations of File-based Storage
While file-based storage is great for development and simple applications, be aware of these limitations:
-
Performance
- No true indexing support (indexes are simulated)
- Full collection scans for complex queries
- Slower than MongoDB for large datasets
- Memory usage increases with collection size during queries
-
Concurrency
- Basic file locking for writes
- No atomic operations
- No transactions support
- Potential race conditions under heavy load
-
Scalability
- Limited by filesystem performance
- Not suitable for high-concurrency applications
- Directory size limits (depending on filesystem)
- All data must fit on a single machine
-
Query Support
- Limited MongoDB query operators supported
- No aggregation pipeline
- No geospatial queries
- No text search capabilities
Best suited for:
- Development and testing
- Single-user applications
- Small to medium datasets (<100K documents)
- Scenarios where MongoDB setup is overkill
Installation
Grab the latest release with a simple pip command:
pip install git+https://github.com/bobuk/uodm.git#egg=uodm
or just pip install uodm
if you're ok to use the latest release.
Don't forget to wear your seatbelt, this thing is fast! 🚗💨
Contributing
Pull requests? Issues? Existential queries about your database? All are welcome!
- Fork it (like stealing your neighbor's wifi but legal).
- Create your feature branch (git checkout -b my-new-feature).
- Commit your changes (git commit -am 'Add some feature').
- Push to the branch (git push origin my-new-feature).
- Create a new Pull Request.
- ...
- PROFIT
License
This project is under "The Unlicense". Yes, it's really unlicensed. Like a fisherman without a fishing license. 🎣
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
File details
Details for the file uodm-0.4.0.tar.gz
.
File metadata
- Download URL: uodm-0.4.0.tar.gz
- Upload date:
- Size: 28.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.16
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 |
3c90320b377979a1d8a283ca354da9487e4eb3a3c14e9a6597c792eafe3a3d9c
|
|
MD5 |
a125dbdc290ca14d5989fe36785baeb3
|
|
BLAKE2b-256 |
15111bd1f1a9524ee123a72484c6aaaf781b067299447e5eabf32d4cfadeffe4
|
File details
Details for the file uodm-0.4.0-py3-none-any.whl
.
File metadata
- Download URL: uodm-0.4.0-py3-none-any.whl
- Upload date:
- Size: 23.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.16
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 |
89fc741e9cf5ccf29c565e425f95e47a93389faba1c987dcaca99a44306bb3a5
|
|
MD5 |
3185f3d5f8df5503c69c343783843621
|
|
BLAKE2b-256 |
200d6113c98f39874ded1f4e452808aca96edcd6397792a0714946d20cf40cf0
|