A hands-on lab that demonstrates two of the most common NoSQL building blocks with Python: MongoDB for document modeling, aggregation, and indexing, and Redis for caching, rate limiting, leaderboards, and pub/sub.
Every module is small, commented, and dependency-injected so it can be driven
either against real mongo:7 / redis:7 containers or against in-memory
doubles (mongomock, fakeredis) in the test suite — no live server required
to run the tests.
mongodb/
models.py # dataclass document models (Movie, Rating) + conversions
repository.py # CRUD repository over a pymongo collection
aggregations.py # $group / $facet / $lookup / $bucket / $unwind pipelines
indexes.py # single, compound, and text indexes + explain helpers
seed.py # deterministic sample catalog generated in code
redis/
cache.py # cache-aside wrapper with TTL + stable key building
leaderboard.py # sorted-set leaderboard (add, top-N, rank, neighbours)
rate_limiter.py # fixed-window rate limiter with pure window math
pubsub.py # publish/subscribe demonstration
connection.py # the only module that imports the real redis driver
tests/ # pytest suite (no live servers needed)
demo.py # guided end-to-end walkthrough
docker-compose.yml # mongo:7 and redis:7 services
- Document modeling (
models.py) — plain dataclasses withto_doc/from_docconverters and a computeddecadeproperty, keeping storage concerns out of the domain objects. - Repository / CRUD (
repository.py) — inserts, genre and year queries, a top-rated query using a nested sort key, an idempotent$addToSetgenre update, and a running-average rating update that avoids re-reading votes. - Aggregation pipelines (
aggregations.py):count_by_genre—$unwind+$group+$sortaverage_rating_by_decade— computed$floor/$divide/$multiplybucket keyruntime_buckets—$bucketwith per-bucket sample titlesgenre_facet—$facetcomputing several summaries in one passdirectors_with_movies— self$lookup+$grouphighest_grossing_per_decade—$addFields+$groupwith$max/$first
- Indexing (
indexes.py) — declarative single, compound, and text index specs, plusexplainhelpers to confirm anIXSCANis used instead of a collection scan.
- Cache-aside (
cache.py) —get_or_setserves from Redis on a hit and computes-then-stores on a miss, with a hashed, order-independent key builder, hit/miss accounting, and a decorator form. - Leaderboard (
leaderboard.py) — a sorted set gives O(log N) score updates and cheap top-N, rank, and "players around me" queries. - Rate limiter (
rate_limiter.py) — fixed-window counters keyed by caller and window index; the window math is a pure, separately tested function. - Pub/Sub (
pubsub.py) — a publisher fans messages out to subscribers on a channel, with a helper that drains a fixed number of messages.
Note: the
redis/directory intentionally has no__init__.py. Under Python's PEP 420 namespace-package rules, the installedredisdriver still winsimport redis, while the lab modules import as top-level names.
Prerequisites: Python 3.12 and Docker.
# 1. Create the environment
make venv # or: python -m venv .venv && pip install -r requirements.txt
# 2. Start MongoDB and Redis
make up # docker compose up -d
# 3. Run the guided demonstration
make demo # or: .venv/bin/python demo.py
# 4. Stop the containers when finished
make downdemo.py seeds the catalog, creates indexes, runs every aggregation, and then
walks through all four Redis patterns, printing what each step shows. Use
python demo.py --seed-only (or make seed) to only load the sample data.
The suite covers leaderboard ordering, cache key building and hit/miss behaviour, rate-limiter window math and enforcement, pub/sub delivery, and the repository / aggregation logic — all against in-memory doubles.
make test # or: .venv/bin/python -m pytest| Variable | Default | Used by |
|---|---|---|
MONGO_URL |
mongodb://localhost:27017 |
demo.py |
REDIS_URL |
redis://localhost:6379/0 |
redis/connection |
Released under the MIT License.