// Code generated by sqlc. DO NOT EDIT. // versions: // sqlc v1.31.1 // source: similarity.sql package dbq import ( "context" "github.com/jackc/pgx/v5/pgtype" ) const deleteSimilarRecordingsForSeed = `-- name: DeleteSimilarRecordingsForSeed :exec DELETE FROM listenbrainz_similar_recordings WHERE seed_track_id = $1 ` // A fetch replaces the seed's cached answer whole. func (q *Queries) DeleteSimilarRecordingsForSeed(ctx context.Context, seedTrackID pgtype.UUID) error { _, err := q.db.Exec(ctx, deleteSimilarRecordingsForSeed, seedTrackID) return err } const getArtistsByMBIDs = `-- name: GetArtistsByMBIDs :many SELECT id, mbid FROM artists WHERE mbid = ANY($1::text[]) ` type GetArtistsByMBIDsRow struct { ID pgtype.UUID Mbid *string } func (q *Queries) GetArtistsByMBIDs(ctx context.Context, dollar_1 []string) ([]GetArtistsByMBIDsRow, error) { rows, err := q.db.Query(ctx, getArtistsByMBIDs, dollar_1) if err != nil { return nil, err } defer rows.Close() var items []GetArtistsByMBIDsRow for rows.Next() { var i GetArtistsByMBIDsRow if err := rows.Scan(&i.ID, &i.Mbid); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const insertSimilarRecordings = `-- name: InsertSimilarRecordings :exec INSERT INTO listenbrainz_similar_recordings (seed_track_id, recording_mbid, score) SELECT $1::uuid, r.mbid, max(r.score) FROM (SELECT unnest($2::text[]) AS mbid, unnest($3::float8[]) AS score) r WHERE r.mbid <> '' GROUP BY r.mbid ` type InsertSimilarRecordingsParams struct { SeedTrackID pgtype.UUID Mbids []string Scores []float64 } // The seed's whole answer, in or out of the library. An MBID LB lists twice // keeps its best score. func (q *Queries) InsertSimilarRecordings(ctx context.Context, arg InsertSimilarRecordingsParams) error { _, err := q.db.Exec(ctx, insertSimilarRecordings, arg.SeedTrackID, arg.Mbids, arg.Scores) return err } const listPlayedArtistsNeedingSimilarity = `-- name: ListPlayedArtistsNeedingSimilarity :many SELECT ar.id, ar.mbid FROM artists ar LEFT JOIN artist_similarity_fetches f ON f.artist_id = ar.id WHERE ar.mbid IS NOT NULL AND EXISTS ( SELECT 1 FROM tracks t JOIN play_events pe ON pe.track_id = t.id WHERE t.artist_id = ar.id ) AND (f.artist_id IS NULL OR f.fetched_at < now() - interval '30 days') ORDER BY f.fetched_at NULLS FIRST, ar.id LIMIT $1 ` type ListPlayedArtistsNeedingSimilarityRow struct { ID pgtype.UUID Mbid *string } // The artist queue, with the same shape and the same reason. func (q *Queries) ListPlayedArtistsNeedingSimilarity(ctx context.Context, limit int32) ([]ListPlayedArtistsNeedingSimilarityRow, error) { rows, err := q.db.Query(ctx, listPlayedArtistsNeedingSimilarity, limit) if err != nil { return nil, err } defer rows.Close() var items []ListPlayedArtistsNeedingSimilarityRow for rows.Next() { var i ListPlayedArtistsNeedingSimilarityRow if err := rows.Scan(&i.ID, &i.Mbid); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listPlayedTracksNeedingSimilarity = `-- name: ListPlayedTracksNeedingSimilarity :many SELECT t.id, t.mbid FROM tracks t LEFT JOIN track_similarity_fetches f ON f.track_id = t.id WHERE t.mbid IS NOT NULL AND t.missing_since IS NULL AND EXISTS (SELECT 1 FROM play_events pe WHERE pe.track_id = t.id) AND (f.track_id IS NULL OR f.fetched_at < now() - interval '30 days') ORDER BY f.fetched_at NULLS FIRST, t.id LIMIT $1 ` type ListPlayedTracksNeedingSimilarityRow struct { ID pgtype.UUID Mbid *string } // The worker's track queue: played, present tracks with a recording MBID that // ListenBrainz has never answered for, or not in 30 days. Never-fetched first, // then the oldest answer. Freshness is read from track_similarity_fetches, not // from the edges, so a seed whose answer matched nothing in the library still // counts as fetched (#5296); reading the edges is what let 25 such seeds hold // the head of the queue forever. func (q *Queries) ListPlayedTracksNeedingSimilarity(ctx context.Context, limit int32) ([]ListPlayedTracksNeedingSimilarityRow, error) { rows, err := q.db.Query(ctx, listPlayedTracksNeedingSimilarity, limit) if err != nil { return nil, err } defer rows.Close() var items []ListPlayedTracksNeedingSimilarityRow for rows.Next() { var i ListPlayedTracksNeedingSimilarityRow if err := rows.Scan(&i.ID, &i.Mbid); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const listSimilarArtistsForArtist = `-- name: ListSimilarArtistsForArtist :many SELECT artists.id, artists.name, artists.sort_name, artists.mbid, artists.created_at, artists.updated_at, artists.artist_thumb_path, artists.artist_fanart_path, artists.artist_art_source, artists.artist_art_sources_version, cov.id AS cover_album_id, cnt.album_count::bigint AS album_count FROM ( SELECT artist_b_id, max(score) AS sim_score FROM artist_similarity WHERE artist_a_id = $1 GROUP BY artist_b_id ) s JOIN artists ON artists.id = s.artist_b_id LEFT JOIN LATERAL ( SELECT id FROM albums WHERE artist_id = artists.id AND cover_art_path IS NOT NULL ORDER BY created_at DESC LIMIT 1 ) cov ON true LEFT JOIN LATERAL ( SELECT count(*) AS album_count FROM albums WHERE artist_id = artists.id ) cnt ON true ORDER BY s.sim_score DESC, artists.sort_name LIMIT $2 ` type ListSimilarArtistsForArtistParams struct { SeedArtistID pgtype.UUID ResultLimit int32 } type ListSimilarArtistsForArtistRow struct { Artist Artist CoverAlbumID pgtype.UUID AlbumCount int64 } // In-library artists similar to a seed artist, ranked by best similarity // score across sources (deduped per candidate). cover_album_id + album_count // mirror ListArtistsAlphaWithCovers so the strip renders identical cards. func (q *Queries) ListSimilarArtistsForArtist(ctx context.Context, arg ListSimilarArtistsForArtistParams) ([]ListSimilarArtistsForArtistRow, error) { rows, err := q.db.Query(ctx, listSimilarArtistsForArtist, arg.SeedArtistID, arg.ResultLimit) if err != nil { return nil, err } defer rows.Close() var items []ListSimilarArtistsForArtistRow for rows.Next() { var i ListSimilarArtistsForArtistRow if err := rows.Scan( &i.Artist.ID, &i.Artist.Name, &i.Artist.SortName, &i.Artist.Mbid, &i.Artist.CreatedAt, &i.Artist.UpdatedAt, &i.Artist.ArtistThumbPath, &i.Artist.ArtistFanartPath, &i.Artist.ArtistArtSource, &i.Artist.ArtistArtSourcesVersion, &i.CoverAlbumID, &i.AlbumCount, ); err != nil { return nil, err } items = append(items, i) } if err := rows.Err(); err != nil { return nil, err } return items, nil } const recordArtistSimilarityFetch = `-- name: RecordArtistSimilarityFetch :exec INSERT INTO artist_similarity_fetches (artist_id, fetched_at, returned) VALUES ($1, now(), $2) ON CONFLICT (artist_id) DO UPDATE SET fetched_at = now(), returned = EXCLUDED.returned ` type RecordArtistSimilarityFetchParams struct { ArtistID pgtype.UUID Returned int32 } func (q *Queries) RecordArtistSimilarityFetch(ctx context.Context, arg RecordArtistSimilarityFetchParams) error { _, err := q.db.Exec(ctx, recordArtistSimilarityFetch, arg.ArtistID, arg.Returned) return err } const recordTrackSimilarityFetch = `-- name: RecordTrackSimilarityFetch :exec INSERT INTO track_similarity_fetches (track_id, fetched_at, returned) VALUES ($1, now(), $2) ON CONFLICT (track_id) DO UPDATE SET fetched_at = now(), returned = EXCLUDED.returned ` type RecordTrackSimilarityFetchParams struct { TrackID pgtype.UUID Returned int32 } func (q *Queries) RecordTrackSimilarityFetch(ctx context.Context, arg RecordTrackSimilarityFetchParams) error { _, err := q.db.Exec(ctx, recordTrackSimilarityFetch, arg.TrackID, arg.Returned) return err } const resolveListenBrainzTrackEdges = `-- name: ResolveListenBrainzTrackEdges :exec WITH want AS ( SELECT DISTINCT ON (c.seed_track_id, c.recording_mbid) c.seed_track_id AS track_a_id, t.id AS track_b_id, c.score FROM listenbrainz_similar_recordings c JOIN tracks t ON t.mbid = c.recording_mbid AND t.missing_since IS NULL AND t.id <> c.seed_track_id WHERE $1::uuid[] IS NULL OR c.seed_track_id = ANY($1::uuid[]) ORDER BY c.seed_track_id, c.recording_mbid, t.id ), upserted AS ( INSERT INTO track_similarity (track_a_id, track_b_id, score, source, fetched_at) SELECT track_a_id, track_b_id, score, 'listenbrainz', now() FROM want ON CONFLICT (track_a_id, track_b_id, source) DO UPDATE SET score = EXCLUDED.score, fetched_at = EXCLUDED.fetched_at WHERE track_similarity.score IS DISTINCT FROM EXCLUDED.score RETURNING 1 ) DELETE FROM track_similarity ts WHERE ts.source = 'listenbrainz' AND ($1::uuid[] IS NULL OR ts.track_a_id = ANY($1::uuid[])) AND EXISTS (SELECT 1 FROM track_similarity_fetches f WHERE f.track_id = ts.track_a_id) AND NOT EXISTS ( SELECT 1 FROM want w WHERE w.track_a_id = ts.track_a_id AND w.track_b_id = ts.track_b_id ) ` // Derives the listenbrainz edges in track_similarity from the cached answers: // every cached recording that is a present track in the library becomes an // edge, whenever it got there. Scoped to the given seeds, or every seed when // seed_ids is NULL. // // One track per recording MBID: two copies of a song (an mp3 beside its flac, // or one recording on two releases) would otherwise be two candidates, and the // pool would offer the same song twice. No cap per seed: LB's answer is already // at most 50. // // Only edges of seeds the cache owns are removed, those with a fetch row. // Edges written before the cache existed, or copied onto a survivor by a fold // (merge.sql), stay until their seed is fetched. func (q *Queries) ResolveListenBrainzTrackEdges(ctx context.Context, seedIds []pgtype.UUID) error { _, err := q.db.Exec(ctx, resolveListenBrainzTrackEdges, seedIds) return err } const upsertArtistSimilarity = `-- name: UpsertArtistSimilarity :exec INSERT INTO artist_similarity (artist_a_id, artist_b_id, score, source, fetched_at) VALUES ($1, $2, $3, 'listenbrainz', now()) ON CONFLICT (artist_a_id, artist_b_id, source) DO UPDATE SET score = EXCLUDED.score, fetched_at = EXCLUDED.fetched_at ` type UpsertArtistSimilarityParams struct { ArtistAID pgtype.UUID ArtistBID pgtype.UUID Score float64 } func (q *Queries) UpsertArtistSimilarity(ctx context.Context, arg UpsertArtistSimilarityParams) error { _, err := q.db.Exec(ctx, upsertArtistSimilarity, arg.ArtistAID, arg.ArtistBID, arg.Score) return err } const upsertArtistSimilarityUnmatched = `-- name: UpsertArtistSimilarityUnmatched :exec INSERT INTO artist_similarity_unmatched ( seed_artist_id, candidate_mbid, candidate_name, score, source ) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (seed_artist_id, candidate_mbid, source) DO UPDATE SET candidate_name = EXCLUDED.candidate_name, score = EXCLUDED.score, fetched_at = now() ` type UpsertArtistSimilarityUnmatchedParams struct { SeedArtistID pgtype.UUID CandidateMbid string CandidateName string Score float64 Source string } // Persists an out-of-library similar-artist MBID. Idempotent on // (seed_artist_id, candidate_mbid, source) — re-fetches refresh the // name/score and bump fetched_at. func (q *Queries) UpsertArtistSimilarityUnmatched(ctx context.Context, arg UpsertArtistSimilarityUnmatchedParams) error { _, err := q.db.Exec(ctx, upsertArtistSimilarityUnmatched, arg.SeedArtistID, arg.CandidateMbid, arg.CandidateName, arg.Score, arg.Source, ) return err }