fix(db): split seed_info CTE so similar-artist works without seed genre
The combined seed_info CTE filtered the seed out when its genre was NULL/empty. Restored the spec's two-CTE design (seed_artist + seed_tags) so similar-artist matching works regardless of the seed's genre coverage. Drops the genre workaround from the SimilarArtist test.
This commit is contained in:
@@ -100,9 +100,13 @@ func (q *Queries) LoadRadioCandidates(ctx context.Context, arg LoadRadioCandidat
|
|||||||
const loadRadioCandidatesV2 = `-- name: LoadRadioCandidatesV2 :many
|
const loadRadioCandidatesV2 = `-- name: LoadRadioCandidatesV2 :many
|
||||||
|
|
||||||
WITH
|
WITH
|
||||||
seed_info AS (
|
seed_artist AS (
|
||||||
SELECT t.artist_id,
|
SELECT artist_id
|
||||||
trim(g)::text AS tag
|
FROM tracks
|
||||||
|
WHERE tracks.id = $2
|
||||||
|
),
|
||||||
|
seed_tags AS (
|
||||||
|
SELECT trim(g) AS tag
|
||||||
FROM tracks t
|
FROM tracks t
|
||||||
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g(tag) ON true
|
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g(tag) ON true
|
||||||
WHERE t.id = $2 AND trim(g) <> ''
|
WHERE t.id = $2 AND trim(g) <> ''
|
||||||
@@ -127,7 +131,7 @@ similar_artists AS (
|
|||||||
SELECT t.id AS track_id, asim.score * 0.5 AS sim_score
|
SELECT t.id AS track_id, asim.score * 0.5 AS sim_score
|
||||||
FROM artist_similarity asim
|
FROM artist_similarity asim
|
||||||
JOIN tracks t ON t.artist_id = asim.artist_b_id
|
JOIN tracks t ON t.artist_id = asim.artist_b_id
|
||||||
JOIN (SELECT DISTINCT artist_id FROM seed_info) sa ON asim.artist_a_id = sa.artist_id
|
JOIN seed_artist sa ON asim.artist_a_id = sa.artist_id
|
||||||
WHERE asim.source = 'listenbrainz'
|
WHERE asim.source = 'listenbrainz'
|
||||||
AND t.id NOT IN (SELECT id FROM excluded_ids)
|
AND t.id NOT IN (SELECT id FROM excluded_ids)
|
||||||
ORDER BY asim.score DESC, random()
|
ORDER BY asim.score DESC, random()
|
||||||
@@ -136,10 +140,10 @@ similar_artists AS (
|
|||||||
tag_overlap AS (
|
tag_overlap AS (
|
||||||
SELECT t.id AS track_id,
|
SELECT t.id AS track_id,
|
||||||
(count(DISTINCT trim(g))::float8
|
(count(DISTINCT trim(g))::float8
|
||||||
/ GREATEST((SELECT count(DISTINCT tag) FROM seed_info), 1)) AS sim_score
|
/ GREATEST((SELECT count(*) FROM seed_tags), 1)) AS sim_score
|
||||||
FROM tracks t
|
FROM tracks t
|
||||||
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g_split(g) ON true
|
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g_split(g) ON true
|
||||||
WHERE trim(g_split.g) IN (SELECT tag FROM seed_info)
|
WHERE trim(g_split.g) IN (SELECT tag FROM seed_tags)
|
||||||
AND t.id NOT IN (SELECT id FROM excluded_ids)
|
AND t.id NOT IN (SELECT id FROM excluded_ids)
|
||||||
AND t.id <> $2
|
AND t.id <> $2
|
||||||
GROUP BY t.id
|
GROUP BY t.id
|
||||||
@@ -156,7 +160,7 @@ likes_overlap AS (
|
|||||||
SELECT 1 FROM tracks t
|
SELECT 1 FROM tracks t
|
||||||
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g_overlap(g) ON true
|
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g_overlap(g) ON true
|
||||||
WHERE t.id = gl.track_id
|
WHERE t.id = gl.track_id
|
||||||
AND trim(g_overlap.g) IN (SELECT tag FROM seed_info)
|
AND trim(g_overlap.g) IN (SELECT tag FROM seed_tags)
|
||||||
)
|
)
|
||||||
ORDER BY random()
|
ORDER BY random()
|
||||||
LIMIT $8
|
LIMIT $8
|
||||||
|
|||||||
@@ -36,9 +36,13 @@ WHERE t.id <> $2
|
|||||||
-- Returns same shape as LoadRadioCandidates plus similarity_score column.
|
-- Returns same shape as LoadRadioCandidates plus similarity_score column.
|
||||||
|
|
||||||
WITH
|
WITH
|
||||||
seed_info AS (
|
seed_artist AS (
|
||||||
SELECT t.artist_id,
|
SELECT artist_id
|
||||||
trim(g)::text AS tag
|
FROM tracks
|
||||||
|
WHERE tracks.id = $2
|
||||||
|
),
|
||||||
|
seed_tags AS (
|
||||||
|
SELECT trim(g) AS tag
|
||||||
FROM tracks t
|
FROM tracks t
|
||||||
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g(tag) ON true
|
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g(tag) ON true
|
||||||
WHERE t.id = $2 AND trim(g) <> ''
|
WHERE t.id = $2 AND trim(g) <> ''
|
||||||
@@ -63,7 +67,7 @@ similar_artists AS (
|
|||||||
SELECT t.id AS track_id, asim.score * 0.5 AS sim_score
|
SELECT t.id AS track_id, asim.score * 0.5 AS sim_score
|
||||||
FROM artist_similarity asim
|
FROM artist_similarity asim
|
||||||
JOIN tracks t ON t.artist_id = asim.artist_b_id
|
JOIN tracks t ON t.artist_id = asim.artist_b_id
|
||||||
JOIN (SELECT DISTINCT artist_id FROM seed_info) sa ON asim.artist_a_id = sa.artist_id
|
JOIN seed_artist sa ON asim.artist_a_id = sa.artist_id
|
||||||
WHERE asim.source = 'listenbrainz'
|
WHERE asim.source = 'listenbrainz'
|
||||||
AND t.id NOT IN (SELECT id FROM excluded_ids)
|
AND t.id NOT IN (SELECT id FROM excluded_ids)
|
||||||
ORDER BY asim.score DESC, random()
|
ORDER BY asim.score DESC, random()
|
||||||
@@ -72,10 +76,10 @@ similar_artists AS (
|
|||||||
tag_overlap AS (
|
tag_overlap AS (
|
||||||
SELECT t.id AS track_id,
|
SELECT t.id AS track_id,
|
||||||
(count(DISTINCT trim(g))::float8
|
(count(DISTINCT trim(g))::float8
|
||||||
/ GREATEST((SELECT count(DISTINCT tag) FROM seed_info), 1)) AS sim_score
|
/ GREATEST((SELECT count(*) FROM seed_tags), 1)) AS sim_score
|
||||||
FROM tracks t
|
FROM tracks t
|
||||||
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g_split(g) ON true
|
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g_split(g) ON true
|
||||||
WHERE trim(g_split.g) IN (SELECT tag FROM seed_info)
|
WHERE trim(g_split.g) IN (SELECT tag FROM seed_tags)
|
||||||
AND t.id NOT IN (SELECT id FROM excluded_ids)
|
AND t.id NOT IN (SELECT id FROM excluded_ids)
|
||||||
AND t.id <> $2
|
AND t.id <> $2
|
||||||
GROUP BY t.id
|
GROUP BY t.id
|
||||||
@@ -92,7 +96,7 @@ likes_overlap AS (
|
|||||||
SELECT 1 FROM tracks t
|
SELECT 1 FROM tracks t
|
||||||
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g_overlap(g) ON true
|
LEFT JOIN LATERAL regexp_split_to_table(coalesce(t.genre, ''), '[;,]') AS g_overlap(g) ON true
|
||||||
WHERE t.id = gl.track_id
|
WHERE t.id = gl.track_id
|
||||||
AND trim(g_overlap.g) IN (SELECT tag FROM seed_info)
|
AND trim(g_overlap.g) IN (SELECT tag FROM seed_tags)
|
||||||
)
|
)
|
||||||
ORDER BY random()
|
ORDER BY random()
|
||||||
LIMIT $8
|
LIMIT $8
|
||||||
|
|||||||
@@ -73,11 +73,6 @@ func TestLoadCandidatesFromSimilarity_LBSimilarSourceContributes(t *testing.T) {
|
|||||||
func TestLoadCandidatesFromSimilarity_SimilarArtistTracksContribute(t *testing.T) {
|
func TestLoadCandidatesFromSimilarity_SimilarArtistTracksContribute(t *testing.T) {
|
||||||
f := newFixture(t, 1) // creates 1 artist + 1 album + 1 track (the seed)
|
f := newFixture(t, 1) // creates 1 artist + 1 album + 1 track (the seed)
|
||||||
seed := f.tracks[0]
|
seed := f.tracks[0]
|
||||||
// seed_info CTE only emits artist_id rows when genre is non-NULL (it
|
|
||||||
// uses a LATERAL regexp_split_to_table join filtered by trim(g) <> '').
|
|
||||||
// Give the seed a genre so that seed_info is populated and the
|
|
||||||
// similar_artists JOIN can find artist_a_id = seed.ArtistID.
|
|
||||||
helperSetTrackGenre(t, f, seed.ID, "Rock")
|
|
||||||
// Add a SECOND artist + track in that artist; relate the two artists via artist_similarity.
|
// Add a SECOND artist + track in that artist; relate the two artists via artist_similarity.
|
||||||
otherArtist, _ := f.q.UpsertArtist(context.Background(), dbq.UpsertArtistParams{Name: "OtherArtist", SortName: "OtherArtist"})
|
otherArtist, _ := f.q.UpsertArtist(context.Background(), dbq.UpsertArtistParams{Name: "OtherArtist", SortName: "OtherArtist"})
|
||||||
otherAlbum, _ := f.q.UpsertAlbum(context.Background(), dbq.UpsertAlbumParams{Title: "OtherAlbum", SortTitle: "OtherAlbum", ArtistID: otherArtist.ID})
|
otherAlbum, _ := f.q.UpsertAlbum(context.Background(), dbq.UpsertAlbumParams{Title: "OtherAlbum", SortTitle: "OtherAlbum", ArtistID: otherArtist.ID})
|
||||||
|
|||||||
Reference in New Issue
Block a user