Files
music_library/docs/database_structure.md
T
2025-05-07 16:53:57 +01:00

5.9 KiB

Database Structure

This document describes the database structure of the Music Library application.

Entity Relationship Diagram

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
        string selected_release_id
        datetime purchased_at
        string release_date
        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_date
        map artists
    }

    ARTIST_INFOS {
        uuid id PK
        map musicbrainz_data
        map discogs_data
        blob image_data
        string image_data_hash
        integer image_data_width
        datetime inserted_at
        datetime updated_at
    }

    ARTIST_RECORDS {
        uuid musicbrainz_id
        uuid record_id
        map artist
    }

    RECORDS ||--o{ RECORDS_SEARCH_INDEX : "syncs via triggers"
    RECORDS ||--o{ ARTIST_INFOS : "references via musicbrainz_id"
    RECORDS ||--o{ ARTIST_RECORDS : "extracted via view"

Tables Description

Records

The main table storing music records. Key features:

  • Uses UUID as primary key
  • Stores basic record information (title, type, format)
  • 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
  • Tracks purchase status via purchased_at field
  • Stores release information including multiple release IDs and a selected release ID
  • Maintains release date information

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
  • Updated to use release_date instead of release field

Artist Infos

A table that stores additional artist information:

  • Uses UUID as primary key
  • Stores MusicBrainz and Discogs data for artists
  • Maintains artist image data with dimensions
  • Includes timestamps for record keeping

Views

Artist Records View

A view that extracts artist information from the embedded JSON in the records table:

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)

This view is crucial for querying artist information as it:

  • Extracts individual artists from the embedded JSON array in the records table
  • Provides a normalized view of the artist-record relationships
  • Makes it easier to query records by artist
  • Maintains the relationship between records and their artists without requiring a separate join table

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
  6. The schema has been updated to use release_date instead of release for better clarity
  7. A new selected_release_id field has been added to track the primary release for a record
  8. The artist_infos table has been added to store additional artist metadata and images
  9. The artist_records view provides a normalized way to query artist-record relationships

WHY ONE TABLE?

In traditional relational database design, you would split out artists into a separate table, and associate them with records via a join table. So why sticking with one table?

  1. You only need to backup/export one table.
  2. Re-fetching data from MusicBrainz becomes trivial, as it just needs to update one field and everything else cascades accordingly.
  3. Traditional efficiency design constraints do not apply to SQLite, so it makes it easier to experiment with alternative database designs.