mongospec 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mongospec-0.0.1/LICENSE +19 -0
- mongospec-0.0.1/PKG-INFO +88 -0
- mongospec-0.0.1/README.md +68 -0
- mongospec-0.0.1/mongospec/document/__init__.py +1 -0
- mongospec-0.0.1/mongospec/document/document.py +112 -0
- mongospec-0.0.1/mongospec/document/operations/__init__.py +0 -0
- mongospec-0.0.1/mongospec/document/operations/base.py +24 -0
- mongospec-0.0.1/mongospec/document/operations/find.py +20 -0
- mongospec-0.0.1/mongospec/document/operations/insert.py +46 -0
- mongospec-0.0.1/mongospec/mongospec.egg-info/PKG-INFO +88 -0
- mongospec-0.0.1/mongospec/mongospec.egg-info/SOURCES.txt +14 -0
- mongospec-0.0.1/mongospec/mongospec.egg-info/dependency_links.txt +1 -0
- mongospec-0.0.1/mongospec/mongospec.egg-info/requires.txt +2 -0
- mongospec-0.0.1/mongospec/mongospec.egg-info/top_level.txt +1 -0
- mongospec-0.0.1/pyproject.toml +30 -0
- mongospec-0.0.1/setup.cfg +4 -0
mongospec-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2025 Dmitry Belozerov
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
14
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
15
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
16
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
17
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
18
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
|
19
|
+
OR OTHER DEALINGS IN THE SOFTWARE.
|
mongospec-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mongospec
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Async MongoDB ODM with msgspec integration and automatic collection binding
|
|
5
|
+
Author: Diprog
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: repository, https://github.com/diprog/mongospec
|
|
8
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Topic :: Database
|
|
13
|
+
Classifier: Typing :: Typed
|
|
14
|
+
Requires-Python: >=3.13
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: mongojet~=0.3.1
|
|
18
|
+
Requires-Dist: msgspec~=0.19.0
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# mongospec
|
|
22
|
+
|
|
23
|
+
Minimal async MongoDB ODM built for **speed** and **simplicity**, featuring automatic collection binding and msgspec
|
|
24
|
+
integration.
|
|
25
|
+
|
|
26
|
+
## Why mongospec?
|
|
27
|
+
|
|
28
|
+
- ⚡ **Blazing fast** - Uses [msgspec](https://github.com/jcrist/msgspec) (fastest Python serialization)
|
|
29
|
+
and [mongojet](https://github.com/romis2012/mongojet) (fastest async MongoDB wrapper)
|
|
30
|
+
- 🧩 **Dead simple** - No complex abstractions, just clean document handling
|
|
31
|
+
- 🏎️ **Zero overhead** - Optimized for performance-critical applications
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install mongospec
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import asyncio
|
|
43
|
+
from datetime import datetime
|
|
44
|
+
|
|
45
|
+
import mongojet
|
|
46
|
+
import msgspec
|
|
47
|
+
|
|
48
|
+
import mongospec
|
|
49
|
+
from mongospec import MongoDocument
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class User(MongoDocument):
|
|
53
|
+
__collection_name__ = "users"
|
|
54
|
+
__indexes__ = [{"keys": [("email", 1)], "options": {"unique": True}}]
|
|
55
|
+
|
|
56
|
+
name: str
|
|
57
|
+
email: str
|
|
58
|
+
created_at: datetime = msgspec.field(default_factory=datetime.now)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def main():
|
|
62
|
+
mongo_client = await mongojet.create_client("mongodb://localhost:27017")
|
|
63
|
+
try:
|
|
64
|
+
await mongospec.init(mongo_client.get_database("db"), document_types=[User])
|
|
65
|
+
|
|
66
|
+
# Create and insert user
|
|
67
|
+
user = User(name="Alice", email="alice@example.com")
|
|
68
|
+
await user.insert()
|
|
69
|
+
|
|
70
|
+
# Find document
|
|
71
|
+
found = await User.find_one({"email": "alice@example.com"})
|
|
72
|
+
print(found.dump())
|
|
73
|
+
finally:
|
|
74
|
+
await mongospec.close()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
asyncio.run(main())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Performance Features
|
|
81
|
+
|
|
82
|
+
- 🚀 **msgspec-powered** serialization (2-10x faster than alternatives)
|
|
83
|
+
- ⚡ **mongojet backend** (faster than Motor with identical API)
|
|
84
|
+
- 🧬 **Compiled structs** for document models
|
|
85
|
+
- 📉 **Minimal abstraction** overhead
|
|
86
|
+
|
|
87
|
+
⚠️ **Early Alpha Notice**
|
|
88
|
+
Basic CRUD operations supported. Designed for rapid prototyping. Not production-ready. API will evolve.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# mongospec
|
|
2
|
+
|
|
3
|
+
Minimal async MongoDB ODM built for **speed** and **simplicity**, featuring automatic collection binding and msgspec
|
|
4
|
+
integration.
|
|
5
|
+
|
|
6
|
+
## Why mongospec?
|
|
7
|
+
|
|
8
|
+
- ⚡ **Blazing fast** - Uses [msgspec](https://github.com/jcrist/msgspec) (fastest Python serialization)
|
|
9
|
+
and [mongojet](https://github.com/romis2012/mongojet) (fastest async MongoDB wrapper)
|
|
10
|
+
- 🧩 **Dead simple** - No complex abstractions, just clean document handling
|
|
11
|
+
- 🏎️ **Zero overhead** - Optimized for performance-critical applications
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install mongospec
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import asyncio
|
|
23
|
+
from datetime import datetime
|
|
24
|
+
|
|
25
|
+
import mongojet
|
|
26
|
+
import msgspec
|
|
27
|
+
|
|
28
|
+
import mongospec
|
|
29
|
+
from mongospec import MongoDocument
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class User(MongoDocument):
|
|
33
|
+
__collection_name__ = "users"
|
|
34
|
+
__indexes__ = [{"keys": [("email", 1)], "options": {"unique": True}}]
|
|
35
|
+
|
|
36
|
+
name: str
|
|
37
|
+
email: str
|
|
38
|
+
created_at: datetime = msgspec.field(default_factory=datetime.now)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async def main():
|
|
42
|
+
mongo_client = await mongojet.create_client("mongodb://localhost:27017")
|
|
43
|
+
try:
|
|
44
|
+
await mongospec.init(mongo_client.get_database("db"), document_types=[User])
|
|
45
|
+
|
|
46
|
+
# Create and insert user
|
|
47
|
+
user = User(name="Alice", email="alice@example.com")
|
|
48
|
+
await user.insert()
|
|
49
|
+
|
|
50
|
+
# Find document
|
|
51
|
+
found = await User.find_one({"email": "alice@example.com"})
|
|
52
|
+
print(found.dump())
|
|
53
|
+
finally:
|
|
54
|
+
await mongospec.close()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
asyncio.run(main())
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Performance Features
|
|
61
|
+
|
|
62
|
+
- 🚀 **msgspec-powered** serialization (2-10x faster than alternatives)
|
|
63
|
+
- ⚡ **mongojet backend** (faster than Motor with identical API)
|
|
64
|
+
- 🧬 **Compiled structs** for document models
|
|
65
|
+
- 📉 **Minimal abstraction** overhead
|
|
66
|
+
|
|
67
|
+
⚠️ **Early Alpha Notice**
|
|
68
|
+
Basic CRUD operations supported. Designed for rapid prototyping. Not production-ready. API will evolve.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .document import MongoDocument
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base MongoDB document model with automatic collection binding.
|
|
3
|
+
|
|
4
|
+
Provides collection name resolution and runtime collection access.
|
|
5
|
+
Uses class name as fallback when __collection_name__ is not specified.
|
|
6
|
+
"""
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Any, ClassVar, Self
|
|
9
|
+
|
|
10
|
+
import mongojet
|
|
11
|
+
import msgspec
|
|
12
|
+
from bson import ObjectId
|
|
13
|
+
from mongojet import IndexModel
|
|
14
|
+
|
|
15
|
+
from .operations.find import FindOperationsMixin
|
|
16
|
+
from .operations.insert import InsertOperationsMixin
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MongoDocument(
|
|
20
|
+
msgspec.Struct,
|
|
21
|
+
InsertOperationsMixin,
|
|
22
|
+
FindOperationsMixin,
|
|
23
|
+
kw_only=True
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Abstract base document for MongoDB collections with automatic binding.
|
|
27
|
+
|
|
28
|
+
.. rubric:: Class Organization
|
|
29
|
+
|
|
30
|
+
Settings:
|
|
31
|
+
__collection_name__: ClassVar[Optional[str]] = None
|
|
32
|
+
Explicit collection name (optional).
|
|
33
|
+
__preserve_types__: ClassVar[Tuple[Type[Any], ...]] = (ObjectId, datetime)
|
|
34
|
+
Types to preserve in their original form during encoding.
|
|
35
|
+
__indexes__: ClassVar[List[Dict]] = []
|
|
36
|
+
List of MongoDB indexes to ensure on initialization.
|
|
37
|
+
|
|
38
|
+
Runtime:
|
|
39
|
+
__collection__: ClassVar[Optional[mongojet.Collection]] = None
|
|
40
|
+
Set during mongospec.init().
|
|
41
|
+
|
|
42
|
+
Document:
|
|
43
|
+
_id: Optional[ObjectId] = None
|
|
44
|
+
MongoDB document ID field.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Configuration settings
|
|
48
|
+
__collection_name__: ClassVar[str | None] = None
|
|
49
|
+
__preserve_types__: ClassVar[tuple[type[Any], ...]] = (ObjectId, datetime)
|
|
50
|
+
__indexes__: ClassVar[list[IndexModel]] = []
|
|
51
|
+
|
|
52
|
+
# Collection initialized externally
|
|
53
|
+
__collection__: ClassVar[mongojet.Collection | None] = None
|
|
54
|
+
|
|
55
|
+
# Primary key field
|
|
56
|
+
_id: ObjectId | None = None
|
|
57
|
+
|
|
58
|
+
@staticmethod
|
|
59
|
+
def _dec_hook(type_: type[Any], obj: Any) -> Any:
|
|
60
|
+
"""Decode custom types like ObjectId and datetime during conversion."""
|
|
61
|
+
if type_ is ObjectId:
|
|
62
|
+
if isinstance(obj, ObjectId):
|
|
63
|
+
return obj
|
|
64
|
+
try:
|
|
65
|
+
return ObjectId(obj)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
raise ValueError(f"Invalid ObjectId: {obj}") from e
|
|
68
|
+
raise NotImplementedError(f"Unsupported type: {type_}")
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _enc_hook(obj: Any) -> Any:
|
|
72
|
+
"""Encode custom types during serialization. Override in subclasses."""
|
|
73
|
+
raise NotImplementedError(f"Type {type(obj)} not supported")
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def get_collection(cls) -> mongojet.Collection:
|
|
77
|
+
"""Retrieve the bound MongoDB collection."""
|
|
78
|
+
if cls.__collection__ is None:
|
|
79
|
+
raise RuntimeError(
|
|
80
|
+
f"Collection for {cls.__name__} not initialized. "
|
|
81
|
+
"Call mongospec.init() first."
|
|
82
|
+
)
|
|
83
|
+
return cls.__collection__
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def get_collection_name(cls) -> str:
|
|
87
|
+
"""Determine the collection name from class settings."""
|
|
88
|
+
return cls.__collection_name__ or cls.__name__
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def load(cls, data: dict[str, Any]) -> Self:
|
|
92
|
+
"""Deserialize a dictionary into a document instance."""
|
|
93
|
+
return msgspec.convert(
|
|
94
|
+
data,
|
|
95
|
+
cls,
|
|
96
|
+
dec_hook=cls._dec_hook,
|
|
97
|
+
from_attributes=True,
|
|
98
|
+
strict=False
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
102
|
+
"""Serialize the document into a MongoDB-compatible dictionary."""
|
|
103
|
+
data = msgspec.to_builtins(
|
|
104
|
+
self,
|
|
105
|
+
enc_hook=self._enc_hook,
|
|
106
|
+
builtin_types=self.__preserve_types__,
|
|
107
|
+
**kwargs
|
|
108
|
+
)
|
|
109
|
+
# Strip None _id to allow MongoDB to generate it
|
|
110
|
+
if data.get("_id") is None:
|
|
111
|
+
del data["_id"]
|
|
112
|
+
return data
|
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base class for MongoDB document operations.
|
|
3
|
+
Handles collection validation and provides utility methods.
|
|
4
|
+
"""
|
|
5
|
+
from typing import Any, TypeVar
|
|
6
|
+
|
|
7
|
+
TDocument = TypeVar("TDocument", bound="MongoDocument")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BaseOperations:
|
|
11
|
+
"""Base class for MongoDB operations mixins"""
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def _get_collection(cls: type[TDocument]) -> Any:
|
|
15
|
+
"""Get collection with type checking"""
|
|
16
|
+
if not hasattr(cls, "get_collection"):
|
|
17
|
+
raise AttributeError("Document model must implement get_collection()")
|
|
18
|
+
return cls.get_collection()
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def _validate_document_type(cls: type[TDocument], document: Any) -> None:
|
|
22
|
+
"""Ensure document matches collection type"""
|
|
23
|
+
if not isinstance(document, cls):
|
|
24
|
+
raise TypeError(f"Document must be of type {cls.__name__}")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CRUD operations mixin for MongoDocument.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .base import BaseOperations, TDocument
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FindOperationsMixin(BaseOperations):
|
|
9
|
+
"""Mixin class for query operations"""
|
|
10
|
+
|
|
11
|
+
@classmethod
|
|
12
|
+
async def find_one(cls: type[TDocument], filter: dict | None = None) -> TDocument | None:
|
|
13
|
+
"""
|
|
14
|
+
Find single document by query filter
|
|
15
|
+
|
|
16
|
+
:param filter: MongoDB query filter
|
|
17
|
+
:return: Document instance or None
|
|
18
|
+
"""
|
|
19
|
+
doc = await cls._get_collection().find_one(filter or {})
|
|
20
|
+
return cls.load(doc) if doc else None
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from .base import BaseOperations, TDocument
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class InsertOperationsMixin(BaseOperations):
|
|
5
|
+
"""Mixin class for insert operations"""
|
|
6
|
+
|
|
7
|
+
@classmethod
|
|
8
|
+
async def insert_one(cls: type[TDocument], document: TDocument) -> TDocument:
|
|
9
|
+
"""
|
|
10
|
+
Insert single document into collection
|
|
11
|
+
|
|
12
|
+
:param document: Document instance to insert
|
|
13
|
+
:return: Inserted document with _id
|
|
14
|
+
"""
|
|
15
|
+
cls._validate_document_type(document)
|
|
16
|
+
result = await cls._get_collection().insert_one(document.dump())
|
|
17
|
+
document._id = result["inserted_id"]
|
|
18
|
+
return document
|
|
19
|
+
|
|
20
|
+
async def insert(self: TDocument) -> TDocument:
|
|
21
|
+
"""
|
|
22
|
+
Insert the document instance into collection including existing _id
|
|
23
|
+
|
|
24
|
+
:return: Inserted document with _id (newly generated or existing)
|
|
25
|
+
"""
|
|
26
|
+
self._validate_document_type(self)
|
|
27
|
+
result = await self._get_collection().insert_one(self.dump())
|
|
28
|
+
self._id = result["inserted_id"]
|
|
29
|
+
return self
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
async def insert_many(cls: type[TDocument], documents: list[TDocument]) -> list[TDocument]:
|
|
33
|
+
"""
|
|
34
|
+
Batch insert documents
|
|
35
|
+
|
|
36
|
+
:param documents: List of document instances
|
|
37
|
+
:return: List of inserted documents with _ids
|
|
38
|
+
"""
|
|
39
|
+
if not all(isinstance(d, cls) for d in documents):
|
|
40
|
+
raise TypeError("All documents must match collection type")
|
|
41
|
+
|
|
42
|
+
result = await cls._get_collection().insert_many([d.dump() for d in documents])
|
|
43
|
+
|
|
44
|
+
for doc, doc_id in zip(documents, result["inserted_ids"]):
|
|
45
|
+
doc._id = doc_id
|
|
46
|
+
return documents
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mongospec
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Async MongoDB ODM with msgspec integration and automatic collection binding
|
|
5
|
+
Author: Diprog
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: repository, https://github.com/diprog/mongospec
|
|
8
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Topic :: Database
|
|
13
|
+
Classifier: Typing :: Typed
|
|
14
|
+
Requires-Python: >=3.13
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: mongojet~=0.3.1
|
|
18
|
+
Requires-Dist: msgspec~=0.19.0
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# mongospec
|
|
22
|
+
|
|
23
|
+
Minimal async MongoDB ODM built for **speed** and **simplicity**, featuring automatic collection binding and msgspec
|
|
24
|
+
integration.
|
|
25
|
+
|
|
26
|
+
## Why mongospec?
|
|
27
|
+
|
|
28
|
+
- ⚡ **Blazing fast** - Uses [msgspec](https://github.com/jcrist/msgspec) (fastest Python serialization)
|
|
29
|
+
and [mongojet](https://github.com/romis2012/mongojet) (fastest async MongoDB wrapper)
|
|
30
|
+
- 🧩 **Dead simple** - No complex abstractions, just clean document handling
|
|
31
|
+
- 🏎️ **Zero overhead** - Optimized for performance-critical applications
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install mongospec
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import asyncio
|
|
43
|
+
from datetime import datetime
|
|
44
|
+
|
|
45
|
+
import mongojet
|
|
46
|
+
import msgspec
|
|
47
|
+
|
|
48
|
+
import mongospec
|
|
49
|
+
from mongospec import MongoDocument
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class User(MongoDocument):
|
|
53
|
+
__collection_name__ = "users"
|
|
54
|
+
__indexes__ = [{"keys": [("email", 1)], "options": {"unique": True}}]
|
|
55
|
+
|
|
56
|
+
name: str
|
|
57
|
+
email: str
|
|
58
|
+
created_at: datetime = msgspec.field(default_factory=datetime.now)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def main():
|
|
62
|
+
mongo_client = await mongojet.create_client("mongodb://localhost:27017")
|
|
63
|
+
try:
|
|
64
|
+
await mongospec.init(mongo_client.get_database("db"), document_types=[User])
|
|
65
|
+
|
|
66
|
+
# Create and insert user
|
|
67
|
+
user = User(name="Alice", email="alice@example.com")
|
|
68
|
+
await user.insert()
|
|
69
|
+
|
|
70
|
+
# Find document
|
|
71
|
+
found = await User.find_one({"email": "alice@example.com"})
|
|
72
|
+
print(found.dump())
|
|
73
|
+
finally:
|
|
74
|
+
await mongospec.close()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
asyncio.run(main())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Performance Features
|
|
81
|
+
|
|
82
|
+
- 🚀 **msgspec-powered** serialization (2-10x faster than alternatives)
|
|
83
|
+
- ⚡ **mongojet backend** (faster than Motor with identical API)
|
|
84
|
+
- 🧬 **Compiled structs** for document models
|
|
85
|
+
- 📉 **Minimal abstraction** overhead
|
|
86
|
+
|
|
87
|
+
⚠️ **Early Alpha Notice**
|
|
88
|
+
Basic CRUD operations supported. Designed for rapid prototyping. Not production-ready. API will evolve.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
mongospec/document/__init__.py
|
|
5
|
+
mongospec/document/document.py
|
|
6
|
+
mongospec/document/operations/__init__.py
|
|
7
|
+
mongospec/document/operations/base.py
|
|
8
|
+
mongospec/document/operations/find.py
|
|
9
|
+
mongospec/document/operations/insert.py
|
|
10
|
+
mongospec/mongospec.egg-info/PKG-INFO
|
|
11
|
+
mongospec/mongospec.egg-info/SOURCES.txt
|
|
12
|
+
mongospec/mongospec.egg-info/dependency_links.txt
|
|
13
|
+
mongospec/mongospec.egg-info/requires.txt
|
|
14
|
+
mongospec/mongospec.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
document
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "mongospec"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
authors = [{name = "Diprog"}]
|
|
5
|
+
description = "Async MongoDB ODM with msgspec integration and automatic collection binding"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.13"
|
|
8
|
+
license = {text = "MIT"}
|
|
9
|
+
dependencies = [
|
|
10
|
+
"mongojet~=0.3.1",
|
|
11
|
+
"msgspec~=0.19.0",
|
|
12
|
+
]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Topic :: Database",
|
|
19
|
+
"Typing :: Typed"
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
repository = "https://github.com/diprog/mongospec"
|
|
24
|
+
|
|
25
|
+
[build-system]
|
|
26
|
+
requires = ["setuptools"]
|
|
27
|
+
build-backend = "setuptools.build_meta"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["mongospec"]
|