feat(users/Profpatsch/whatcd-resolver): add simple favourites
A simple favourite system that adds an artist to your favourites when you go to their artist page for the first time. Also, if you fetch something from an artist, they get automatically added to the favourites. This is pretty much just done by adding more crap to the main SQL query, and has the disadvantage that the `artist_has_been_snatched` CTE slows down the query a lot for the favourite filter (by 50% or even sometimes 150% in a weird degenerate case). I think we can optimize this somewhat, but that needs a small rewrite of the query CTE stages. Change-Id: I7aa6d99dc26e24eb22ef56ffd4d2db5c6978ad48 Reviewed-on: https://cl.tvl.fyi/c/depot/+/13238 Reviewed-by: Profpatsch <mail@profpatsch.de> Tested-by: BuildkiteCI
This commit is contained in:
parent
1e188ee116
commit
bc09c01dfd
2 changed files with 76 additions and 27 deletions
|
|
@ -624,7 +624,8 @@ data GetBestTorrentsFilter = GetBestTorrentsFilter
|
||||||
onlyTheseTorrents :: Maybe ([Label "torrentId" Int]),
|
onlyTheseTorrents :: Maybe ([Label "torrentId" Int]),
|
||||||
disallowedReleaseTypes :: [ReleaseType],
|
disallowedReleaseTypes :: [ReleaseType],
|
||||||
limitResults :: Maybe Natural,
|
limitResults :: Maybe Natural,
|
||||||
ordering :: BestTorrentsOrdering
|
ordering :: BestTorrentsOrdering,
|
||||||
|
onlyFavourites :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
data BestTorrentsOrdering = BySeedingWeight | ByLastReleases
|
data BestTorrentsOrdering = BySeedingWeight | ByLastReleases
|
||||||
|
|
@ -676,6 +677,16 @@ getBestTorrents opts = do
|
||||||
JOIN redacted.torrent_groups tg ON tg.id = t.torrent_group
|
JOIN redacted.torrent_groups tg ON tg.id = t.torrent_group
|
||||||
WHERE
|
WHERE
|
||||||
tg.full_json_result->>'releaseType' <> ALL (?::text[])
|
tg.full_json_result->>'releaseType' <> ALL (?::text[])
|
||||||
|
),
|
||||||
|
prepare2 AS MATERIALIZED (
|
||||||
|
-- extract the json artist ids field into an array of ints
|
||||||
|
SELECT *, array(select id from jsonb_to_recordset(artists) as (id int)) as artist_ids
|
||||||
|
FROM prepare1
|
||||||
|
),
|
||||||
|
artist_has_been_snatched AS MATERIALIZED (
|
||||||
|
SELECT DISTINCT artist_id
|
||||||
|
FROM (SELECT UNNEST(artist_ids) as artist_id, has_torrent_file from prepare2) as _
|
||||||
|
WHERE has_torrent_file
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
group_id,
|
group_id,
|
||||||
|
|
@ -688,7 +699,14 @@ getBestTorrents opts = do
|
||||||
has_torrent_file,
|
has_torrent_file,
|
||||||
transmission_torrent_hash,
|
transmission_torrent_hash,
|
||||||
torrent_format
|
torrent_format
|
||||||
FROM prepare1
|
FROM prepare2
|
||||||
|
JOIN LATERAL
|
||||||
|
(SELECT (
|
||||||
|
artist_ids && ARRAY(SELECT artist_id FROM artist_has_been_snatched)
|
||||||
|
OR artist_ids && ARRAY(SELECT artist_id FROM redacted.artist_favourites)
|
||||||
|
) as is_favourite) as _
|
||||||
|
-- filter everything that’s not a favourite if requested
|
||||||
|
ON (NOT ?::bool OR is_favourite)
|
||||||
|]
|
|]
|
||||||
<> case opts.ordering of
|
<> case opts.ordering of
|
||||||
BySeedingWeight -> [fmt|ORDER BY seeding_weight DESC|] <> "\n"
|
BySeedingWeight -> [fmt|ORDER BY seeding_weight DESC|] <> "\n"
|
||||||
|
|
@ -709,6 +727,7 @@ getBestTorrents opts = do
|
||||||
onlyTheseTorrentsB :: Bool,
|
onlyTheseTorrentsB :: Bool,
|
||||||
onlyTheseTorrents,
|
onlyTheseTorrents,
|
||||||
(opts.disallowedReleaseTypes & concatMap (\rt -> [rt.stringKey, rt.intKey & buildText intDecimalT]) & PGArray :: PGArray Text),
|
(opts.disallowedReleaseTypes & concatMap (\rt -> [rt.stringKey, rt.intKey & buildText intDecimalT]) & PGArray :: PGArray Text),
|
||||||
|
opts.onlyFavourites :: Bool,
|
||||||
opts.limitResults <&> naturalToInteger :: Maybe Integer
|
opts.limitResults <&> naturalToInteger :: Maybe Integer
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import Data.Map.Strict qualified as Map
|
||||||
import Data.Pool qualified as Pool
|
import Data.Pool qualified as Pool
|
||||||
import Data.Text qualified as Text
|
import Data.Text qualified as Text
|
||||||
import Database.PostgreSQL.Simple qualified as Postgres
|
import Database.PostgreSQL.Simple qualified as Postgres
|
||||||
import Database.PostgreSQL.Simple.Types (PGArray (PGArray))
|
import Database.PostgreSQL.Simple.Types (Only (..), PGArray (PGArray))
|
||||||
import Database.Postgres.Temp qualified as TmpPg
|
import Database.Postgres.Temp qualified as TmpPg
|
||||||
import FieldParser (FieldParser)
|
import FieldParser (FieldParser)
|
||||||
import FieldParser qualified as Field
|
import FieldParser qualified as Field
|
||||||
|
|
@ -159,7 +159,7 @@ htmlUi = do
|
||||||
( do
|
( do
|
||||||
d <-
|
d <-
|
||||||
getBestTorrentsData
|
getBestTorrentsData
|
||||||
(t3 #limitResults Nothing #ordering BySeedingWeight #disallowedReleaseTypes [])
|
bestTorrentsDataDefault
|
||||||
( Just
|
( Just
|
||||||
( E21
|
( E21
|
||||||
(label @"onlyTheseTorrents" res.newTorrents)
|
(label @"onlyTheseTorrents" res.newTorrents)
|
||||||
|
|
@ -305,13 +305,21 @@ htmlUi = do
|
||||||
)
|
)
|
||||||
$ \dat _span ->
|
$ \dat _span ->
|
||||||
( do
|
( do
|
||||||
runTransaction $ inSpan' "finding artist name" $ \span -> do
|
runTransaction $ do
|
||||||
|
(artistName, _) <-
|
||||||
|
concurrentlyTraced
|
||||||
|
( inSpan' "finding artist name" $ \span -> do
|
||||||
addAttribute span "artist-redacted-id" (dat.queryArgs.artistRedactedId, intDecimalT)
|
addAttribute span "artist-redacted-id" (dat.queryArgs.artistRedactedId, intDecimalT)
|
||||||
mArtistName <- getArtistNameById (lbl #artistId dat.queryArgs.artistRedactedId)
|
mArtistName <- getArtistNameById (lbl #artistId dat.queryArgs.artistRedactedId)
|
||||||
let pageTitle = case mArtistName of
|
let pageTitle = case mArtistName of
|
||||||
Nothing -> "whatcd-resolver"
|
Nothing -> "whatcd-resolver"
|
||||||
Just a -> [fmt|{a} - Artist Page - whatcd-resolver|]
|
Just a -> [fmt|{a} - Artist Page - whatcd-resolver|]
|
||||||
pure $ htmlPageChrome ourHtmlIntegrities pageTitle,
|
pure $ htmlPageChrome ourHtmlIntegrities pageTitle
|
||||||
|
)
|
||||||
|
( do
|
||||||
|
execute [sql|INSERT INTO redacted.artist_favourites (artist_id) VALUES (?) ON CONFLICT DO NOTHING|] (Only (dat.queryArgs.artistRedactedId :: Int))
|
||||||
|
)
|
||||||
|
pure artistName,
|
||||||
do
|
do
|
||||||
artistPage (T2 dat.queryArgs (label @"uniqueRunId" uniqueRunId))
|
artistPage (T2 dat.queryArgs (label @"uniqueRunId" uniqueRunId))
|
||||||
)
|
)
|
||||||
|
|
@ -382,16 +390,17 @@ htmlUi = do
|
||||||
( do
|
( do
|
||||||
d <-
|
d <-
|
||||||
getBestTorrentsData
|
getBestTorrentsData
|
||||||
( t3
|
( BestTorrentsData
|
||||||
#limitResults
|
{ limitResults = Just 100,
|
||||||
(Just 100)
|
ordering = ByLastReleases,
|
||||||
#ordering
|
onlyFavourites = True,
|
||||||
ByLastReleases
|
disallowedReleaseTypes =
|
||||||
#disallowedReleaseTypes
|
|
||||||
[ releaseTypeBootleg,
|
[ releaseTypeBootleg,
|
||||||
releaseTypeGuestAppearance,
|
releaseTypeGuestAppearance,
|
||||||
releaseTypeRemix
|
releaseTypeRemix
|
||||||
]
|
],
|
||||||
|
..
|
||||||
|
}
|
||||||
)
|
)
|
||||||
Nothing
|
Nothing
|
||||||
pure $ case d & nonEmpty of
|
pure $ case d & nonEmpty of
|
||||||
|
|
@ -601,7 +610,7 @@ artistPage dat = runTransaction $ do
|
||||||
(fresh, settings) <-
|
(fresh, settings) <-
|
||||||
concurrentlyTraced
|
concurrentlyTraced
|
||||||
( getBestTorrentsData
|
( getBestTorrentsData
|
||||||
(t3 #limitResults Nothing #ordering BySeedingWeight #disallowedReleaseTypes [])
|
bestTorrentsDataDefault
|
||||||
(Just $ E22 (getLabel @"artistRedactedId" dat))
|
(Just $ E22 (getLabel @"artistRedactedId" dat))
|
||||||
)
|
)
|
||||||
(getSettings)
|
(getSettings)
|
||||||
|
|
@ -833,17 +842,30 @@ data ArtistFilter = ArtistFilter
|
||||||
doIfJust :: (Applicative f) => (a -> f ()) -> Maybe a -> f ()
|
doIfJust :: (Applicative f) => (a -> f ()) -> Maybe a -> f ()
|
||||||
doIfJust = traverse_
|
doIfJust = traverse_
|
||||||
|
|
||||||
|
data BestTorrentsData = BestTorrentsData
|
||||||
|
{ limitResults :: Maybe Natural,
|
||||||
|
ordering :: BestTorrentsOrdering,
|
||||||
|
disallowedReleaseTypes :: [ReleaseType],
|
||||||
|
onlyFavourites :: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
bestTorrentsDataDefault :: BestTorrentsData
|
||||||
|
bestTorrentsDataDefault =
|
||||||
|
BestTorrentsData
|
||||||
|
{ limitResults = Nothing,
|
||||||
|
ordering = BySeedingWeight,
|
||||||
|
disallowedReleaseTypes = [],
|
||||||
|
onlyFavourites = False
|
||||||
|
}
|
||||||
|
|
||||||
getBestTorrentsData ::
|
getBestTorrentsData ::
|
||||||
( MonadTransmission m,
|
( MonadTransmission m,
|
||||||
MonadThrow m,
|
MonadThrow m,
|
||||||
MonadLogger m,
|
MonadLogger m,
|
||||||
MonadPostgres m,
|
MonadPostgres m,
|
||||||
MonadOtel m,
|
MonadOtel m
|
||||||
HasField "limitResults" opts (Maybe Natural),
|
|
||||||
HasField "ordering" opts BestTorrentsOrdering,
|
|
||||||
HasField "disallowedReleaseTypes" opts [ReleaseType]
|
|
||||||
) =>
|
) =>
|
||||||
opts ->
|
BestTorrentsData ->
|
||||||
Maybe (E2 "onlyTheseTorrents" [Label "torrentId" Int] "artistRedactedId" Int) ->
|
Maybe (E2 "onlyTheseTorrents" [Label "torrentId" Int] "artistRedactedId" Int) ->
|
||||||
Transaction m [TorrentData (Label "percentDone" Percentage)]
|
Transaction m [TorrentData (Label "percentDone" Percentage)]
|
||||||
getBestTorrentsData opts filters = inSpan' "get torrents table data" $ \span -> do
|
getBestTorrentsData opts filters = inSpan' "get torrents table data" $ \span -> do
|
||||||
|
|
@ -855,7 +877,9 @@ getBestTorrentsData opts filters = inSpan' "get torrents table data" $ \span ->
|
||||||
|
|
||||||
let ordering = opts.ordering
|
let ordering = opts.ordering
|
||||||
let disallowedReleaseTypes = opts.disallowedReleaseTypes
|
let disallowedReleaseTypes = opts.disallowedReleaseTypes
|
||||||
|
let onlyFavourites = opts.onlyFavourites
|
||||||
let getBest = getBestTorrents GetBestTorrentsFilter {..}
|
let getBest = getBestTorrents GetBestTorrentsFilter {..}
|
||||||
|
|
||||||
bestStale :: [TorrentData ()] <- getBest
|
bestStale :: [TorrentData ()] <- getBest
|
||||||
(statusInfo, transmissionStatus) <-
|
(statusInfo, transmissionStatus) <-
|
||||||
getAndUpdateTransmissionTorrentsStatus
|
getAndUpdateTransmissionTorrentsStatus
|
||||||
|
|
@ -1139,6 +1163,12 @@ migrate = inSpan "Database Migration" $ do
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS torrents_json_seeding ON redacted.torrents_json(((full_json_result->'seeding')::integer));
|
CREATE INDEX IF NOT EXISTS torrents_json_seeding ON redacted.torrents_json(((full_json_result->'seeding')::integer));
|
||||||
CREATE INDEX IF NOT EXISTS torrents_json_snatches ON redacted.torrents_json(((full_json_result->'snatches')::integer));
|
CREATE INDEX IF NOT EXISTS torrents_json_snatches ON redacted.torrents_json(((full_json_result->'snatches')::integer));
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS redacted.artist_favourites (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
artist_id INTEGER NOT NULL,
|
||||||
|
UNIQUE(artist_id)
|
||||||
|
);
|
||||||
|]
|
|]
|
||||||
()
|
()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue