Metadata-Version: 2.4
Name: mongoflow
Version: 0.0.2
Summary: Elegant MongoDB Object Document Mapper (ODM) for Python - fluent query builder included
Author-email: Caio Norder <caio@caionorder.com>
License: MIT
Project-URL: Homepage, https://github.com/caionorder/mongoflow
Project-URL: Bug Tracker, https://github.com/caionorder/mongoflow/issues
Project-URL: Documentation, https://mongoflow.readthedocs.io
Project-URL: Source Code, https://github.com/caionorder/mongoflow
Project-URL: Changelog, https://github.com/caionorder/mongoflow/blob/main/CHANGELOG.md
Keywords: mongodb,odm,orm,database,nosql,query-builder,mongoflow,motor,async,pymongo
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Database
Classifier: Topic :: Database :: Database Engines/Servers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Classifier: Framework :: AsyncIO
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pymongo<5.0,>=4.0
Requires-Dist: python-dateutil>=2.8.0
Requires-Dist: typing-extensions>=4.0.0; python_version < "3.10"
Provides-Extra: cache
Requires-Dist: redis>=4.0.0; extra == "cache"
Requires-Dist: hiredis>=2.0.0; extra == "cache"
Provides-Extra: validation
Requires-Dist: pydantic>=2.0.0; extra == "validation"
Provides-Extra: async
Requires-Dist: motor>=3.0.0; extra == "async"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: mongomock>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: flake8>=6.0.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: isort>=5.0.0; extra == "dev"
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
Requires-Dist: sphinx>=6.0.0; extra == "dev"
Requires-Dist: sphinx-rtd-theme>=1.0.0; extra == "dev"
Requires-Dist: tox>=4.0.0; extra == "dev"
Requires-Dist: twine>=4.0.0; extra == "dev"
Requires-Dist: build>=0.10.0; extra == "dev"
Provides-Extra: all
Requires-Dist: mongoflow[async,cache,validation]; extra == "all"
Dynamic: license-file

# MongoFlow 🌊

Elegant MongoDB Object Document Mapper (ODM) for Python - with a fluent query builder that makes working with MongoDB a breeze! 🚀

## ✨ Features

- 🎯 **Intuitive Query Builder** - Fluent, chainable queries that feel natural
- ⚡ **High Performance** - Connection pooling, batch operations, and streaming
- 🔧 **Flexible** - Use as simple queries or full ODM with models
- 🎨 **Clean API** - Pythonic, fully typed, and well-documented
- 🚀 **Production Ready** - Battle-tested patterns with automatic retries
- 💾 **Smart Caching** - Optional Redis integration for blazing speed
- 🔄 **Async Support** - Full async/await support with Motor
- 📦 **Lightweight** - Minimal dependencies, maximum functionality

## 📦 Installation

```bash
# Basic installation
pip install mongoflow

# With all features
pip install mongoflow[all]

# With specific features
pip install mongoflow[cache]      # Redis caching
pip install mongoflow[validation] # Pydantic validation
pip install mongoflow[async]      # Async support
```

## 🚀 Quick Start

### Basic Usage

```python
from mongoflow import MongoFlow, Repository

# Connect to MongoDB
MongoFlow.connect('mongodb://localhost:27017', 'mydb')

# Define a repository
class UserRepository(Repository):
    collection_name = 'users'

# Use it!
users = UserRepository()

# Create
user = users.create({
    'name': 'John Doe',
    'email': 'john@example.com',
    'age': 30
})

# Query with fluent builder
active_adults = (users.query()
    .where('status', 'active')
    .where_greater_than('age', 18)
    .order_by('created_at', 'desc')
    .limit(10)
    .get())
```

### Async Support

```python
import asyncio
from mongoflow import AsyncMongoFlow, AsyncRepository

class AsyncUserRepository(AsyncRepository):
    collection_name = 'users'

async def main():
    # Connect
    await AsyncMongoFlow.connect('mongodb://localhost:27017', 'mydb')
    
    repo = AsyncUserRepository()
    
    # All methods support async/await
    user = await repo.create({'name': 'Jane'})
    users = await repo.query().where('active', True).get()
    
    # Async streaming for large datasets
    async for user in repo.query().stream():
        print(user)

asyncio.run(main())
```

## 📚 Query Builder Methods

Both sync and async query builders support the following methods:

### Filtering Methods

```python
# Basic where conditions
.where('field', 'value')                    # field == value
.where('field', 'value', '$ne')            # field != value
.where_in('field', [1, 2, 3])              # field in [1, 2, 3]
.where_not_in('field', [1, 2, 3])          # field not in [1, 2, 3]
.where_between('field', min, max)          # min <= field <= max
.where_greater_than('field', value)        # field > value
.where_less_than('field', value)           # field < value
.where_like('field', 'pattern')            # regex pattern matching
.where_null('field')                        # field is null
.where_not_null('field')                    # field is not null
.where_exists('field', True)               # field exists in document

# Logical operators
.or_where([{'field1': 'value1'}, {'field2': 'value2'}])
.and_where([{'field1': 'value1'}, {'field2': 'value2'}])
```

### Query Modifiers

```python
# Projection
.select('field1', 'field2')                # Include only these fields
.exclude('field1', 'field2')               # Exclude these fields

# Sorting and pagination
.order_by('field', 'asc')                  # Sort ascending
.order_by('field', 'desc')                 # Sort descending
.skip(10)                                   # Skip N documents
.limit(20)                                  # Limit to N documents
.paginate(page=2, per_page=20)            # Get paginated results
```

### Execution Methods

```python
# Sync methods
.get()                                      # Get all results
.first()                                    # Get first result
.last()                                     # Get last result
.count()                                    # Count matching documents
.exists()                                   # Check if any documents exist
.distinct('field')                          # Get distinct values
.stream(batch_size=100)                    # Stream results

# Async methods (same as above but with await)
await query.get()
await query.first()
await query.last()
await query.count()
await query.exists()
await query.distinct('field')
async for doc in query.stream():
    process(doc)
```

### Aggregation Methods

```python
# Simple aggregations
.sum('field')                               # Sum of field values
.avg('field')                               # Average of field values
.min('field')                               # Minimum value
.max('field')                               # Maximum value

# Group operations
.group('status', {'count': {'$sum': 1}})   # Group by field
.group(
    {'status': '$status', 'type': '$type'},  # Group by multiple fields
    {'total': {'$sum': '$amount'}}           # With aggregations
)

# Custom aggregation pipelines
.aggregate([
    {'$match': {'status': 'active'}},
    {'$group': {'_id': '$category', 'total': {'$sum': '$amount'}}}
])
```

### Async-Specific Features

All the above methods work with async query builders, just remember to use `await`:

```python
# Async examples
users = await users.query().where('active', True).get()
count = await users.query().where_between('age', 18, 65).count()
stats = await users.query().group('role', {'count': {'$sum': 1}})

# Async streaming
async for user in users.query().where('status', 'active').stream():
    await process_user(user)
