METANOIA/schema.sql

190 lines
7.9 KiB
SQL

CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS btree_gin;
CREATE TYPE name_kind AS ENUM ('main', 'original', 'romaji', 'kanji', 'english');
CREATE TYPE artist_kind AS ENUM ('main', 'original', 'performer', 'composer', 'arranger');
-- groups resources in specific releases / groupings
CREATE TABLE releases (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
identifiers TEXT[] NOT NULL DEFAULT '{}'::TEXT[], -- array of "semi-unique" identifiers, like site sources or identifiers
metadata jsonb NOT NULL DEFAULT '{}'::jsonb
);
CREATE INDEX idx_releases_identifiers_gin ON releases USING GIN (identifiers);
CREATE TABLE resources (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
hash bytea UNIQUE NOT NULL, -- hash of the resource, SHA256
size BIGINT NOT NULL,
path bytea, -- can be null if not available locally
mime TEXT NOT NULL
);
CREATE INDEX idx_resources_hash ON resources USING BTREE (hash); -- TODO: check if you can do partial prefix queries
CREATE INDEX idx_resources_path ON resources USING BTREE (path);
CREATE INDEX idx_resources_path_gin ON resources USING GIN (path); -- Allows for partial path queries
CREATE TABLE resource_alternate_identifiers (
resource BIGINT NOT NULL,
name TEXT NOT NULL,
identifier bytea NOT NULL,
CONSTRAINT fk_resource FOREIGN KEY (resource) REFERENCES resources(id),
CONSTRAINT idx_resource_alternate_identifiers UNIQUE (resource, name, identifier)
);
CREATE INDEX idx_resource_alternate_identifiers_resource ON resource_alternate_identifiers USING BTREE (resource) INCLUDE (name, identifier);
CREATE INDEX idx_resource_alternate_identifiers_name_identifier ON resource_alternate_identifiers USING BTREE (name, identifier) INCLUDE (resource);
CREATE TABLE resource_releases (
resource BIGINT NOT NULL,
release BIGINT NOT NULL,
CONSTRAINT fk_resource FOREIGN KEY (resource) REFERENCES resources(id),
CONSTRAINT fk_release FOREIGN KEY (release) REFERENCES releases(id),
CONSTRAINT idx_resource_releases UNIQUE (resource, release)
);
CREATE INDEX idx_resource_releases_resource ON resource_releases USING BTREE (resource) INCLUDE (release);
CREATE INDEX idx_resource_releases_release ON resource_releases USING BTREE (release) INCLUDE (resource);
CREATE TABLE albums (
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
cover BIGINT, -- can be null if not available
identifiers TEXT[] NOT NULL DEFAULT '{}'::TEXT[], -- array of "semi-unique" identifiers, like catalog number
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
CONSTRAINT fk_cover FOREIGN KEY (cover) REFERENCES resources(id)
);
CREATE INDEX idx_albums_identifiers_gin ON albums USING GIN (identifiers);
CREATE TABLE albums_names (
album INTEGER NOT NULL,
kind name_kind NOT NULL,
name TEXT NOT NULL,
CONSTRAINT idx_albums_names UNIQUE (album, kind),
CONSTRAINT fk_album FOREIGN KEY (album) REFERENCES albums(id)
);
CREATE INDEX idx_albums_names_album ON albums_names USING BTREE (album);
CREATE INDEX idx_albums_names_name_gin ON albums_names USING GIN (name gin_trgm_ops);
CREATE TABLE artists (
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
metadata jsonb NOT NULL DEFAULT '{}'::jsonb
);
CREATE TABLE artists_names (
artist INTEGER NOT NULL,
kind name_kind NOT NULL,
name TEXT NOT NULL,
CONSTRAINT idx_artists_names UNIQUE (artist, kind),
CONSTRAINT fk_artist FOREIGN KEY (artist) REFERENCES artists(id)
);
CREATE INDEX idx_artists_names_artist ON artists_names USING BTREE (artist);
CREATE INDEX idx_artists_names_name_gin ON artists_names USING GIN (name gin_trgm_ops);
CREATE TABLE songs (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
resource BIGINT NOT NULL,
cover BIGINT, -- set to override album cover. can be null if not available
album INTEGER, -- can be null
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
CONSTRAINT fk_resource FOREIGN KEY (resource) REFERENCES resources(id),
CONSTRAINT fk_cover FOREIGN KEY (cover) REFERENCES resources(id),
CONSTRAINT fk_album FOREIGN KEY (album) REFERENCES albums(id)
);
CREATE INDEX idx_songs_resource ON songs USING BTREE (resource);
CREATE INDEX idx_songs_album ON songs USING BTREE (album);
CREATE TABLE songs_names (
song INTEGER NOT NULL,
kind name_kind NOT NULL,
name TEXT NOT NULL,
CONSTRAINT idx_songs_names UNIQUE (song, kind),
CONSTRAINT fk_song FOREIGN KEY (song) REFERENCES songs(id)
);
CREATE INDEX idx_songs_names_song ON songs_names USING BTREE (song);
CREATE INDEX idx_songs_names_name_gin ON songs_names USING GIN (name gin_trgm_ops);
-- extra mapping table of artists <-> song
CREATE TABLE album_artists (
album INTEGER NOT NULL,
artist INTEGER NOT NULL,
kind artist_kind NOT NULL,
CONSTRAINT idx_album_artists UNIQUE (album, artist, kind),
CONSTRAINT fk_album FOREIGN KEY (album) REFERENCES albums(id),
CONSTRAINT fk_artist FOREIGN KEY (artist) REFERENCES artists(id)
);
CREATE INDEX idx_album_artists_album ON album_artists USING BTREE (album) INCLUDE (artist, kind);
CREATE INDEX idx_album_artists_artist ON album_artists USING BTREE (artist) INCLUDE (album);
-- CREATE INDEX idx_album_artists_artist_kind ON album_artists (artist, kind);
-- extra mapping table of artists <-> song
CREATE TABLE song_artists (
song INTEGER NOT NULL,
artist INTEGER NOT NULL,
kind artist_kind NOT NULL,
CONSTRAINT idx_song_artists UNIQUE (song, artist, kind),
CONSTRAINT fk_song FOREIGN KEY (song) REFERENCES songs(id),
CONSTRAINT fk_artist FOREIGN KEY (artist) REFERENCES artists(id)
);
CREATE INDEX idx_song_artists_song ON song_artists USING BTREE (song) INCLUDE (artist, kind);
CREATE INDEX idx_song_artists_artist ON song_artists USING BTREE (artist) INCLUDE (song);
-- CREATE INDEX idx_song_artists_artist_kind ON song_artists (artist, kind);
CREATE TABLE tags (
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
name TEXT UNIQUE NOT NULL
);
-- primary key covering index
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS tags_pkey_new ON tags USING BTREE (id) INCLUDE (name);
ALTER TABLE tags DROP CONSTRAINT tags_pkey, ADD CONSTRAINT tags_pkey PRIMARY KEY USING INDEX tags_pkey_new;
CREATE INDEX idx_tags_name ON tags USING BTREE (name) INCLUDE (id);
CREATE TABLE song_taggings (
tag INTEGER NOT NULL,
song BIGINT NOT NULL,
CONSTRAINT fk_tag FOREIGN KEY (tag) REFERENCES tags(id),
CONSTRAINT fk_song FOREIGN KEY (song) REFERENCES songs(id),
CONSTRAINT idx_song_taggings UNIQUE (tag, song)
);
CREATE INDEX idx_song_taggings_tag ON song_taggings USING BTREE (tag) INCLUDE (song);
CREATE INDEX idx_song_taggings_song ON song_taggings USING BTREE (song) INCLUDE (tag);
CREATE TABLE album_taggings (
tag INTEGER NOT NULL,
album INTEGER NOT NULL,
CONSTRAINT fk_tag FOREIGN KEY (tag) REFERENCES tags(id),
CONSTRAINT fk_song FOREIGN KEY (album) REFERENCES albums(id),
CONSTRAINT idx_album_taggings UNIQUE (tag, album)
);
CREATE INDEX idx_album_taggings_tag ON album_taggings USING BTREE (tag) INCLUDE (album);
CREATE INDEX idx_album_taggings_album ON album_taggings USING BTREE (album) INCLUDE (tag);
-- -- Panako fingerprints
-- CREATE TABLE song_fingerprints (
-- song BIGINT NOT NULL,
-- hash INTEGER NOT NULL, -- 32-bit value
-- packed INTEGER NOT NULL, -- 32-bit value, packed Time + Frequency
-- CONSTRAINT idx_song_fingerprints UNIQUE (song, hash, packed),
-- CONSTRAINT fk_song FOREIGN KEY (song) REFERENCES songs(id)
-- );
-- -- TODO: check this again when CLUSTER works for BRIN: CREATE INDEX idx_song_fingerprints_hash ON song_fingerprints USING BRIN (hash) WITH (pages_per_range = 32, autosummarize = on); -- Use BRIN indexes to have small cache size, BTREE would have more overhead on top
-- CREATE INDEX idx_song_fingerprints_hash ON song_fingerprints USING BTREE (hash) WITH (fillfactor = 80); -- lower fillfactor for faster insertions
-- CLUSTER VERBOSE song_fingerprints USING idx_song_fingerprints_hash;