Document SQLite query optimization conventions
This commit is contained in:
@@ -100,7 +100,7 @@ Last.fm schemas (separate, not Ecto-persisted to main DB):
|
||||
| `RecordSets` | RecordSet, RecordSetItem | User-curated record groupings with ordering |
|
||||
| `ScrobbleRules` | ScrobbleRule | Rules to remap Last.fm scrobble data to correct MusicBrainz IDs; searchable by match_value/target/description, orderable by alphabetical or inserted_at |
|
||||
| `ScrobbleActivity` | — | Scrobbling releases/media/tracks to Last.fm |
|
||||
| `ListeningStats` | (LastFm.Track, ArtistRecord, ArtistInfo) | Scrobble persistence, refresh scheduling, listening analytics, track CRUD, search, listing: scrobble counts, artist play counts (from DB), recent activity, top albums/artists by period |
|
||||
| `ListeningStats` | (LastFm.Track, RecordRelease, ArtistRecord, ArtistInfo) | Scrobble persistence, refresh scheduling, listening analytics, track CRUD, search, listing: scrobble counts, artist play counts (from DB), recent activity, top albums/artists by period |
|
||||
| `OnlineStoreTemplates` | OnlineStoreTemplate | URL templates for buying records online; searchable by name/description |
|
||||
| `Search` | (cross-context) | Universal search dispatcher across collection, wishlist, artists, record sets (delegates to domain contexts) |
|
||||
| `Secrets` | Secret | Encrypted key-value storage (CRUD + delete) |
|
||||
|
||||
@@ -53,6 +53,9 @@ Rules extracted from commit history that are specific to this project and not al
|
||||
## Database
|
||||
|
||||
- **SQLite JSON patterns:** `json_each()` and `json_extract()` via `fragment` for JSON column queries. Expression-based indexes on `json_extract` for performance.
|
||||
- **Match `GROUP BY` expressions to existing expression indexes.** SQLite treats `json_extract(?, '$.path')` and `? ->> '$.path'` as semantically equal but textually distinct. Use `json_extract` in `fragment` when an expression index on `json_extract(...)` already exists, so the GROUP BY can use the index for natural ordering.
|
||||
- **Force subquery materialization with `limit: -1` to prevent flattening.** When a date-filtered scan feeds an outer `GROUP BY` and SQLite prefers the wrong composite index, wrap the filter in `subquery()` with `limit: -1`. The forced materialization preserves the range-scan index.
|
||||
- **Correlated scalar subqueries beat `LEFT JOIN` for small-LIMIT enrichment.** When attaching lookups from a large table to a LIMIT'd result set, inline `(SELECT ... WHERE ...)` in the outer SELECT. Cost then scales with the outer LIMIT, not the lookup table size.
|
||||
- **Materialized views via triggers** (SQLite lacks native materialized views). Use explicit `up`/`down` in migrations for non-reversible DDL.
|
||||
- **Read-only schemas** for materialized/view tables: `@primary_key false`, no changeset functions, no timestamps.
|
||||
- **Every `execute` provides both up and down SQL.** Every index has a comment explaining which query it helps.
|
||||
|
||||
Reference in New Issue
Block a user