chg: dev: add RecentBattle entity that is a Materialized View to speed up the view - and further refactor on ProfileController #8
This commit is contained in:
124
src/Migrations/2026/04/Version20260420130000.php
Normal file
124
src/Migrations/2026/04/Version20260420130000.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of the SplendidBear Websites' projects.
|
||||
*
|
||||
* Copyright (c) 2026 @ www.splendidbear.org
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Migrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Class Version20260420130000
|
||||
*
|
||||
* Creates recent_battles materialized view for ProfileController optimization.
|
||||
*
|
||||
* @package App\Migrations
|
||||
* @author Lang <https://www.splendidbear.org>
|
||||
* @category Class
|
||||
* @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
|
||||
* @link www.splendidbear.org
|
||||
* @since 2026. 04. 20.
|
||||
*/
|
||||
final class Version20260420130000 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Create recent_battles materialized view for profile recent games list.';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->addSql('
|
||||
CREATE MATERIALIZED VIEW recent_battles AS
|
||||
SELECT
|
||||
pg.id AS game_id,
|
||||
pg.uuid::text AS uuid,
|
||||
u.id AS user_id,
|
||||
CASE WHEN pg.red_id = u.id THEN true ELSE false END AS is_red,
|
||||
|
||||
COALESCE(ru.username, ra.user_name, \'Guest\') AS red_name,
|
||||
COALESCE(bu.username, ba.user_name, \'Guest\') AS blue_name,
|
||||
|
||||
ru.avatar_path AS red_avatar_path,
|
||||
bu.avatar_path AS blue_avatar_path,
|
||||
|
||||
pg.red_points,
|
||||
pg.blue_points,
|
||||
pg.red_exploded_bomb,
|
||||
pg.blue_exploded_bomb,
|
||||
pg.resign,
|
||||
|
||||
COALESCE(pg.red_bonus_points, 0.0) AS red_bonus_points,
|
||||
COALESCE(pg.blue_bonus_points, 0.0) AS blue_bonus_points,
|
||||
COALESCE(pg.red_bonus_stats, \'[]\'::json) AS red_bonus_stats,
|
||||
COALESCE(pg.blue_bonus_stats, \'[]\'::json) AS blue_bonus_stats,
|
||||
|
||||
CASE
|
||||
WHEN pg.red_id = u.id AND pg.resign = \'red\' THEN \'loss\'
|
||||
WHEN pg.blue_id = u.id AND pg.resign = \'blue\' THEN \'loss\'
|
||||
WHEN pg.red_id = u.id AND pg.resign = \'blue\' THEN \'win\'
|
||||
WHEN pg.blue_id = u.id AND pg.resign = \'red\' THEN \'win\'
|
||||
WHEN pg.red_id = u.id AND pg.red_points IS NOT NULL AND pg.blue_points IS NOT NULL
|
||||
AND pg.red_points > pg.blue_points THEN \'win\'
|
||||
WHEN pg.blue_id = u.id AND pg.blue_points IS NOT NULL AND pg.red_points IS NOT NULL
|
||||
AND pg.blue_points > pg.red_points THEN \'win\'
|
||||
WHEN pg.red_id = u.id AND pg.red_points IS NOT NULL AND pg.blue_points IS NOT NULL
|
||||
AND pg.red_points < pg.blue_points THEN \'loss\'
|
||||
WHEN pg.blue_id = u.id AND pg.blue_points IS NOT NULL AND pg.red_points IS NOT NULL
|
||||
AND pg.blue_points < pg.red_points THEN \'loss\'
|
||||
ELSE \'draw\'
|
||||
END AS result,
|
||||
|
||||
-- Whether the opponent in this game is an anonymous guest (no app_user account)
|
||||
CASE
|
||||
WHEN pg.red_id = u.id AND pg.blue_id IS NULL THEN true
|
||||
WHEN pg.blue_id = u.id AND pg.red_id IS NULL THEN true
|
||||
ELSE false
|
||||
END AS opp_is_guest,
|
||||
|
||||
pg.created,
|
||||
pg.updated
|
||||
FROM app_user u
|
||||
JOIN played_game pg
|
||||
ON pg.red_id = u.id
|
||||
OR pg.blue_id = u.id
|
||||
LEFT JOIN app_user ru ON ru.id = pg.red_id
|
||||
LEFT JOIN app_user bu ON bu.id = pg.blue_id
|
||||
LEFT JOIN gamer ra ON ra.id = pg.red_anon
|
||||
LEFT JOIN gamer ba ON ba.id = pg.blue_anon
|
||||
');
|
||||
|
||||
$this->addSql('CREATE UNIQUE INDEX idx_recent_battles_pk ON recent_battles (user_id, game_id)');
|
||||
$this->addSql('CREATE INDEX idx_recent_battles_user_upd ON recent_battles (user_id, updated DESC)');
|
||||
|
||||
$this->addSql('
|
||||
CREATE OR REPLACE FUNCTION refresh_recent_battles()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
REFRESH MATERIALIZED VIEW CONCURRENTLY recent_battles;
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql
|
||||
');
|
||||
|
||||
$this->addSql('
|
||||
CREATE TRIGGER trigger_refresh_recent_battles
|
||||
AFTER INSERT OR UPDATE OR DELETE ON played_game
|
||||
FOR EACH STATEMENT
|
||||
EXECUTE FUNCTION refresh_recent_battles()
|
||||
');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->addSql('DROP TRIGGER IF EXISTS trigger_refresh_recent_battles ON played_game');
|
||||
$this->addSql('DROP FUNCTION IF EXISTS refresh_recent_battles()');
|
||||
$this->addSql('DROP MATERIALIZED VIEW IF EXISTS recent_battles');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user