From f9819376455c371eb5bd3c84ad938698335a3d67 Mon Sep 17 00:00:00 2001 From: Yassine Doghri Date: Sat, 28 Dec 2024 15:23:02 +0000 Subject: [PATCH] fix: add downloads_count to episodes table, computed every hour This removes computing latency when retrieving episodes list with download count in admin. The more analytics records, the more it took to calculate the sum of hits to get the downloads count for each episode. --- app/Commands/EpisodesComputeDownloads.php | 50 +++++++++++++++++++ app/Config/Tasks.php | 4 ++ ...5-29-100000_add_episode_download_count.php | 29 +++++++++++ app/Entities/Episode.php | 5 +- app/Models/EpisodeModel.php | 1 + .../Admin/Controllers/EpisodeController.php | 17 ++----- themes/cp_admin/episode/list.php | 2 +- 7 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 app/Commands/EpisodesComputeDownloads.php create mode 100644 app/Database/Migrations/2024-05-29-100000_add_episode_download_count.php diff --git a/app/Commands/EpisodesComputeDownloads.php b/app/Commands/EpisodesComputeDownloads.php new file mode 100644 index 00000000..1bdc7b2d --- /dev/null +++ b/app/Commands/EpisodesComputeDownloads.php @@ -0,0 +1,50 @@ +builder() + ->select('episodes.id as id, IFNULL(SUM(ape.hits),0) as downloads_count') + ->join('analytics_podcasts_by_episode ape', 'episodes.id=ape.episode_id', 'left') + ->groupBy('episodes.id'); + + $episodeModel2 = new EpisodeModel(); + $episodeModel2->builder() + ->setQueryAsData($query) + ->onConstraint('id') + ->updateBatch(); + } +} diff --git a/app/Config/Tasks.php b/app/Config/Tasks.php index 06dd531c..e78ebfc3 100644 --- a/app/Config/Tasks.php +++ b/app/Config/Tasks.php @@ -51,5 +51,9 @@ class Tasks extends BaseConfig $schedule->command('podcast:import') ->everyMinute() ->named('podcast-import'); + + $schedule->command('episodes:compute-downloads') + ->everyHour() + ->named('episodes:compute-downloads'); } } diff --git a/app/Database/Migrations/2024-05-29-100000_add_episode_download_count.php b/app/Database/Migrations/2024-05-29-100000_add_episode_download_count.php new file mode 100644 index 00000000..ec6a9023 --- /dev/null +++ b/app/Database/Migrations/2024-05-29-100000_add_episode_download_count.php @@ -0,0 +1,29 @@ + [ + 'type' => 'INT', + 'unsigned' => true, + 'default' => 0, + 'after' => 'is_published_on_hubs', + ], + ]; + + $this->forge->addColumn('episodes', $fields); + } + + public function down(): void + { + $this->forge->dropColumn('episodes', 'downloads_count'); + } +} diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index e9e8bcfa..16e41d85 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -72,9 +72,9 @@ use RuntimeException; * @property string|null $location_geo * @property string|null $location_osm * @property bool $is_published_on_hubs + * @property int $downloads_count * @property int $posts_count * @property int $comments_count - * @property int $downloads * @property EpisodeComment[]|null $comments * @property bool $is_premium * @property int $created_by @@ -112,8 +112,6 @@ class Episode extends Entity protected ?Chapters $chapters = null; - protected int $downloads = 0; - /** * @var Person[]|null */ @@ -171,6 +169,7 @@ class Episode extends Entity 'location_geo' => '?string', 'location_osm' => '?string', 'is_published_on_hubs' => 'boolean', + 'downloads_count' => 'integer', 'posts_count' => 'integer', 'comments_count' => 'integer', 'is_premium' => 'boolean', diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index 69e3bf07..9ec14a54 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -88,6 +88,7 @@ class EpisodeModel extends UuidModel 'location_geo', 'location_osm', 'is_published_on_hubs', + 'downloads_count', 'posts_count', 'comments_count', 'is_premium', diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php index f6f23088..c684754a 100644 --- a/modules/Admin/Controllers/EpisodeController.php +++ b/modules/Admin/Controllers/EpisodeController.php @@ -70,34 +70,25 @@ class EpisodeController extends BaseController // Use LIKE operator as a fallback. if (strlen($query) < 4) { $episodes = $episodeModel - ->select('episodes.*, IFNULL(SUM(ape.hits),0) as downloads') - ->join('analytics_podcasts_by_episode ape', 'episodes.id=ape.episode_id', 'left') - ->where('episodes.podcast_id', $podcast->id) + ->where('podcast_id', $podcast->id) ->like('title', $episodeModel->db->escapeLikeString($query)) ->orLike('description_markdown', $episodeModel->db->escapeLikeString($query)) ->orLike('slug', $episodeModel->db->escapeLikeString($query)) ->orLike('location_name', $episodeModel->db->escapeLikeString($query)) - ->groupBy('episodes.id') ->orderBy('-`published_at`', '', false) ->orderBy('created_at', 'desc'); } else { $episodes = $episodeModel - ->select('episodes.*, IFNULL(SUM(ape.hits),0) as downloads') - ->join('analytics_podcasts_by_episode ape', 'episodes.id=ape.episode_id', 'left') - ->where('episodes.podcast_id', $podcast->id) + ->where('podcast_id', $podcast->id) ->where( "MATCH (title, description_markdown, slug, location_name) AGAINST ('{$episodeModel->db->escapeString( $query )}')" - ) - ->groupBy('episodes.id'); + ); } } else { $episodes = $episodeModel - ->select('episodes.*, IFNULL(SUM(ape.hits),0) as downloads') - ->join('analytics_podcasts_by_episode ape', 'episodes.id=ape.episode_id', 'left') - ->where('episodes.podcast_id', $podcast->id) - ->groupBy('episodes.id') + ->where('podcast_id', $podcast->id) ->orderBy('-`published_at`', '', false) ->orderBy('created_at', 'desc'); } diff --git a/themes/cp_admin/episode/list.php b/themes/cp_admin/episode/list.php index 5ede63da..0662e8e7 100644 --- a/themes/cp_admin/episode/list.php +++ b/themes/cp_admin/episode/list.php @@ -92,7 +92,7 @@ data_table( [ 'header' => lang('Episode.list.downloads'), 'cell' => function ($episode): string { - return downloads_abbr($episode->downloads); + return downloads_abbr($episode->downloads_count); }, ], [