CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-odmantic

AsyncIO MongoDB Object Document Mapper for Python using type hints

Pending
Overview
Eval results
Files

indexes.mddocs/

Indexes

ODMantic provides index management for MongoDB collections through field-level indexing options and compound index definitions.

Capabilities

Index Class

Define compound indexes spanning multiple fields with custom options.

class Index:
    """Compound index definition for MongoDB collections."""
    
    def __init__(self, *fields, **kwargs):
        """
        Create compound index definition.
        
        Args:
            *fields: Field names or (field, direction) tuples
            **kwargs: Index options (unique, sparse, background, etc.)
        
        Example:
            Index("field1", ("field2", -1), unique=True)
            Index(User.name, User.email, background=True)
        """

Reference Function

Define reference fields that link to other documents.

def Reference(*, key_name=None):
    """
    Define reference field to another document.
    
    Args:
        key_name: MongoDB field name for the reference
        
    Returns:
        Field configuration for ObjectId reference
    """

Field-Level Indexing

Index options available in Field definitions.

# In Field() function:
# index=True         - Create regular index
# unique=True        - Create unique index  
# primary_field=True - Use as primary key (_id)

Usage Examples

Single Field Indexes

from odmantic import Model, Field

class User(Model):
    # Regular index
    username: str = Field(index=True)
    
    # Unique index
    email: str = Field(unique=True)
    
    # Both indexed
    phone: str = Field(index=True, unique=True)
    
    # Non-indexed fields
    first_name: str
    last_name: str
    age: int

# The engine will create indexes when configure_database is called
async def setup_indexes(engine):
    await engine.configure_database([User])

Compound Indexes

from odmantic import Model, Index

class BlogPost(Model):
    title: str
    author: str = Field(index=True)
    category: str = Field(index=True)
    published_date: datetime
    view_count: int = 0
    tags: list[str] = []
    
    # Define compound indexes
    model_config = {
        "collection": "blog_posts",
        "indexes": [
            # Compound index on author and published_date
            Index("author", "published_date"),
            
            # Compound index with sort direction
            Index("category", ("published_date", -1)),
            
            # Unique compound index
            Index("author", "title", unique=True),
            
            # Text index for search
            Index(("title", "text"), ("content", "text")),
            
            # Sparse index (only indexes documents with the field)
            Index("tags", sparse=True),
        ]
    }

# Alternative way to define indexes using class attribute
class Product(Model):
    name: str
    category: str
    price: float
    brand: str
    sku: str = Field(unique=True)
    
    # Define indexes as class attribute
    __indexes__ = [
        Index("category", "brand"),
        Index("price", background=True),
        Index(("name", "text"), name="text_search_index"),
    ]

Reference Fields

from odmantic import Model, Reference, ObjectId

class Author(Model):
    name: str = Field(unique=True)
    email: str = Field(unique=True)
    bio: str = ""

class Book(Model):
    title: str
    isbn: str = Field(unique=True)
    
    # Reference to Author document
    author_id: ObjectId = Reference()
    
    # Reference with custom key name
    publisher_id: ObjectId = Reference(key_name="publisher")
    
    published_date: datetime
    page_count: int

class Review(Model):
    # Multiple references
    book_id: ObjectId = Reference()
    reviewer_id: ObjectId = Reference()
    
    rating: int = Field(ge=1, le=5)
    comment: str
    created_at: datetime = Field(default_factory=datetime.utcnow)

# Usage
async def reference_example(engine):
    # Create author
    author = Author(name="Jane Doe", email="jane@example.com")
    await engine.save(author)
    
    # Create book with reference to author
    book = Book(
        title="Python Patterns",
        isbn="978-1234567890",
        author_id=author.id,  # Reference using ObjectId
        publisher_id=ObjectId(),  # Some publisher ID
        published_date=datetime.utcnow(),
        page_count=300
    )
    await engine.save(book)
    
    # Find books by author
    author_books = await engine.find(Book, Book.author_id == author.id)

Text Indexes

class Article(Model):
    title: str
    content: str
    author: str
    tags: list[str] = []
    
    # Text search index
    __indexes__ = [
        # Simple text index
        Index(("title", "text"), ("content", "text")),
        
        # Text index with weights
        Index(
            ("title", "text"), 
            ("content", "text"),
            weights={"title": 10, "content": 5},
            name="article_text_search"
        ),
        
        # Combined text and regular index
        Index("author", ("title", "text")),
    ]

# Text search usage
async def text_search_example(engine):
    # Configure indexes
    await engine.configure_database([Article])
    
    # Text search requires MongoDB's $text operator
    # ODMantic doesn't have built-in text search functions,
    # but you can use raw MongoDB queries through the collection
    collection = engine.get_collection(Article)
    
    # Raw text search
    cursor = collection.find({"$text": {"$search": "python programming"}})
    articles = []
    async for doc in cursor:
        article = Article.model_validate_doc(doc)
        articles.append(article)

Geospatial Indexes

class Location(Model):
    name: str
    # GeoJSON point format: [longitude, latitude]
    coordinates: list[float] = Field(min_items=2, max_items=2)
    type: str = "Point"
    
    # 2dsphere index for geospatial queries
    __indexes__ = [
        Index(("coordinates", "2dsphere")),
        Index("name", ("coordinates", "2dsphere")),
    ]

# Geospatial usage
async def geospatial_example(engine):
    # Create location
    location = Location(
        name="Central Park",
        coordinates=[-73.965355, 40.782865]  # [longitude, latitude]
    )
    await engine.save(location)
    
    # Geospatial queries require raw MongoDB operations
    collection = engine.get_collection(Location)
    
    # Find locations near a point
    near_query = {
        "coordinates": {
            "$near": {
                "$geometry": {
                    "type": "Point",
                    "coordinates": [-73.970, 40.780]
                },
                "$maxDistance": 1000  # meters
            }
        }
    }
    
    cursor = collection.find(near_query)
    nearby_locations = []
    async for doc in cursor:
        location = Location.model_validate_doc(doc)
        nearby_locations.append(location)

Index Management

async def index_management_examples(engine):
    # Configure database indexes for models
    models = [User, BlogPost, Product, Article, Location]
    await engine.configure_database(models)
    
    # Update existing indexes (use with caution in production)
    await engine.configure_database(models, update_existing_indexes=True)
    
    # Get raw collection for manual index operations
    collection = engine.get_collection(User)
    
    # List all indexes
    indexes = await collection.list_indexes().to_list(None)
    for index in indexes:
        print(f"Index: {index}")
    
    # Create index manually if needed
    await collection.create_index([("custom_field", 1)], background=True)
    
    # Drop index manually if needed
    await collection.drop_index("custom_field_1")

Performance Considerations

class OptimizedModel(Model):
    # Frequently queried fields should be indexed
    user_id: ObjectId = Field(index=True)
    status: str = Field(index=True)
    created_at: datetime = Field(index=True, default_factory=datetime.utcnow)
    
    # Compound index for common query patterns
    __indexes__ = [
        # For queries like: status="active" AND created_at > date
        Index("status", ("created_at", -1)),
        
        # For queries like: user_id=X AND status="active"
        Index("user_id", "status"),
        
        # Background index creation (non-blocking)
        Index("some_large_field", background=True),
        
        # Sparse index (only for documents with the field)
        Index("optional_field", sparse=True),
        
        # Partial index with filter expression
        Index(
            "filtered_field",
            partialFilterExpression={"filtered_field": {"$exists": True}}
        ),
    ]

# Index strategy guidelines:
def index_strategy_example():
    """
    Index Strategy Guidelines:
    
    1. Index fields used in WHERE clauses frequently
    2. Create compound indexes for multi-field queries
    3. Order compound index fields by selectivity (most selective first)
    4. Use sparse indexes for optional fields
    5. Use background=True for large collections
    6. Monitor index usage and remove unused indexes
    7. Consider partial indexes for conditional queries
    """
    
    # Good compound index order (high to low selectivity)
    # Index("user_id", "status", "date")  # user_id is most selective
    
    # Bad compound index order
    # Index("status", "user_id", "date")  # status has few unique values

Custom Index Options

class AdvancedIndexModel(Model):
    name: str
    data: dict = {}
    expires_at: datetime
    
    __indexes__ = [
        # TTL index (documents auto-expire)
        Index("expires_at", expireAfterSeconds=3600),
        
        # Case-insensitive index
        Index("name", collation={"locale": "en", "strength": 2}),
        
        # Partial index with complex filter
        Index(
            "data.important_field",
            partialFilterExpression={
                "data.important_field": {"$exists": True, "$ne": None}
            },
            sparse=True
        ),
        
        # Named index for reference
        Index("name", "data.category", name="name_category_idx"),
        
        # Index with custom options
        Index(
            ("name", "text"),
            default_language="english",
            language_override="lang_field"
        ),
    ]

Install with Tessl CLI

npx tessl i tessl/pypi-odmantic

docs

bson-types.md

engines.md

fields.md

index.md

indexes.md

models.md

queries.md

sessions.md

tile.json