Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions api/v1_users_listen_counts_monthly.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ func (app *ApiServer) v1UsersListenCountsMonthly(c *fiber.Ctx) error {
return err
}

userId := app.getUserId(c)

// See v1_users_tracks.go: fold the (usually empty) collaborator set in via an
// explicit id array only when present, so the common case keeps its owner-only
// plan rather than seq-scanning tracks.
collabRows, err := app.pool.Query(c.Context(),
`SELECT track_id FROM track_collaborators WHERE collaborator_user_id = $1 AND status = 'accepted'`,
userId)
if err != nil {
return err
}
collabTrackIds, err := pgx.CollectRows(collabRows, pgx.RowTo[int32])
if err != nil {
return err
}

ownerFilter := "owner_id = @userId"
if len(collabTrackIds) > 0 {
ownerFilter = "(owner_id = @userId OR track_id = ANY(@collab_track_ids))"
}

sql := `
SELECT
play_item_id,
Expand All @@ -26,9 +47,7 @@ func (app *ApiServer) v1UsersListenCountsMonthly(c *fiber.Ctx) error {
FROM aggregate_monthly_plays
WHERE play_item_id IN (
SELECT track_id FROM tracks WHERE stem_of IS NULL
AND (owner_id = @userId
OR track_id IN (SELECT track_id FROM track_collaborators
WHERE collaborator_user_id = @userId AND status = 'accepted'))
AND ` + ownerFilter + `
AND (access_authorities IS NULL
OR (COALESCE(@authed_wallet, '') <> ''
AND EXISTS (SELECT 1 FROM unnest(access_authorities) aa WHERE lower(aa) = lower(@authed_wallet))))
Expand All @@ -39,12 +58,17 @@ func (app *ApiServer) v1UsersListenCountsMonthly(c *fiber.Ctx) error {
;
`

rows, err := app.pool.Query(c.Context(), sql, pgx.NamedArgs{
"userId": app.getUserId(c),
args := pgx.NamedArgs{
"userId": userId,
"startTime": params.StartTime,
"endTime": params.EndTime,
"authed_wallet": app.tryGetAuthedWallet(c),
})
}
if len(collabTrackIds) > 0 {
args["collab_track_ids"] = collabTrackIds
}

rows, err := app.pool.Query(c.Context(), sql, args)
if err != nil {
return err
}
Expand Down
42 changes: 36 additions & 6 deletions api/v1_users_tracks.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,46 @@ func (app *ApiServer) v1UserTracks(c *fiber.Ctx) error {
gateConditions := queryMulti(c, "gate_condition")
gateFilter := buildGateConditionFilter(gateConditions)

// Fetch the user's accepted-collaboration track ids up front. This is a
// small, index-only lookup (covering index on
// track_collaborators(collaborator_user_id, status, track_id)). For the
// overwhelming majority of users it returns nothing, which lets us run the
// original owner-only query with its original plan. Folding the collaborator
// set into the main WHERE as `OR t.track_id IN (subquery)` instead made
// Postgres sequential-scan the entire tracks table on every profile load.
collabRows, err := app.pool.Query(c.Context(),
`SELECT track_id FROM track_collaborators WHERE collaborator_user_id = $1 AND status = 'accepted'`,
userId)
if err != nil {
return err
}
collabTrackIds, err := pgx.CollectRows(collabRows, pgx.RowTo[int32])
if err != nil {
return err
}

// Default (no collaborations): identical to the original owner-only query —
// `u` is the profile user, so the artist pick comes from the join.
ownerFilter := "t.owner_id = @user_id"
pinExpr := "t.track_id = u.artist_pick_track_id"
if len(collabTrackIds) > 0 {
// Use an explicit id array (plans far better than a correlated subquery,
// via a bitmap OR of the owner_id index and the track_id PK). `u` may be
// another owner for collab tracks, so the pin references the profile user.
ownerFilter = "(t.owner_id = @user_id OR t.track_id = ANY(@collab_track_ids))"
pinExpr = "t.track_id = (SELECT artist_pick_track_id FROM users WHERE user_id = @user_id)"
}

// The profile lists a user's own tracks plus tracks they've accepted a
// collaborator credit on. `u` is the track's owner (the deactivation check
// stays on the owner); the artist-pick pin references the profile user, so a
// collaborator's track is never spuriously pinned by the owner's pick.
// stays on the owner).
sql := `
SELECT track_id
FROM tracks t
JOIN users u ON owner_id = u.user_id
LEFT JOIN aggregate_plays ON track_id = play_item_id
LEFT JOIN aggregate_track USING (track_id)
WHERE (t.owner_id = @user_id
OR t.track_id IN (SELECT track_id FROM track_collaborators
WHERE collaborator_user_id = @user_id AND status = 'accepted'))
WHERE ` + ownerFilter + `
AND u.is_deactivated = false
AND t.is_delete = false
AND t.is_available = true
Expand All @@ -85,7 +112,7 @@ func (app *ApiServer) v1UserTracks(c *fiber.Ctx) error {
AND (t.access_authorities IS NULL
OR (COALESCE(@authed_wallet, '') <> ''
AND EXISTS (SELECT 1 FROM unnest(t.access_authorities) aa WHERE lower(aa) = lower(@authed_wallet))))` + gateFilter + `
ORDER BY (CASE WHEN t.track_id = (SELECT artist_pick_track_id FROM users WHERE user_id = @user_id) THEN 0 ELSE 1 END), ` + orderClause + `
ORDER BY (CASE WHEN ` + pinExpr + ` THEN 0 ELSE 1 END), ` + orderClause + `
LIMIT @limit
OFFSET @offset
`
Expand All @@ -95,6 +122,9 @@ func (app *ApiServer) v1UserTracks(c *fiber.Ctx) error {
"my_id": myId,
"authed_wallet": app.tryGetAuthedWallet(c),
}
if len(collabTrackIds) > 0 {
args["collab_track_ids"] = collabTrackIds
}
args["limit"] = params.Limit
args["offset"] = params.Offset

Expand Down
Loading