Skip to content

Use Case 13: Declaring Indexes (Single-Field, Composite, Unique)

Scenario: Ensure each collection has the right indexes for fast queries and data integrity โ€” declared in one place and created at startup.


๐Ÿ“ฆ What's New?

Component Description
ModelRegistry.register(indexes=...) Each spec is passed as-is to pymongo create_index. Supported forms: tuple ("field", direction), compound list [("a", 1), ("b", -1)], or a dict with keys + options.
ModelRegistry.initialize_all Creates every registered index during startup (idempotent โ€” create_index skips existing indexes).

๐Ÿš€ Example

Python
from contextlib import asynccontextmanager

from pymongo import ASCENDING, DESCENDING
from mongo_ops import BaseDocument, ModelRegistry, MongoConnectionManager


class User(BaseDocument):
    username: str = ""
    email: str = ""


class BlogPost(BaseDocument):
    author_id: str = ""
    created_at: str = ""
    title: str = ""


class Passenger(BaseDocument):
    email: str = ""
    seat: str = ""


# 1๏ธโƒฃ Single-field index โ€” email lookups.
ModelRegistry.register(
    collection_name="users",
    model=User,
    indexes=[("email", ASCENDING)],
)

# 2๏ธโƒฃ Composite index โ€” queries filtering by author + creation date.
ModelRegistry.register(
    collection_name="posts",
    model=BlogPost,
    indexes=[[("author_id", ASCENDING), ("created_at", DESCENDING)]],
)

# 3๏ธโƒฃ Unique index with a custom name โ€” enforce unique emails.
ModelRegistry.register(
    collection_name="passengers",
    model=Passenger,
    indexes=[
        {
            "keys": [("email", ASCENDING)],
            "options": {"unique": True, "name": "uq_passenger_email"},
        }
    ],
)


# 4๏ธโƒฃ Everything is created on startup.
@asynccontextmanager
async def lifespan(_app):
    async with MongoConnectionManager.lifespan(
        uri="mongodb://localhost:27017",
        db_name="mydb",
    ):
        await ModelRegistry.initialize_all()
        yield

๐Ÿ’ก Tips

  • Every spec passes through to collection.create_index(spec) โ€” so MongoDB options like unique, sparse, and expireAfterSeconds (TTL) belong in the options dict.
  • Idempotent by construction: create_index is a no-op when a same-shape index already exists.
  • Verify with the Mongo shell:
JavaScript
db.<collection>.getIndexes()
  • A unique index on an already-duplicated field will fail with DuplicateKeyError on startup โ€” clean the data first.