Add composite database indexes for gallery queries#8
Conversation
The gallery views rely on several query patterns that currently perform full table scans and in-memory sorts: - Default gallery: ORDER BY taken_at DESC, id - "Rating first" toggle: ORDER BY rating DESC, taken_at DESC, id - Recipe image list: WHERE fujifilm_recipe_id = ? ORDER BY rating, taken_at - Favorites filter: WHERE is_favorite = true ORDER BY taken_at DESC - Recipe lookups by film simulation type With ~10k images these queries take 100-200ms (seq scan + sort). Composite indexes bring them down to 5-10ms (index scan). Indexes added: - idx_image_taken_id: covers default gallery sort - idx_image_rating_taken: covers "Rating first" sort - idx_image_recipe_rating: covers per-recipe image pages - idx_image_favorite_taken: covers favorites filter - idx_recipe_film_sim: covers recipe graph film simulation lookups Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Thanks for this PR! Good catch — I hadn't noticed the queries were getting that slow. In the past I'd implemented this kind of multi-filter gallery with Elasticsearch, where everything is indexed by default, so I never had to think about it explicitly.
The PR looks good overall. I've left some comments on the code and one about the structure of the PR in general:
(convention): The PR contains a no-op sync commit that introduces no changes:
14c408f — Merge branch 'main' into perf/add-gallery-indexes
This commit exists solely to sync the branch with main, which should be done with git rebase main instead. Please rebase and force-push to remove it before merging.
See: Do only one thing with each commit / Clean up mistakes by rewriting history
|
|
||
| class Meta: | ||
| indexes = [ | ||
| models.Index(fields=["film_simulation"], name="idx_recipe_film_sim"), |
There was a problem hiding this comment.
(suggestion): The FujifilmRecipe table tends to stay small relative to the image catalog — recipes are unique combinations of camera settings, so the cardinality is naturally limited. For a small table, PostgreSQL's query planner may prefer a sequential scan over an index lookup, making this index add write overhead without a measurable query benefit. Worth considering whether it's needed.
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
There was a problem hiding this comment.
(question)[non-blocking]: This migration is safe to skip for non-developer users since a missing index will not break the app. However, it is a good reminder that we need a way for users who just want to run the project to apply migrations easily — e.g. a make update command that runs git pull origin main followed by python manage.py migrate. Not required for this PR, but important to have before we ship any migration that adds or modifies fields or tables.
Summary
ImageandFujifilmRecipemodels targeting the most frequent query patterns in gallery viewsWhy
The gallery views use
ORDER BYandWHEREpatterns that currently require full table scans + in-memory sorts. With a catalog of ~10k images, these queries take 100-200ms. Composite indexes bring them down to ~5-10ms via index scans.Indexes added
idx_image_taken_id-taken_at, id)idx_image_rating_taken-rating, -taken_at, id)idx_image_recipe_ratingrecipe_id, -rating, -taken_at)idx_image_favorite_takenis_favorite, -taken_at)idx_recipe_film_simQuery patterns matched
Test plan
🤖 Generated with Claude Code