From 01b534d75bbb51c0f60abcb9efb67bf2069c9940 Mon Sep 17 00:00:00 2001 From: Wes Copeland Date: Wed, 15 Jan 2025 21:05:08 -0500 Subject: [PATCH] perf(site-award): query for game_sets in SeparateAwards() --- app/Helpers/render/site-award.php | 76 ++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/app/Helpers/render/site-award.php b/app/Helpers/render/site-award.php index 358cf7ba4b..c6b5adda19 100644 --- a/app/Helpers/render/site-award.php +++ b/app/Helpers/render/site-award.php @@ -1,38 +1,72 @@ $award['AwardType'] == AwardType::Mastery && $award['ConsoleName'] != 'Events')); - - $eventAwards = array_filter($userAwards, fn ($award) => $award['AwardType'] == AwardType::Mastery && $award['ConsoleName'] == 'Events'); - - $devEventsPrefix = "[Dev Events - "; - $devEventsHub = "[Central - Developer Events]"; - $devEventAwards = []; - foreach ($eventAwards as $eventAward) { - $related = getGameAlternatives($eventAward['AwardData']); - foreach ($related as $hub) { - if ($hub['Title'] == $devEventsHub || str_starts_with($hub['Title'], $devEventsPrefix)) { - $devEventAwards[] = $eventAward; - break; + // Pre-calculate type checks and perform partitioning in a single pass. + $typeInfo = []; + $gameAwards = []; + $eventAwards = []; + + // Process each award: store its type info for later O(1) lookups. + foreach ($userAwards as $key => $award) { + $type = (int) $award['AwardType']; + $typeInfo[$key] = ['isGame' => AwardType::isGame($type), 'isActive' => AwardType::isActive($type)]; + + if ($type === AwardType::Mastery) { + if ($award['ConsoleName'] === 'Events') { + $eventAwards[] = $award; + } else { + $gameAwards[] = $award; } } } - $eventAwards = array_values(array_filter($eventAwards, fn ($award) => !in_array($award, $devEventAwards))); + // If there are no event awards, don't even check for dev events. We're done. + if (empty($eventAwards)) { + return [ + $gameAwards, + [], + array_values(array_filter($userAwards, fn ($award, $key) => !$typeInfo[$key]['isGame'] && $typeInfo[$key]['isActive'], ARRAY_FILTER_USE_BOTH)), + ]; + } - $siteAwards = array_values(array_filter($userAwards, function ($userAward) use ($devEventAwards) { - $isNotMasteryOrGameBeaten = !AwardType::isGame((int) $userAward['AwardType']); - $isActiveAwardType = AwardType::isActive((int) $userAward['AwardType']); - $isDevEventAward = in_array($userAward, $devEventAwards); + // Find dev event games with a single optimized query. + $devEventGamesLookup = array_flip(GameSet::query() + ->whereHas('games', fn ($q) => $q->whereIn('game_id', array_column($eventAwards, 'AwardData'))) + ->where(fn ($q) => $q->where('id', 5)->orWhere('title', 'like', '[Dev Events - %')) + ->with(['games' => fn ($q) => $q->select('GameData.ID as id')]) + ->get() + ->pluck('games.*.id') + ->flatten() + ->unique() + ->values() + ->all()); + + // Separate dev event awards and regular event awards with O(1) lookups. + $devEventAwards = []; + $regularEventAwards = []; + foreach ($eventAwards as $award) { + if (isset($devEventGamesLookup[$award['AwardData']])) { + $devEventAwards[] = $award; + } else { + $regularEventAwards[] = $award; + } + } - return ($isNotMasteryOrGameBeaten && $isActiveAwardType) || $isDevEventAward; - })); + // Calculate site awards including dev event awards. + $devEventAwardsLookup = array_flip(array_map('serialize', $devEventAwards)); - return [$gameAwards, $eventAwards, $siteAwards]; + return [ + $gameAwards, + $regularEventAwards, + array_values(array_filter($userAwards, fn ($award, $key) => isset($devEventAwardsLookup[serialize($award)]) + || (!$typeInfo[$key]['isGame'] && $typeInfo[$key]['isActive']), ARRAY_FILTER_USE_BOTH)), + ]; } function RenderSiteAwards(array $userAwards, string $awardsOwnerUsername): void