mindtrace-database 0.2.0__tar.gz → 0.4.0__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.
- mindtrace_database-0.4.0/PKG-INFO +623 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/README.md +18 -17
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace/database/__init__.py +1 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace/database/backends/mongo_odm_backend.py +1 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace/database/backends/redis_odm_backend.py +4 -3
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace/database/backends/unified_odm_backend.py +1 -0
- mindtrace_database-0.4.0/mindtrace_database.egg-info/PKG-INFO +623 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/pyproject.toml +2 -1
- mindtrace_database-0.2.0/PKG-INFO +0 -17
- mindtrace_database-0.2.0/mindtrace_database.egg-info/PKG-INFO +0 -17
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/LICENSE +0 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace/database/backends/local_odm_backend.py +0 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace/database/backends/mindtrace_odm_backend.py +0 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace/database/core/exceptions.py +0 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace_database.egg-info/SOURCES.txt +0 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace_database.egg-info/dependency_links.txt +0 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace_database.egg-info/requires.txt +0 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/mindtrace_database.egg-info/top_level.txt +0 -0
- {mindtrace_database-0.2.0 → mindtrace_database-0.4.0}/setup.cfg +0 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mindtrace-database
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Database functionality for Mindtrace
|
|
5
|
+
Author: Mindtrace Team
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://mindtrace.ai
|
|
8
|
+
Project-URL: Repository, https://github.com/mindtrace/mindtrace/blob/main/mindtrace/database
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: beanie<2,>=1.29.0
|
|
14
|
+
Requires-Dist: mindtrace-core
|
|
15
|
+
Requires-Dist: redis>=4.0.0
|
|
16
|
+
Requires-Dist: redis-om>=0.3.5
|
|
17
|
+
Requires-Dist: motor>=3.3.0
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
[](https://pypi.org/project/mindtrace-database/)
|
|
21
|
+
[](https://github.com/mindtrace/mindtrace/blob/main/mindtrace/database/LICENSE)
|
|
22
|
+
[](https://pepy.tech/projects/mindtrace-database)
|
|
23
|
+
|
|
24
|
+
# Mindtrace Database Module
|
|
25
|
+
|
|
26
|
+
A powerful, flexible Object-Document Mapping (ODM) system that provides a **unified interface** for working with multiple database backends in the Mindtrace project. Write once, run on MongoDB, Redis, or both!
|
|
27
|
+
|
|
28
|
+
## Key Features
|
|
29
|
+
|
|
30
|
+
- **Unified Backend System** - One interface for multiple databases
|
|
31
|
+
- **Dynamic Backend Switching** - Switch between MongoDB and Redis at runtime
|
|
32
|
+
- **Simplified Document Models** - Define once, use everywhere
|
|
33
|
+
- **Async/Sync Support** - Choose your preferred programming style
|
|
34
|
+
- **Advanced Querying** - Rich query capabilities across all backends
|
|
35
|
+
- **Comprehensive Error Handling** - Clear, actionable error messages
|
|
36
|
+
- **Full Test Coverage** - Thoroughly tested with unit and integration tests
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### The Simple Way: Unified Documents
|
|
41
|
+
|
|
42
|
+
Define your document model once and use it with any backend:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from mindtrace.database import UnifiedMindtraceDocument, UnifiedMindtraceODMBackend, BackendType
|
|
46
|
+
from pydantic import Field
|
|
47
|
+
|
|
48
|
+
# 1. Define your document model (works with both MongoDB and Redis!)
|
|
49
|
+
class User(UnifiedMindtraceDocument):
|
|
50
|
+
name: str = Field(description="User's full name")
|
|
51
|
+
age: int = Field(ge=0, description="User's age")
|
|
52
|
+
email: str = Field(description="User's email address")
|
|
53
|
+
skills: list[str] = Field(default_factory=list)
|
|
54
|
+
|
|
55
|
+
class Meta:
|
|
56
|
+
collection_name = "users"
|
|
57
|
+
global_key_prefix = "myapp"
|
|
58
|
+
indexed_fields = ["email", "name"]
|
|
59
|
+
unique_fields = ["email"]
|
|
60
|
+
|
|
61
|
+
# 2. Create backend (supports both MongoDB and Redis)
|
|
62
|
+
backend = UnifiedMindtraceODMBackend(
|
|
63
|
+
unified_model_cls=User,
|
|
64
|
+
mongo_db_uri="mongodb://localhost:27017",
|
|
65
|
+
mongo_db_name="myapp",
|
|
66
|
+
redis_url="redis://localhost:6379",
|
|
67
|
+
preferred_backend=BackendType.MONGO # Start with MongoDB
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# 3. Initialize
|
|
71
|
+
await backend.initialize_async() # For async operations
|
|
72
|
+
# or
|
|
73
|
+
backend.initialize_sync() # For sync operations
|
|
74
|
+
|
|
75
|
+
# 4. Use it! (Same API regardless of backend)
|
|
76
|
+
user = User(name="Alice", age=30, email="alice@example.com", skills=["Python"])
|
|
77
|
+
|
|
78
|
+
# Insert
|
|
79
|
+
inserted_user = await backend.insert_async(user)
|
|
80
|
+
# or: inserted_user = backend.insert(user)
|
|
81
|
+
|
|
82
|
+
# Get by ID
|
|
83
|
+
retrieved_user = await backend.get_async(inserted_user.id)
|
|
84
|
+
|
|
85
|
+
# Find with filters
|
|
86
|
+
python_users = await backend.find_async({"skills": "Python"})
|
|
87
|
+
|
|
88
|
+
# Switch backends on the fly!
|
|
89
|
+
backend.switch_backend(BackendType.REDIS)
|
|
90
|
+
redis_user = backend.insert(user) # Now using Redis
|
|
91
|
+
|
|
92
|
+
# Get all users
|
|
93
|
+
all_users = await backend.all_async()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Traditional Way: Backend-Specific Models
|
|
97
|
+
|
|
98
|
+
If you prefer more control, you can still define backend-specific models:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from mindtrace.database import (
|
|
102
|
+
MongoMindtraceODMBackend,
|
|
103
|
+
RedisMindtraceODMBackend,
|
|
104
|
+
MindtraceDocument,
|
|
105
|
+
MindtraceRedisDocument
|
|
106
|
+
)
|
|
107
|
+
from beanie import Indexed
|
|
108
|
+
from redis_om import Field as RedisField
|
|
109
|
+
from typing import Annotated
|
|
110
|
+
|
|
111
|
+
# MongoDB model
|
|
112
|
+
class MongoUser(MindtraceDocument):
|
|
113
|
+
name: str
|
|
114
|
+
email: Annotated[str, Indexed(unique=True)]
|
|
115
|
+
age: int
|
|
116
|
+
|
|
117
|
+
class Settings:
|
|
118
|
+
name = "users"
|
|
119
|
+
|
|
120
|
+
# Redis model
|
|
121
|
+
class RedisUser(MindtraceRedisDocument):
|
|
122
|
+
name: str = RedisField(index=True)
|
|
123
|
+
email: str = RedisField(index=True)
|
|
124
|
+
age: int = RedisField(index=True)
|
|
125
|
+
|
|
126
|
+
class Meta:
|
|
127
|
+
global_key_prefix = "myapp"
|
|
128
|
+
|
|
129
|
+
# Use them separately
|
|
130
|
+
mongo_backend = MongoMindtraceODMBackend(
|
|
131
|
+
model_cls=MongoUser,
|
|
132
|
+
db_uri="mongodb://localhost:27017",
|
|
133
|
+
db_name="myapp"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
redis_backend = RedisMindtraceODMBackend(
|
|
137
|
+
model_cls=RedisUser,
|
|
138
|
+
redis_url="redis://localhost:6379"
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Available Backends
|
|
143
|
+
|
|
144
|
+
### 1. UnifiedMindtraceODMBackend (Recommended)
|
|
145
|
+
|
|
146
|
+
The flagship backend that provides a unified interface for multiple databases:
|
|
147
|
+
|
|
148
|
+
**Key Features:**
|
|
149
|
+
- **Single Interface**: One API for all backends
|
|
150
|
+
- **Runtime Switching**: Change backends without code changes
|
|
151
|
+
- **Automatic Model Generation**: Converts unified models to backend-specific formats
|
|
152
|
+
- **Flexible Configuration**: Use one or multiple backends
|
|
153
|
+
|
|
154
|
+
**Configuration Options:**
|
|
155
|
+
```python
|
|
156
|
+
# Option 1: Unified model (recommended)
|
|
157
|
+
backend = UnifiedMindtraceODMBackend(
|
|
158
|
+
unified_model_cls=MyUnifiedDoc,
|
|
159
|
+
mongo_db_uri="mongodb://localhost:27017",
|
|
160
|
+
mongo_db_name="mydb",
|
|
161
|
+
redis_url="redis://localhost:6379",
|
|
162
|
+
preferred_backend=BackendType.MONGO
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
# Option 2: Separate models
|
|
166
|
+
backend = UnifiedMindtraceODMBackend(
|
|
167
|
+
mongo_model_cls=MyMongoDoc,
|
|
168
|
+
redis_model_cls=MyRedisDoc,
|
|
169
|
+
mongo_db_uri="mongodb://localhost:27017",
|
|
170
|
+
mongo_db_name="mydb",
|
|
171
|
+
redis_url="redis://localhost:6379",
|
|
172
|
+
preferred_backend=BackendType.REDIS
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Option 3: Single backend
|
|
176
|
+
backend = UnifiedMindtraceODMBackend(
|
|
177
|
+
unified_model_cls=MyUnifiedDoc,
|
|
178
|
+
mongo_db_uri="mongodb://localhost:27017",
|
|
179
|
+
mongo_db_name="mydb",
|
|
180
|
+
preferred_backend=BackendType.MONGO
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 2. MongoMindtraceODMBackend
|
|
185
|
+
|
|
186
|
+
Specialized MongoDB backend using Beanie ODM:
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from mindtrace.database import MongoMindtraceODMBackend, MindtraceDocument
|
|
190
|
+
|
|
191
|
+
class User(MindtraceDocument):
|
|
192
|
+
name: str
|
|
193
|
+
email: str
|
|
194
|
+
|
|
195
|
+
class Settings:
|
|
196
|
+
name = "users"
|
|
197
|
+
use_cache = False
|
|
198
|
+
|
|
199
|
+
backend = MongoMindtraceODMBackend(
|
|
200
|
+
model_cls=User,
|
|
201
|
+
db_uri="mongodb://localhost:27017",
|
|
202
|
+
db_name="myapp"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Supports MongoDB-specific features
|
|
206
|
+
pipeline = [{"$match": {"age": {"$gte": 18}}}]
|
|
207
|
+
results = await backend.aggregate(pipeline)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 3. RedisMindtraceODMBackend
|
|
211
|
+
|
|
212
|
+
High-performance Redis backend with JSON support:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
from mindtrace.database import RedisMindtraceODMBackend, MindtraceRedisDocument
|
|
216
|
+
from redis_om import Field
|
|
217
|
+
|
|
218
|
+
class User(MindtraceRedisDocument):
|
|
219
|
+
name: str = Field(index=True)
|
|
220
|
+
email: str = Field(index=True)
|
|
221
|
+
age: int = Field(index=True)
|
|
222
|
+
|
|
223
|
+
class Meta:
|
|
224
|
+
global_key_prefix = "myapp"
|
|
225
|
+
|
|
226
|
+
backend = RedisMindtraceODMBackend(
|
|
227
|
+
model_cls=User,
|
|
228
|
+
redis_url="redis://localhost:6379"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Initialize Redis OM
|
|
232
|
+
await backend.initialize()
|
|
233
|
+
|
|
234
|
+
# Supports Redis-specific queries
|
|
235
|
+
users = backend.find(User.age >= 18)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 4. LocalMindtraceODMBackend
|
|
239
|
+
|
|
240
|
+
In-memory backend for testing and development:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
from mindtrace.database import LocalMindtraceODMBackend
|
|
244
|
+
from pydantic import BaseModel
|
|
245
|
+
|
|
246
|
+
class User(BaseModel):
|
|
247
|
+
name: str
|
|
248
|
+
email: str
|
|
249
|
+
|
|
250
|
+
backend = LocalMindtraceODMBackend(model_cls=User)
|
|
251
|
+
# No initialization needed - works immediately!
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## API Reference
|
|
255
|
+
|
|
256
|
+
### Core Operations
|
|
257
|
+
|
|
258
|
+
All backends support these essential operations:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
# Insert a document
|
|
262
|
+
inserted_doc = await backend.insert_async(doc)
|
|
263
|
+
# or: inserted_doc = backend.insert(doc)
|
|
264
|
+
|
|
265
|
+
# Get document by ID
|
|
266
|
+
doc = await backend.get_async("doc_id")
|
|
267
|
+
# or: doc = backend.get("doc_id")
|
|
268
|
+
|
|
269
|
+
# Delete document
|
|
270
|
+
await backend.delete_async("doc_id")
|
|
271
|
+
# or: backend.delete("doc_id")
|
|
272
|
+
|
|
273
|
+
# Get all documents
|
|
274
|
+
all_docs = await backend.all_async()
|
|
275
|
+
# or: all_docs = backend.all()
|
|
276
|
+
|
|
277
|
+
# Find documents with filters
|
|
278
|
+
results = await backend.find_async({"name": "Alice"})
|
|
279
|
+
# or: results = backend.find({"name": "Alice"})
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Unified Backend Specific
|
|
283
|
+
|
|
284
|
+
Additional methods for the unified backend:
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
# Backend management
|
|
288
|
+
backend.switch_backend(BackendType.REDIS)
|
|
289
|
+
current_type = backend.get_current_backend_type()
|
|
290
|
+
is_async = backend.is_async()
|
|
291
|
+
|
|
292
|
+
# Backend availability
|
|
293
|
+
has_mongo = backend.has_mongo_backend()
|
|
294
|
+
has_redis = backend.has_redis_backend()
|
|
295
|
+
|
|
296
|
+
# Direct backend access
|
|
297
|
+
mongo_backend = backend.get_mongo_backend()
|
|
298
|
+
redis_backend = backend.get_redis_backend()
|
|
299
|
+
|
|
300
|
+
# Model access
|
|
301
|
+
raw_model = backend.get_raw_model()
|
|
302
|
+
unified_model = backend.get_unified_model()
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Advanced Querying
|
|
306
|
+
|
|
307
|
+
#### MongoDB (through Unified Backend)
|
|
308
|
+
```python
|
|
309
|
+
# MongoDB-style queries
|
|
310
|
+
users = await backend.find_async({"age": {"$gte": 18}})
|
|
311
|
+
users = await backend.find_async({"skills": {"$in": ["Python", "JavaScript"]}})
|
|
312
|
+
|
|
313
|
+
# Aggregation pipelines (when using MongoDB)
|
|
314
|
+
if backend.get_current_backend_type() == BackendType.MONGO:
|
|
315
|
+
pipeline = [
|
|
316
|
+
{"$match": {"age": {"$gte": 18}}},
|
|
317
|
+
{"$group": {"_id": "$department", "count": {"$sum": 1}}}
|
|
318
|
+
]
|
|
319
|
+
results = await backend.get_mongo_backend().aggregate(pipeline)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
#### Redis (through Unified Backend)
|
|
323
|
+
```python
|
|
324
|
+
# Switch to Redis for these queries
|
|
325
|
+
backend.switch_backend(BackendType.REDIS)
|
|
326
|
+
|
|
327
|
+
# Redis OM expressions
|
|
328
|
+
Model = backend.get_raw_model()
|
|
329
|
+
users = backend.find(Model.age >= 18)
|
|
330
|
+
users = backend.find(Model.name == "Alice")
|
|
331
|
+
users = backend.find(Model.skills << "Python") # Contains
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
## Error Handling
|
|
335
|
+
|
|
336
|
+
The module provides comprehensive error handling:
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
from mindtrace.database import DocumentNotFoundError, DuplicateInsertError
|
|
340
|
+
|
|
341
|
+
try:
|
|
342
|
+
user = await backend.get_async("non_existent_id")
|
|
343
|
+
except DocumentNotFoundError as e:
|
|
344
|
+
print(f"User not found: {e}")
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
await backend.insert_async(duplicate_user)
|
|
348
|
+
except DuplicateInsertError as e:
|
|
349
|
+
print(f"User already exists: {e}")
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Testing
|
|
353
|
+
|
|
354
|
+
The database module includes comprehensive test coverage with both unit and integration tests.
|
|
355
|
+
|
|
356
|
+
### Test Structure
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
tests/
|
|
360
|
+
├── unit/mindtrace/database/ # Unit tests (no DB required)
|
|
361
|
+
│ ├── test_mongo_unit.py
|
|
362
|
+
│ ├── test_redis_unit.py
|
|
363
|
+
│ └── test_unified_unit.py
|
|
364
|
+
└── integration/mindtrace/database/ # Integration tests (DB required)
|
|
365
|
+
├── test_mongo.py
|
|
366
|
+
├── test_redis_odm.py
|
|
367
|
+
└── test_unified.py
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Running Tests
|
|
371
|
+
|
|
372
|
+
#### Quick Start - All Tests
|
|
373
|
+
```bash
|
|
374
|
+
# Use the test script (handles everything automatically)
|
|
375
|
+
./scripts/run_tests.sh tests/unit/mindtrace/database tests/integration/mindtrace/database
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### Unit Tests Only (No Database Required)
|
|
379
|
+
```bash
|
|
380
|
+
# From project root
|
|
381
|
+
PYTHONPATH=mindtrace/core:mindtrace/database:$PYTHONPATH \
|
|
382
|
+
python -m pytest tests/unit/mindtrace/database/ -v
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
#### Integration Tests (Requires Databases)
|
|
386
|
+
```bash
|
|
387
|
+
# Start test databases
|
|
388
|
+
docker compose -f tests/docker-compose.yml up -d
|
|
389
|
+
|
|
390
|
+
# Run integration tests
|
|
391
|
+
PYTHONPATH=mindtrace/core:mindtrace/database:$PYTHONPATH \
|
|
392
|
+
python -m pytest tests/integration/mindtrace/database/ -v
|
|
393
|
+
|
|
394
|
+
# Stop test databases
|
|
395
|
+
docker compose -f tests/docker-compose.yml down
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
#### Targeted Testing
|
|
399
|
+
```bash
|
|
400
|
+
# Test only unified backend
|
|
401
|
+
./scripts/run_tests.sh --integration tests/integration/mindtrace/database/test_unified.py
|
|
402
|
+
|
|
403
|
+
# Test only MongoDB
|
|
404
|
+
./scripts/run_tests.sh --integration tests/integration/mindtrace/database/test_mongo.py
|
|
405
|
+
|
|
406
|
+
# Test only Redis
|
|
407
|
+
./scripts/run_tests.sh --integration tests/integration/mindtrace/database/test_redis_odm.py
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Test Coverage
|
|
411
|
+
|
|
412
|
+
The test suite covers:
|
|
413
|
+
|
|
414
|
+
- **CRUD Operations** - Create, Read, Update, Delete
|
|
415
|
+
- **Query Operations** - Find, filter, search
|
|
416
|
+
- **Error Handling** - All exception scenarios
|
|
417
|
+
- **Backend Switching** - Dynamic backend changes
|
|
418
|
+
- **Async/Sync Compatibility** - Both programming styles
|
|
419
|
+
- **Model Conversion** - Unified to backend-specific models
|
|
420
|
+
- **Edge Cases** - Duplicate keys, missing documents, invalid queries
|
|
421
|
+
|
|
422
|
+
## Examples
|
|
423
|
+
|
|
424
|
+
### Complete Example: User Management System
|
|
425
|
+
|
|
426
|
+
```python
|
|
427
|
+
import asyncio
|
|
428
|
+
from mindtrace.database import (
|
|
429
|
+
UnifiedMindtraceODMBackend,
|
|
430
|
+
UnifiedMindtraceDocument,
|
|
431
|
+
BackendType,
|
|
432
|
+
DocumentNotFoundError
|
|
433
|
+
)
|
|
434
|
+
from pydantic import Field
|
|
435
|
+
from typing import List
|
|
436
|
+
|
|
437
|
+
class User(UnifiedMindtraceDocument):
|
|
438
|
+
name: str = Field(description="Full name")
|
|
439
|
+
email: str = Field(description="Email address")
|
|
440
|
+
age: int = Field(ge=0, le=150, description="Age")
|
|
441
|
+
department: str = Field(description="Department")
|
|
442
|
+
skills: List[str] = Field(default_factory=list)
|
|
443
|
+
|
|
444
|
+
class Meta:
|
|
445
|
+
collection_name = "employees"
|
|
446
|
+
global_key_prefix = "company"
|
|
447
|
+
indexed_fields = ["email", "department", "skills"]
|
|
448
|
+
unique_fields = ["email"]
|
|
449
|
+
|
|
450
|
+
async def main():
|
|
451
|
+
# Setup backend with both MongoDB and Redis
|
|
452
|
+
backend = UnifiedMindtraceODMBackend(
|
|
453
|
+
unified_model_cls=User,
|
|
454
|
+
mongo_db_uri="mongodb://localhost:27017",
|
|
455
|
+
mongo_db_name="company",
|
|
456
|
+
redis_url="redis://localhost:6379",
|
|
457
|
+
preferred_backend=BackendType.MONGO
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
await backend.initialize_async()
|
|
461
|
+
|
|
462
|
+
# Create some users
|
|
463
|
+
users = [
|
|
464
|
+
User(
|
|
465
|
+
name="Alice Johnson",
|
|
466
|
+
email="alice@company.com",
|
|
467
|
+
age=30,
|
|
468
|
+
department="Engineering",
|
|
469
|
+
skills=["Python", "MongoDB", "Docker"]
|
|
470
|
+
),
|
|
471
|
+
User(
|
|
472
|
+
name="Bob Smith",
|
|
473
|
+
email="bob@company.com",
|
|
474
|
+
age=25,
|
|
475
|
+
department="Engineering",
|
|
476
|
+
skills=["JavaScript", "Redis", "React"]
|
|
477
|
+
),
|
|
478
|
+
User(
|
|
479
|
+
name="Carol Davis",
|
|
480
|
+
email="carol@company.com",
|
|
481
|
+
age=35,
|
|
482
|
+
department="Marketing",
|
|
483
|
+
skills=["Analytics", "SQL"]
|
|
484
|
+
)
|
|
485
|
+
]
|
|
486
|
+
|
|
487
|
+
# Insert users
|
|
488
|
+
print("Creating users...")
|
|
489
|
+
for user in users:
|
|
490
|
+
try:
|
|
491
|
+
inserted = await backend.insert_async(user)
|
|
492
|
+
print(f"Created: {inserted.name} (ID: {inserted.id})")
|
|
493
|
+
except Exception as e:
|
|
494
|
+
print(f"Failed to create {user.name}: {e}")
|
|
495
|
+
|
|
496
|
+
# Find engineers
|
|
497
|
+
print("\nFinding engineers...")
|
|
498
|
+
engineers = await backend.find_async({"department": "Engineering"})
|
|
499
|
+
for eng in engineers:
|
|
500
|
+
print(f"{eng.name} - Skills: {', '.join(eng.skills)}")
|
|
501
|
+
|
|
502
|
+
# Switch to Redis for fast lookups
|
|
503
|
+
print("\n Switching to Redis for fast operations...")
|
|
504
|
+
backend.switch_backend(BackendType.REDIS)
|
|
505
|
+
|
|
506
|
+
# Insert more users in Redis
|
|
507
|
+
redis_user = User(
|
|
508
|
+
name="Dave Wilson",
|
|
509
|
+
email="dave@company.com",
|
|
510
|
+
age=28,
|
|
511
|
+
department="DevOps",
|
|
512
|
+
skills=["Kubernetes", "Redis", "Monitoring"]
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
redis_inserted = backend.insert(redis_user)
|
|
516
|
+
print(f"Redis user created: {redis_inserted.name}")
|
|
517
|
+
|
|
518
|
+
# Demonstrate backend isolation
|
|
519
|
+
print(f"\n MongoDB users: {len(await backend.get_mongo_backend().all())}")
|
|
520
|
+
print(f"Redis users: {len(backend.get_redis_backend().all())}")
|
|
521
|
+
|
|
522
|
+
# Switch back to MongoDB
|
|
523
|
+
backend.switch_backend(BackendType.MONGO)
|
|
524
|
+
print(f"Back to MongoDB - Users: {len(await backend.all_async())}")
|
|
525
|
+
|
|
526
|
+
if __name__ == "__main__":
|
|
527
|
+
asyncio.run(main())
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### More Examples
|
|
531
|
+
|
|
532
|
+
Check out the `samples/database/` directory for additional examples:
|
|
533
|
+
|
|
534
|
+
- **`using_unified_backend.py`** - Comprehensive unified backend usage
|
|
535
|
+
- **Advanced querying patterns**
|
|
536
|
+
- **Backend switching strategies**
|
|
537
|
+
- **Error handling best practices**
|
|
538
|
+
|
|
539
|
+
## Best Practices
|
|
540
|
+
|
|
541
|
+
### 1. Model Design
|
|
542
|
+
```python
|
|
543
|
+
# Good: Clear, descriptive models
|
|
544
|
+
class Product(UnifiedMindtraceDocument):
|
|
545
|
+
name: str = Field(description="Product name", min_length=1)
|
|
546
|
+
price: float = Field(ge=0, description="Price in USD")
|
|
547
|
+
category: str = Field(description="Product category")
|
|
548
|
+
|
|
549
|
+
class Meta:
|
|
550
|
+
collection_name = "products"
|
|
551
|
+
indexed_fields = ["category", "name"]
|
|
552
|
+
unique_fields = ["name"]
|
|
553
|
+
|
|
554
|
+
# Avoid: Unclear models without validation
|
|
555
|
+
class Product(UnifiedMindtraceDocument):
|
|
556
|
+
n: str
|
|
557
|
+
p: float
|
|
558
|
+
c: str
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### 2. Error Handling
|
|
562
|
+
```python
|
|
563
|
+
# Always handle database exceptions
|
|
564
|
+
try:
|
|
565
|
+
user = await backend.get_async(user_id)
|
|
566
|
+
print(f"Found user: {user.name}")
|
|
567
|
+
except DocumentNotFoundError:
|
|
568
|
+
print("User not found - creating new user")
|
|
569
|
+
user = await backend.insert_async(User(name="New User", email="new@example.com"))
|
|
570
|
+
except Exception as e:
|
|
571
|
+
logger.error(f"Database error: {e}")
|
|
572
|
+
# Handle appropriately
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### 3. Backend Selection
|
|
576
|
+
```python
|
|
577
|
+
# Choose backends based on use case
|
|
578
|
+
if high_frequency_reads:
|
|
579
|
+
backend.switch_backend(BackendType.REDIS) # Fast reads
|
|
580
|
+
else:
|
|
581
|
+
backend.switch_backend(BackendType.MONGO) # Complex queries
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
### 4. Initialization
|
|
585
|
+
```python
|
|
586
|
+
# Initialize once at application startup
|
|
587
|
+
class DatabaseService:
|
|
588
|
+
def __init__(self):
|
|
589
|
+
self.backend = UnifiedMindtraceODMBackend(...)
|
|
590
|
+
|
|
591
|
+
async def initialize(self):
|
|
592
|
+
await self.backend.initialize_async()
|
|
593
|
+
self.backend.initialize_sync() # If you need both
|
|
594
|
+
|
|
595
|
+
async def cleanup(self):
|
|
596
|
+
# Cleanup if needed
|
|
597
|
+
pass
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
## Contributing
|
|
601
|
+
|
|
602
|
+
When adding new features:
|
|
603
|
+
|
|
604
|
+
1. **Add tests** - Both unit and integration tests
|
|
605
|
+
2. **Update documentation** - Keep README and docstrings current
|
|
606
|
+
3. **Follow patterns** - Use existing code style and patterns
|
|
607
|
+
4. **Test thoroughly** - Run the full test suite
|
|
608
|
+
|
|
609
|
+
## Requirements
|
|
610
|
+
|
|
611
|
+
- **Python 3.9+**
|
|
612
|
+
- **MongoDB 4.4+** (for MongoDB backend)
|
|
613
|
+
- **Redis 6.0+** (for Redis backend)
|
|
614
|
+
- **Core dependencies**: `pydantic`, `beanie`, `redis-om-python`
|
|
615
|
+
|
|
616
|
+
## Need Help?
|
|
617
|
+
|
|
618
|
+
- Check the `samples/database/` directory for working examples
|
|
619
|
+
- Look at the test files for usage patterns
|
|
620
|
+
- Review the docstrings in the source code for detailed API documentation
|
|
621
|
+
|
|
622
|
+
The Mindtrace Database Module makes it easy to work with multiple databases through a single, powerful interface. Start simple with the unified backend, then customize as your needs grow!
|
|
623
|
+
|