<p align="center">
<img src="assets/logo.svg" width="35%" alt="mongospec"/>
</p>
[](https://pypi.org/project/mongospec/)
[](https://www.python.org/downloads/)
[](https://opensource.org/licenses/MIT)
Minimal **async** MongoDB ODM built for *speed* and *simplicity*, featuring automatic collection binding,
[msgspec](https://github.com/jcrist/msgspec) integration, and first-class asyncio support.
---
## Table of Contents
1. [Installation](#installation)
2. [Quick Start](#quick-start)
3. [Examples](#examples)
4. [Key Features](#key-features)
5. [Core Concepts](#core-concepts)
- [Document Models](#document-models)
- [Connection Management](#connection-management)
- [Collection Binding](#collection-binding)
- [CRUD Operations](#crud-operations)
- [Indexes](#indexes)
6. [Contributing](#contributing)
7. [License](#license)
---
## Installation
```bash
pip install mongospec
```
Requires **Python 3.13+** and a running MongoDB 6.0+ server.
---
## Quick Start
```python
import asyncio
from datetime import datetime
from typing import ClassVar, Sequence
import mongojet
import msgspec
import mongospec
from mongospec import MongoDocument
from mongojet import IndexModel
class User(MongoDocument):
__collection_name__ = "users"
__indexes__: ClassVar[Sequence[IndexModel]] = [
IndexModel(keys=[("email", 1)], options={"unique": True})
]
name: str
email: str
created_at: datetime = msgspec.field(default_factory=datetime.now)
async def main() -> None:
client = await mongojet.create_client("mongodb://localhost:27017")
await mongospec.init(client.get_database("example_db"), document_types=[User])
user = User(name="Alice", email="alice@example.com")
await user.insert()
print("Inserted:", user)
fetched = await User.find_one({"email": "alice@example.com"})
print("Fetched:", fetched)
await fetched.delete()
await mongospec.close()
if __name__ == "__main__":
asyncio.run(main())
```
---
## Examples
All other usage examples have been moved to standalone scripts in the
[`examples/`](./examples) directory.
Each file is self-contained and can be executed directly:
| Script | What it covers |
|----------------------------|----------------------------------------------|
| `quick_start.py` | End-to-end “hello world” |
| `document_models.py` | Defining typed models & indexes |
| `connection_management.py` | Initialising the ODM and binding collections |
| `collection_binding.py` | Using models immediately after init |
| `index_creation.py` | Unique, compound & text indexes |
| `create_documents.py` | Single & bulk inserts, conditional insert |
| `read_documents.py` | Queries, cursors, projections |
| `update_documents.py` | Field updates, atomic & versioned updates |
| `delete_documents.py` | Single & batch deletes |
| `count_documents.py` | Fast counts & estimated counts |
| `working_with_cursors.py` | Batch processing large result sets |
| `batch_operations.py` | Bulk insert / update / delete |
| `atomic_updates.py` | Optimistic-locking with version field |
| `upsert_operations.py` | Upsert via `save` and `update_one` |
| `projection_example.py` | Field selection for performance |
---
## Key Features
* **Zero-boilerplate models** – automatic collection resolution & binding.
* **Async first** – built on `mongojet`, fully `await`-able API.
* **Typed & fast** – data classes powered by `msgspec` for
ultra-fast (de)serialization.
* **Declarative indexes** – define indexes right on the model with
familiar `pymongo`/`mongojet` `IndexModel`s.
* **Batteries included** – helpers for common CRUD patterns, bulk and
atomic operations, cursors, projections, upserts and more.
---
## Core Concepts
### Document Models
Define your schema by subclassing **`MongoDocument`**
and adding typed attributes.
See **[`examples/document_models.py`](./examples/document_models.py)**.
### Connection Management
Initialise once with `mongospec.init(...)`, passing a
`mongojet.Database` and the list of models to bind.
See **[`examples/connection_management.py`](./examples/connection_management.py)**.
### Collection Binding
After initialisation every model knows its collection and can be used
immediately – no manual wiring required.
See **[`examples/collection_binding.py`](./examples/collection_binding.py)**.
### CRUD Operations
The `MongoDocument` class (and its mixins) exposes a rich async CRUD API:
`insert`, `find`, `update`, `delete`, `count`, cursors, bulk helpers,
atomic `find_one_and_update`, upserts, etc.
See scripts in `examples/` grouped by operation type.
### Indexes
Declare indexes in `__indexes__` as a `Sequence[IndexModel]`
(unique, compound, text, …).
Indexes are created automatically at init time.
See **[`examples/index_creation.py`](./examples/index_creation.py)**.
### Automatic Discovery of Document Models
In addition to manually listing document classes when calling `mongospec.init(...)`, you can use the utility function `collect_document_types(...)` to automatically discover all models in a package:
```python
from mongospec.utils import collect_document_types
document_types = collect_document_types("myapp.db.models")
await mongospec.init(db, document_types=document_types)
```
This function supports:
* Recursive import of all submodules in the target package
* Filtering by base class (default: `MongoDocument`)
* Optional exclusion of abstract or re-exported classes
* Regex or callable-based module filtering
* Graceful handling of import errors
**Usage Example:**
```python
from mongospec.utils import collect_document_types
# Collect all document models in `myapp.db.models` and its submodules
models = collect_document_types(
"myapp.db.models",
ignore_abstract=True,
local_only=True,
on_error="warn",
)
await mongospec.init(db, document_types=models)
```
**Advanced options include:**
* `predicate=...` to filter only specific model types
* `return_map=True` to get a `{qualified_name: class}` dict
* `module_filter=".*models.*"` to restrict traversal
See the full function signature in [`mongospec/utils.py`](./mongospec/utils.py).
Raw data
{
"_id": null,
"home_page": null,
"name": "mongospec",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.13",
"maintainer_email": null,
"keywords": "mongodb, odm, async, database, asyncio, msgspec, mongojet",
"author": null,
"author_email": "Diprog <diprog991@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/1a/45/274ea5082d1487996bf38c5973bdbbb70b289cd60f96329f03d24a27d538/mongospec-0.1.5.tar.gz",
"platform": null,
"description": "<p align=\"center\">\r\n <img src=\"assets/logo.svg\" width=\"35%\" alt=\"mongospec\"/>\r\n</p>\r\n\r\n[](https://pypi.org/project/mongospec/)\r\n[](https://www.python.org/downloads/)\r\n[](https://opensource.org/licenses/MIT)\r\n\r\nMinimal **async** MongoDB ODM built for *speed* and *simplicity*, featuring automatic collection binding,\r\n[msgspec](https://github.com/jcrist/msgspec) integration, and first-class asyncio support.\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n1. [Installation](#installation) \r\n2. [Quick Start](#quick-start) \r\n3. [Examples](#examples) \r\n4. [Key Features](#key-features) \r\n5. [Core Concepts](#core-concepts) \r\n - [Document Models](#document-models) \r\n - [Connection Management](#connection-management) \r\n - [Collection Binding](#collection-binding) \r\n - [CRUD Operations](#crud-operations) \r\n - [Indexes](#indexes) \r\n6. [Contributing](#contributing) \r\n7. [License](#license)\r\n\r\n---\r\n\r\n## Installation\r\n\r\n```bash\r\npip install mongospec\r\n```\r\n\r\nRequires **Python 3.13+** and a running MongoDB 6.0+ server.\r\n\r\n---\r\n\r\n## Quick Start\r\n\r\n```python\r\nimport asyncio\r\nfrom datetime import datetime\r\nfrom typing import ClassVar, Sequence\r\n\r\nimport mongojet\r\nimport msgspec\r\n\r\nimport mongospec\r\nfrom mongospec import MongoDocument\r\nfrom mongojet import IndexModel\r\n\r\n\r\nclass User(MongoDocument):\r\n __collection_name__ = \"users\"\r\n __indexes__: ClassVar[Sequence[IndexModel]] = [\r\n IndexModel(keys=[(\"email\", 1)], options={\"unique\": True})\r\n ]\r\n\r\n name: str\r\n email: str\r\n created_at: datetime = msgspec.field(default_factory=datetime.now)\r\n\r\n\r\nasync def main() -> None:\r\n client = await mongojet.create_client(\"mongodb://localhost:27017\")\r\n await mongospec.init(client.get_database(\"example_db\"), document_types=[User])\r\n\r\n user = User(name=\"Alice\", email=\"alice@example.com\")\r\n await user.insert()\r\n print(\"Inserted:\", user)\r\n\r\n fetched = await User.find_one({\"email\": \"alice@example.com\"})\r\n print(\"Fetched:\", fetched)\r\n\r\n await fetched.delete()\r\n await mongospec.close()\r\n\r\n\r\nif __name__ == \"__main__\":\r\n asyncio.run(main())\r\n```\r\n\r\n---\r\n\r\n## Examples\r\n\r\nAll other usage examples have been moved to standalone scripts in the\r\n[`examples/`](./examples) directory.\r\nEach file is self-contained and can be executed directly:\r\n\r\n| Script | What it covers |\r\n|----------------------------|----------------------------------------------|\r\n| `quick_start.py` | End-to-end \u201chello world\u201d |\r\n| `document_models.py` | Defining typed models & indexes |\r\n| `connection_management.py` | Initialising the ODM and binding collections |\r\n| `collection_binding.py` | Using models immediately after init |\r\n| `index_creation.py` | Unique, compound & text indexes |\r\n| `create_documents.py` | Single & bulk inserts, conditional insert |\r\n| `read_documents.py` | Queries, cursors, projections |\r\n| `update_documents.py` | Field updates, atomic & versioned updates |\r\n| `delete_documents.py` | Single & batch deletes |\r\n| `count_documents.py` | Fast counts & estimated counts |\r\n| `working_with_cursors.py` | Batch processing large result sets |\r\n| `batch_operations.py` | Bulk insert / update / delete |\r\n| `atomic_updates.py` | Optimistic-locking with version field |\r\n| `upsert_operations.py` | Upsert via `save` and `update_one` |\r\n| `projection_example.py` | Field selection for performance |\r\n\r\n---\r\n\r\n## Key Features\r\n\r\n* **Zero-boilerplate models** \u2013 automatic collection resolution & binding.\r\n* **Async first** \u2013 built on `mongojet`, fully `await`-able API.\r\n* **Typed & fast** \u2013 data classes powered by `msgspec` for\r\n ultra-fast (de)serialization.\r\n* **Declarative indexes** \u2013 define indexes right on the model with\r\n familiar `pymongo`/`mongojet` `IndexModel`s.\r\n* **Batteries included** \u2013 helpers for common CRUD patterns, bulk and\r\n atomic operations, cursors, projections, upserts and more.\r\n\r\n---\r\n\r\n## Core Concepts\r\n\r\n### Document Models\r\n\r\nDefine your schema by subclassing **`MongoDocument`**\r\nand adding typed attributes.\r\nSee **[`examples/document_models.py`](./examples/document_models.py)**.\r\n\r\n### Connection Management\r\n\r\nInitialise once with `mongospec.init(...)`, passing a\r\n`mongojet.Database` and the list of models to bind.\r\nSee **[`examples/connection_management.py`](./examples/connection_management.py)**.\r\n\r\n### Collection Binding\r\n\r\nAfter initialisation every model knows its collection and can be used\r\nimmediately \u2013 no manual wiring required.\r\nSee **[`examples/collection_binding.py`](./examples/collection_binding.py)**.\r\n\r\n### CRUD Operations\r\n\r\nThe `MongoDocument` class (and its mixins) exposes a rich async CRUD API:\r\n`insert`, `find`, `update`, `delete`, `count`, cursors, bulk helpers,\r\natomic `find_one_and_update`, upserts, etc.\r\nSee scripts in `examples/` grouped by operation type.\r\n\r\n### Indexes\r\n\r\nDeclare indexes in `__indexes__` as a `Sequence[IndexModel]`\r\n(unique, compound, text, \u2026).\r\nIndexes are created automatically at init time.\r\nSee **[`examples/index_creation.py`](./examples/index_creation.py)**.\r\n\r\n### Automatic Discovery of Document Models\r\n\r\nIn addition to manually listing document classes when calling `mongospec.init(...)`, you can use the utility function `collect_document_types(...)` to automatically discover all models in a package:\r\n\r\n```python\r\nfrom mongospec.utils import collect_document_types\r\n\r\ndocument_types = collect_document_types(\"myapp.db.models\")\r\nawait mongospec.init(db, document_types=document_types)\r\n\r\n```\r\n\r\nThis function supports:\r\n\r\n* Recursive import of all submodules in the target package\r\n* Filtering by base class (default: `MongoDocument`)\r\n* Optional exclusion of abstract or re-exported classes\r\n* Regex or callable-based module filtering\r\n* Graceful handling of import errors\r\n\r\n**Usage Example:**\r\n\r\n```python\r\nfrom mongospec.utils import collect_document_types\r\n\r\n# Collect all document models in `myapp.db.models` and its submodules\r\nmodels = collect_document_types(\r\n \"myapp.db.models\",\r\n ignore_abstract=True,\r\n local_only=True,\r\n on_error=\"warn\",\r\n)\r\n\r\nawait mongospec.init(db, document_types=models)\r\n```\r\n\r\n**Advanced options include:**\r\n\r\n* `predicate=...` to filter only specific model types\r\n* `return_map=True` to get a `{qualified_name: class}` dict\r\n* `module_filter=\".*models.*\"` to restrict traversal\r\n\r\nSee the full function signature in [`mongospec/utils.py`](./mongospec/utils.py).\r\n",
"bugtrack_url": null,
"license": null,
"summary": "Async MongoDB ODM with msgspec integration and automatic collection binding",
"version": "0.1.5",
"project_urls": {
"Bug Tracker": "https://github.com/diprog/mongospec/issues",
"Documentation": "https://github.com/diprog/mongospec#readme",
"Repository": "https://github.com/diprog/mongospec"
},
"split_keywords": [
"mongodb",
" odm",
" async",
" database",
" asyncio",
" msgspec",
" mongojet"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "7fd127a9094790d4277ef122f620c466880fb1e88382fbbfaa9d949684ed8d2e",
"md5": "5e5859d408d325a975c6f5380cefaaae",
"sha256": "2b740ec5f4ce169778d03eaf7f5b1d3353312bd74451db14639d8e332a40a2eb"
},
"downloads": -1,
"filename": "mongospec-0.1.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5e5859d408d325a975c6f5380cefaaae",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.13",
"size": 32755,
"upload_time": "2025-08-02T15:53:39",
"upload_time_iso_8601": "2025-08-02T15:53:39.635178Z",
"url": "https://files.pythonhosted.org/packages/7f/d1/27a9094790d4277ef122f620c466880fb1e88382fbbfaa9d949684ed8d2e/mongospec-0.1.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "1a45274ea5082d1487996bf38c5973bdbbb70b289cd60f96329f03d24a27d538",
"md5": "2762618e4e278921383665246e914093",
"sha256": "e4fe1f7ed1162a79a739d96b92c95772660dbd7b2337a1f29b0564a7355e1bf2"
},
"downloads": -1,
"filename": "mongospec-0.1.5.tar.gz",
"has_sig": false,
"md5_digest": "2762618e4e278921383665246e914093",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.13",
"size": 22852,
"upload_time": "2025-08-02T15:53:40",
"upload_time_iso_8601": "2025-08-02T15:53:40.958860Z",
"url": "https://files.pythonhosted.org/packages/1a/45/274ea5082d1487996bf38c5973bdbbb70b289cd60f96329f03d24a27d538/mongospec-0.1.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-02 15:53:40",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "diprog",
"github_project": "mongospec",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "mongospec"
}