Discover request surface — taste-aware, rotating, snoozable, tag-targeted (milestone #268) #116
@@ -1022,22 +1022,6 @@ func (q *Queries) LoadRadioCandidatesV2(ctx context.Context, arg LoadRadioCandid
|
|||||||
}
|
}
|
||||||
|
|
||||||
const suggestArtistsForUser = `-- name: SuggestArtistsForUser :many
|
const suggestArtistsForUser = `-- name: SuggestArtistsForUser :many
|
||||||
-- Per-user artist suggestions ranked by taste signal x similarity, projected
|
|
||||||
-- through artist_similarity_unmatched (out-of-library candidates only).
|
|
||||||
--
|
|
||||||
-- Seeds are TIERED (rule #131) so the surface never empties:
|
|
||||||
-- tier 1 - taste_profile_artists.weight: engagement-graded, time-decayed and
|
|
||||||
-- SIGNED by internal/taste, so an artist the user has drifted away
|
|
||||||
-- from stops contributing instead of accumulating forever.
|
|
||||||
-- tier 2 - likes + completed plays, used ONLY when the profile has no rows
|
|
||||||
-- (new account, or before the first daily recompute).
|
|
||||||
--
|
|
||||||
-- The signal is log-damped: contribution is signal x similarity, and the old
|
|
||||||
-- undamped sum let one heavily-played artist's neighbours take every slot --
|
|
||||||
-- entrenching harder the MORE the user listened (issue #2367 mechanism 2).
|
|
||||||
--
|
|
||||||
-- Candidates already in the library, or already requested and not terminal,
|
|
||||||
-- are excluded. $1=user_id, $2=half_life_days (tier 2 decay), $3=limit.
|
|
||||||
WITH artist_plays AS (
|
WITH artist_plays AS (
|
||||||
-- Completed plays only. The previous seed query counted every play_event,
|
-- Completed plays only. The previous seed query counted every play_event,
|
||||||
-- so skipping an artist repeatedly INCREASED its signal and pushed more of
|
-- so skipping an artist repeatedly INCREASED its signal and pushed more of
|
||||||
@@ -1128,13 +1112,23 @@ type SuggestArtistsForUserRow struct {
|
|||||||
TopPlayCounts []int64
|
TopPlayCounts []int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// M5c: per-user artist suggestions ranked by signal x similarity. The
|
// Per-user artist suggestions ranked by taste signal x similarity, projected
|
||||||
// seeds CTE collects the user's likes (x5) plus recency-decayed plays
|
// through artist_similarity_unmatched (out-of-library candidates only).
|
||||||
// (exp(-age_days / $2)). The contributions CTE joins those seeds against
|
//
|
||||||
// artist_similarity_unmatched and filters out candidates already in
|
// Seeds are TIERED (rule #131) so the surface never empties:
|
||||||
// library or already in a non-terminal lidarr_request. The outer SELECT
|
//
|
||||||
// aggregates per candidate, returning the top-3 contributing seeds for
|
// tier 1 - taste_profile_artists.weight: engagement-graded, time-decayed and
|
||||||
// attribution. $1=user_id, $2=half_life_days, $3=limit.
|
// SIGNED by internal/taste, so an artist the user has drifted away
|
||||||
|
// from stops contributing instead of accumulating forever.
|
||||||
|
// tier 2 - likes + completed plays, used ONLY when the profile has no rows
|
||||||
|
// (new account, or before the first daily recompute).
|
||||||
|
//
|
||||||
|
// The signal is log-damped: contribution is signal x similarity, and the old
|
||||||
|
// undamped sum let one heavily-played artist's neighbours take every slot --
|
||||||
|
// entrenching harder the MORE the user listened (issue #2367 mechanism 2).
|
||||||
|
//
|
||||||
|
// Candidates already in the library, or already requested and not terminal,
|
||||||
|
// are excluded. $1=user_id, $2=half_life_days (tier 2 decay), $3=limit.
|
||||||
func (q *Queries) SuggestArtistsForUser(ctx context.Context, arg SuggestArtistsForUserParams) ([]SuggestArtistsForUserRow, error) {
|
func (q *Queries) SuggestArtistsForUser(ctx context.Context, arg SuggestArtistsForUserParams) ([]SuggestArtistsForUserRow, error) {
|
||||||
rows, err := q.db.Query(ctx, suggestArtistsForUser, arg.UserID, arg.Column2, arg.Limit)
|
rows, err := q.db.Query(ctx, suggestArtistsForUser, arg.UserID, arg.Column2, arg.Limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user