From c029e4618b29cbd4db6decae4768e2bb5db93816 Mon Sep 17 00:00:00 2001 From: Claudio Ortolina Date: Thu, 3 Apr 2025 09:47:42 +0100 Subject: [PATCH] Add database structure documentation --- README.md | 4 ++ docs/database_structure.md | 121 +++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 docs/database_structure.md diff --git a/README.md b/README.md index 0e07008d..afd769bd 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,10 @@ The application is setup for deployment on Fly.io - just make sure you edit See the `.github` folder. +## Architecture + +See the `docs` folder. + ## Favicons This favicon was generated using the following graphics from Twitter Twemoji: diff --git a/docs/database_structure.md b/docs/database_structure.md new file mode 100644 index 00000000..97f5c065 --- /dev/null +++ b/docs/database_structure.md @@ -0,0 +1,121 @@ +# Database Structure + +This document describes the database structure of the Music Library application. + +## Entity Relationship Diagram + +```mermaid +erDiagram + RECORDS { + uuid id PK + string type + string format + string title + uuid musicbrainz_id + string[] genres + string cover_url + blob cover_data + string cover_hash + map musicbrainz_data + string[] release_ids + string[] included_release_group_ids + datetime purchased_at + string release + map artists + datetime inserted_at + datetime updated_at + } + + RECORDS_SEARCH_INDEX { + uuid id PK + string type + string format + string title + uuid musicbrainz_id + string[] genres + string[] release_ids + string[] included_release_group_ids + string cover_hash + datetime purchased_at + string release + map artists + } + + ARTIST_RECORDS { + uuid musicbrainz_id + uuid record_id + map artist + } + + RECORDS ||--o{ RECORDS_SEARCH_INDEX : "syncs via triggers" + RECORDS ||--o{ ARTIST_RECORDS : "extracted from artists JSON" +``` + +## Tables Description + +### Records + +The main table storing music records. Key features: + +- Uses UUID as primary key +- Stores basic record information (title, type, format, year) +- Includes MusicBrainz integration with IDs and additional data +- Stores cover image data and URLs +- Embeds artists data directly in a JSON field +- Includes timestamps for record keeping + +### Records Search Index + +A virtual FTS5 (Full Text Search) table that mirrors the records table for efficient searching: + +- Automatically synced with the records table via triggers +- Optimized for full-text search operations +- Contains most fields from the records table +- Some fields are marked as UNINDEXED for efficiency + +### Views + +#### Artist Records View + +A view that extracts artist information from the embedded JSON in the records table: + +```sql +CREATE VIEW artist_records AS + SELECT json_extract(json_each.value, '$.musicbrainz_id') AS musicbrainz_id, + records.id AS record_id, + json_each.value as artist + FROM records, + json_each(records.artists) +``` + +### Triggers + +The following triggers maintain the search index: + +1. `records_search_index_before_update`: Removes old record data from search index before updates +2. `records_search_index_before_delete`: Removes record data from search index before deletion +3. `records_after_insert`: Inserts new record data into search index after record creation +4. `records_after_update`: Updates record data in search index after record updates + +### Indices + +The following indices are maintained for performance: + +1. On `records`: + - `format` + - `title` + - `musicbrainz_id` + - `purchased_at` + - `included_release_group_ids` + - `release_ids` + +## Notes + +1. The database uses SQLite as the primary database. +2. Artists data is embedded directly in the records table as JSON/map data, rather than having a separate table. +3. The search index is implemented using SQLite's FTS5 extension for efficient full-text search capabilities. +4. Where needed queries use SQLite's `unicode` extension to filter/sort over UTF-8 data. +5. The database supports both collection and wishlist functionality through the `purchased_at` field: + - Records with `purchased_at IS NOT NULL` are in the collection + - Records with `purchased_at IS NULL` are in the wishlist +