feat(db): add M2 likes schema (general_likes, _albums, _artists)
Three tables keyed on (user_id, entity_id) with liked_at. Per-table indexes on (user_id, liked_at DESC) for the recently-liked feed. sqlc queries cover like/unlike/list-rows/count/list-ids per entity.
This commit is contained in:
@@ -0,0 +1,342 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.31.1
|
||||||
|
// source: likes.sql
|
||||||
|
|
||||||
|
package dbq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
const countLikedAlbums = `-- name: CountLikedAlbums :one
|
||||||
|
SELECT count(*) FROM general_likes_albums WHERE user_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountLikedAlbums(ctx context.Context, userID pgtype.UUID) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countLikedAlbums, userID)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const countLikedArtists = `-- name: CountLikedArtists :one
|
||||||
|
SELECT count(*) FROM general_likes_artists WHERE user_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountLikedArtists(ctx context.Context, userID pgtype.UUID) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countLikedArtists, userID)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const countLikedTracks = `-- name: CountLikedTracks :one
|
||||||
|
SELECT count(*) FROM general_likes WHERE user_id = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) CountLikedTracks(ctx context.Context, userID pgtype.UUID) (int64, error) {
|
||||||
|
row := q.db.QueryRow(ctx, countLikedTracks, userID)
|
||||||
|
var count int64
|
||||||
|
err := row.Scan(&count)
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const likeAlbum = `-- name: LikeAlbum :exec
|
||||||
|
INSERT INTO general_likes_albums (user_id, album_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (user_id, album_id) DO NOTHING
|
||||||
|
`
|
||||||
|
|
||||||
|
type LikeAlbumParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
AlbumID pgtype.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) LikeAlbum(ctx context.Context, arg LikeAlbumParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, likeAlbum, arg.UserID, arg.AlbumID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const likeArtist = `-- name: LikeArtist :exec
|
||||||
|
INSERT INTO general_likes_artists (user_id, artist_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (user_id, artist_id) DO NOTHING
|
||||||
|
`
|
||||||
|
|
||||||
|
type LikeArtistParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
ArtistID pgtype.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) LikeArtist(ctx context.Context, arg LikeArtistParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, likeArtist, arg.UserID, arg.ArtistID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const likeTrack = `-- name: LikeTrack :exec
|
||||||
|
INSERT INTO general_likes (user_id, track_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (user_id, track_id) DO NOTHING
|
||||||
|
`
|
||||||
|
|
||||||
|
type LikeTrackParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
TrackID pgtype.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) LikeTrack(ctx context.Context, arg LikeTrackParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, likeTrack, arg.UserID, arg.TrackID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const listLikedAlbumIDs = `-- name: ListLikedAlbumIDs :many
|
||||||
|
SELECT album_id FROM general_likes_albums WHERE user_id = $1 ORDER BY liked_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) ListLikedAlbumIDs(ctx context.Context, userID pgtype.UUID) ([]pgtype.UUID, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listLikedAlbumIDs, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []pgtype.UUID
|
||||||
|
for rows.Next() {
|
||||||
|
var album_id pgtype.UUID
|
||||||
|
if err := rows.Scan(&album_id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, album_id)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listLikedAlbumRows = `-- name: ListLikedAlbumRows :many
|
||||||
|
SELECT a.id, a.title, a.sort_title, a.artist_id, a.release_date, a.mbid, a.cover_art_path, a.created_at, a.updated_at FROM albums a
|
||||||
|
JOIN general_likes_albums l ON l.album_id = a.id
|
||||||
|
WHERE l.user_id = $1
|
||||||
|
ORDER BY l.liked_at DESC
|
||||||
|
LIMIT $2 OFFSET $3
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListLikedAlbumRowsParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
Limit int32
|
||||||
|
Offset int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListLikedAlbumRows(ctx context.Context, arg ListLikedAlbumRowsParams) ([]Album, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listLikedAlbumRows, arg.UserID, arg.Limit, arg.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Album
|
||||||
|
for rows.Next() {
|
||||||
|
var i Album
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Title,
|
||||||
|
&i.SortTitle,
|
||||||
|
&i.ArtistID,
|
||||||
|
&i.ReleaseDate,
|
||||||
|
&i.Mbid,
|
||||||
|
&i.CoverArtPath,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listLikedArtistIDs = `-- name: ListLikedArtistIDs :many
|
||||||
|
SELECT artist_id FROM general_likes_artists WHERE user_id = $1 ORDER BY liked_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) ListLikedArtistIDs(ctx context.Context, userID pgtype.UUID) ([]pgtype.UUID, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listLikedArtistIDs, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []pgtype.UUID
|
||||||
|
for rows.Next() {
|
||||||
|
var artist_id pgtype.UUID
|
||||||
|
if err := rows.Scan(&artist_id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, artist_id)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listLikedArtistRows = `-- name: ListLikedArtistRows :many
|
||||||
|
SELECT a.id, a.name, a.sort_name, a.mbid, a.created_at, a.updated_at FROM artists a
|
||||||
|
JOIN general_likes_artists l ON l.artist_id = a.id
|
||||||
|
WHERE l.user_id = $1
|
||||||
|
ORDER BY l.liked_at DESC
|
||||||
|
LIMIT $2 OFFSET $3
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListLikedArtistRowsParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
Limit int32
|
||||||
|
Offset int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListLikedArtistRows(ctx context.Context, arg ListLikedArtistRowsParams) ([]Artist, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listLikedArtistRows, arg.UserID, arg.Limit, arg.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Artist
|
||||||
|
for rows.Next() {
|
||||||
|
var i Artist
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Name,
|
||||||
|
&i.SortName,
|
||||||
|
&i.Mbid,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listLikedTrackIDs = `-- name: ListLikedTrackIDs :many
|
||||||
|
SELECT track_id FROM general_likes WHERE user_id = $1 ORDER BY liked_at DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) ListLikedTrackIDs(ctx context.Context, userID pgtype.UUID) ([]pgtype.UUID, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listLikedTrackIDs, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []pgtype.UUID
|
||||||
|
for rows.Next() {
|
||||||
|
var track_id pgtype.UUID
|
||||||
|
if err := rows.Scan(&track_id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, track_id)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const listLikedTrackRows = `-- name: ListLikedTrackRows :many
|
||||||
|
SELECT t.id, t.title, t.album_id, t.artist_id, t.track_number, t.disc_number, t.duration_ms, t.file_path, t.file_size, t.file_format, t.bitrate, t.mbid, t.genre, t.added_at, t.updated_at FROM tracks t
|
||||||
|
JOIN general_likes l ON l.track_id = t.id
|
||||||
|
WHERE l.user_id = $1
|
||||||
|
ORDER BY l.liked_at DESC
|
||||||
|
LIMIT $2 OFFSET $3
|
||||||
|
`
|
||||||
|
|
||||||
|
type ListLikedTrackRowsParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
Limit int32
|
||||||
|
Offset int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ListLikedTrackRows(ctx context.Context, arg ListLikedTrackRowsParams) ([]Track, error) {
|
||||||
|
rows, err := q.db.Query(ctx, listLikedTrackRows, arg.UserID, arg.Limit, arg.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Track
|
||||||
|
for rows.Next() {
|
||||||
|
var i Track
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Title,
|
||||||
|
&i.AlbumID,
|
||||||
|
&i.ArtistID,
|
||||||
|
&i.TrackNumber,
|
||||||
|
&i.DiscNumber,
|
||||||
|
&i.DurationMs,
|
||||||
|
&i.FilePath,
|
||||||
|
&i.FileSize,
|
||||||
|
&i.FileFormat,
|
||||||
|
&i.Bitrate,
|
||||||
|
&i.Mbid,
|
||||||
|
&i.Genre,
|
||||||
|
&i.AddedAt,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const unlikeAlbum = `-- name: UnlikeAlbum :exec
|
||||||
|
DELETE FROM general_likes_albums WHERE user_id = $1 AND album_id = $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type UnlikeAlbumParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
AlbumID pgtype.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UnlikeAlbum(ctx context.Context, arg UnlikeAlbumParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, unlikeAlbum, arg.UserID, arg.AlbumID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const unlikeArtist = `-- name: UnlikeArtist :exec
|
||||||
|
DELETE FROM general_likes_artists WHERE user_id = $1 AND artist_id = $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type UnlikeArtistParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
ArtistID pgtype.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UnlikeArtist(ctx context.Context, arg UnlikeArtistParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, unlikeArtist, arg.UserID, arg.ArtistID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const unlikeTrack = `-- name: UnlikeTrack :exec
|
||||||
|
DELETE FROM general_likes WHERE user_id = $1 AND track_id = $2
|
||||||
|
`
|
||||||
|
|
||||||
|
type UnlikeTrackParams struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
TrackID pgtype.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UnlikeTrack(ctx context.Context, arg UnlikeTrackParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, unlikeTrack, arg.UserID, arg.TrackID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -29,6 +29,24 @@ type Artist struct {
|
|||||||
UpdatedAt pgtype.Timestamptz
|
UpdatedAt pgtype.Timestamptz
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GeneralLike struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
TrackID pgtype.UUID
|
||||||
|
LikedAt pgtype.Timestamptz
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeneralLikesAlbum struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
AlbumID pgtype.UUID
|
||||||
|
LikedAt pgtype.Timestamptz
|
||||||
|
}
|
||||||
|
|
||||||
|
type GeneralLikesArtist struct {
|
||||||
|
UserID pgtype.UUID
|
||||||
|
ArtistID pgtype.UUID
|
||||||
|
LikedAt pgtype.Timestamptz
|
||||||
|
}
|
||||||
|
|
||||||
type PlayEvent struct {
|
type PlayEvent struct {
|
||||||
ID pgtype.UUID
|
ID pgtype.UUID
|
||||||
UserID pgtype.UUID
|
UserID pgtype.UUID
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
DROP TABLE IF EXISTS general_likes_artists;
|
||||||
|
DROP TABLE IF EXISTS general_likes_albums;
|
||||||
|
DROP TABLE IF EXISTS general_likes;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
-- M2 likes: three tables, one per entity type. Schema follows the spec §5
|
||||||
|
-- pattern (general_likes was originally track-only); this slice extends it
|
||||||
|
-- to albums and artists for full Subsonic star/unstar support.
|
||||||
|
|
||||||
|
CREATE TABLE general_likes (
|
||||||
|
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
track_id uuid NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
|
||||||
|
liked_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
PRIMARY KEY (user_id, track_id)
|
||||||
|
);
|
||||||
|
CREATE INDEX general_likes_user_liked_at_idx ON general_likes (user_id, liked_at DESC);
|
||||||
|
|
||||||
|
CREATE TABLE general_likes_albums (
|
||||||
|
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
album_id uuid NOT NULL REFERENCES albums(id) ON DELETE CASCADE,
|
||||||
|
liked_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
PRIMARY KEY (user_id, album_id)
|
||||||
|
);
|
||||||
|
CREATE INDEX general_likes_albums_user_liked_at_idx ON general_likes_albums (user_id, liked_at DESC);
|
||||||
|
|
||||||
|
CREATE TABLE general_likes_artists (
|
||||||
|
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
artist_id uuid NOT NULL REFERENCES artists(id) ON DELETE CASCADE,
|
||||||
|
liked_at timestamptz NOT NULL DEFAULT now(),
|
||||||
|
PRIMARY KEY (user_id, artist_id)
|
||||||
|
);
|
||||||
|
CREATE INDEX general_likes_artists_user_liked_at_idx ON general_likes_artists (user_id, liked_at DESC);
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
-- name: LikeTrack :exec
|
||||||
|
INSERT INTO general_likes (user_id, track_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (user_id, track_id) DO NOTHING;
|
||||||
|
|
||||||
|
-- name: UnlikeTrack :exec
|
||||||
|
DELETE FROM general_likes WHERE user_id = $1 AND track_id = $2;
|
||||||
|
|
||||||
|
-- name: ListLikedTrackRows :many
|
||||||
|
SELECT t.* FROM tracks t
|
||||||
|
JOIN general_likes l ON l.track_id = t.id
|
||||||
|
WHERE l.user_id = $1
|
||||||
|
ORDER BY l.liked_at DESC
|
||||||
|
LIMIT $2 OFFSET $3;
|
||||||
|
|
||||||
|
-- name: CountLikedTracks :one
|
||||||
|
SELECT count(*) FROM general_likes WHERE user_id = $1;
|
||||||
|
|
||||||
|
-- name: ListLikedTrackIDs :many
|
||||||
|
SELECT track_id FROM general_likes WHERE user_id = $1 ORDER BY liked_at DESC;
|
||||||
|
|
||||||
|
-- name: LikeAlbum :exec
|
||||||
|
INSERT INTO general_likes_albums (user_id, album_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (user_id, album_id) DO NOTHING;
|
||||||
|
|
||||||
|
-- name: UnlikeAlbum :exec
|
||||||
|
DELETE FROM general_likes_albums WHERE user_id = $1 AND album_id = $2;
|
||||||
|
|
||||||
|
-- name: ListLikedAlbumRows :many
|
||||||
|
SELECT a.* FROM albums a
|
||||||
|
JOIN general_likes_albums l ON l.album_id = a.id
|
||||||
|
WHERE l.user_id = $1
|
||||||
|
ORDER BY l.liked_at DESC
|
||||||
|
LIMIT $2 OFFSET $3;
|
||||||
|
|
||||||
|
-- name: CountLikedAlbums :one
|
||||||
|
SELECT count(*) FROM general_likes_albums WHERE user_id = $1;
|
||||||
|
|
||||||
|
-- name: ListLikedAlbumIDs :many
|
||||||
|
SELECT album_id FROM general_likes_albums WHERE user_id = $1 ORDER BY liked_at DESC;
|
||||||
|
|
||||||
|
-- name: LikeArtist :exec
|
||||||
|
INSERT INTO general_likes_artists (user_id, artist_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (user_id, artist_id) DO NOTHING;
|
||||||
|
|
||||||
|
-- name: UnlikeArtist :exec
|
||||||
|
DELETE FROM general_likes_artists WHERE user_id = $1 AND artist_id = $2;
|
||||||
|
|
||||||
|
-- name: ListLikedArtistRows :many
|
||||||
|
SELECT a.* FROM artists a
|
||||||
|
JOIN general_likes_artists l ON l.artist_id = a.id
|
||||||
|
WHERE l.user_id = $1
|
||||||
|
ORDER BY l.liked_at DESC
|
||||||
|
LIMIT $2 OFFSET $3;
|
||||||
|
|
||||||
|
-- name: CountLikedArtists :one
|
||||||
|
SELECT count(*) FROM general_likes_artists WHERE user_id = $1;
|
||||||
|
|
||||||
|
-- name: ListLikedArtistIDs :many
|
||||||
|
SELECT artist_id FROM general_likes_artists WHERE user_id = $1 ORDER BY liked_at DESC;
|
||||||
Reference in New Issue
Block a user