From 5bd5c7741615a92b70970fa8cfbbe0beef510d3e Mon Sep 17 00:00:00 2001 From: Buster Neece Date: Fri, 27 Jan 2023 07:57:13 -0600 Subject: [PATCH] Initial indexing work for Meilisearch. --- src/Service/Meilisearch/Index.php | 159 +++++++++++++++++++++++++----- 1 file changed, 137 insertions(+), 22 deletions(-) diff --git a/src/Service/Meilisearch/Index.php b/src/Service/Meilisearch/Index.php index 56d7f0249..2a18dddfd 100644 --- a/src/Service/Meilisearch/Index.php +++ b/src/Service/Meilisearch/Index.php @@ -8,6 +8,7 @@ use App\Doctrine\ReloadableEntityManagerInterface; use App\Entity\Repository\CustomFieldRepository; use App\Entity\Station; use App\Entity\StorageLocation; +use App\Environment; use Meilisearch\Client; final class Index @@ -15,6 +16,7 @@ final class Index public function __construct( private readonly ReloadableEntityManagerInterface $em, private readonly CustomFieldRepository $customFieldRepo, + private readonly Environment $environment, private readonly Client $client, private readonly StorageLocation $storageLocation, private readonly string $indexUid @@ -23,32 +25,144 @@ final class Index public function configure(): void { - $indexSettings = [ - 'primaryKey' => 'id', - 'filterableAttributes' => [ - 'playlists', - 'is_requestable', - 'is_on_demand', - ], - 'sortableAttributes' => [ - 'path', - 'mtime', - 'length', - 'title', - 'artist', - 'album', - 'genre', - 'isrc', - ], + $filterableAttributes = []; + + $mediaFields = [ + 'id', + 'path', + 'mtime', + 'length', + 'title', + 'artist', + 'album', + 'genre', + 'isrc', ]; - foreach ($this->customFieldRepo->getFieldIds() as $fieldId => $fieldShortCode) { - $indexSettings['sortableAttributes'][] = 'custom_field_' . $fieldId; + foreach ($this->iterateStations() as $station) { + $stationId = $station->getIdRequired(); + + $filterableAttributes[] = 'station_' . $stationId . '_playlists'; + $filterableAttributes[] = 'station_' . $stationId . '_is_requestable'; + $filterableAttributes[] = 'station_' . $stationId . '_is_on_demand'; } - $this->client->updateIndex( - $this->indexUid, - $indexSettings + foreach ($this->customFieldRepo->getFieldIds() as $fieldId => $fieldShortCode) { + $mediaFields[] = 'custom_field_' . $fieldId; + } + + $indexSettings = [ + 'primaryKey' => 'id', + 'filterableAttributes' => $filterableAttributes, + 'sortableAttributes' => $mediaFields, + 'displayedAttributes' => $this->environment->isProduction() + ? ['id'] + : ['*'], + ]; + + // Avoid updating settings unless necessary to avoid triggering a reindex. + $this->client->createIndex($this->indexUid); + + $index = $this->client->index($this->indexUid); + $currentSettings = $index->getSettings(); + $settingsToUpdate = []; + + foreach ($indexSettings as $settingKey => $setting) { + $currentSetting = $currentSettings[$settingKey] ?? []; + if ($currentSetting !== $setting) { + $settingsToUpdate[$settingKey] = $setting; + } + } + + if (!empty($settingsToUpdate)) { + $index->updateSettings($settingsToUpdate); + } + } + + public function addMedia(array $ids): void + { + $this->refreshMedia($ids, true); + } + + public function refreshMedia( + array $ids, + bool $includePlaylists = false + ): void { + } + + public function refreshPlaylists(Station $station): void + { + $stationId = $station->getIdRequired(); + + $playlistsKey = 'station_' . $stationId . '_playlists'; + $isRequestableKey = 'station_' . $stationId . '_is_requestable'; + $isOnDemandKey = 'station_' . $stationId . '_is_on_demand'; + + $allMediaRaw = $this->em->createQuery( + <<<'DQL' + SELECT m.id, m.unique_id FROM App\Entity\StationMedia m + WHERE m.storage_location = :storageLocation + DQL + )->setParameter('storageLocation', $this->storageLocation) + ->getArrayResult(); + + $media = []; + foreach ($allMediaRaw as $mediaRow) { + $media[$mediaRow['id']] = [ + 'id' => $mediaRow['unique_id'], + $playlistsKey => [], + $isRequestableKey => false, + $isOnDemandKey => false, + ]; + } + + $allPlaylists = $this->em->createQuery( + <<<'DQL' + SELECT p.id, p.include_in_on_demand, p.include_in_requests + FROM App\Entity\StationPlaylist p + WHERE p.station = :station AND p.is_enabled = 1 + DQL + )->setParameter('station', $station) + ->getArrayResult(); + + $allPlaylistIds = []; + $onDemandPlaylists = []; + $requestablePlaylists = []; + + foreach ($allPlaylists as $playlist) { + $allPlaylistIds[] = $playlist['id']; + if ($playlist['include_in_on_demand']) { + $onDemandPlaylists[$playlist['id']] = $playlist['id']; + } + if ($playlist['include_in_requests']) { + $requestablePlaylists[$playlist['id']] = $playlist['id']; + } + } + + $mediaInPlaylists = $this->em->createQuery( + <<<'DQL' + SELECT spm.media_id, spm.playlist_id + FROM App\Entity\StationPlaylistMedia spm + WHERE spm.playlist_id IN (:allPlaylistIds) + DQL + )->setParameter('allPlaylistIds', $allPlaylistIds) + ->toIterable(); + + foreach ($mediaInPlaylists as $spmRow) { + $mediaId = $spmRow['media_id']; + $playlistId = $spmRow['playlist_id']; + + $media[$mediaId][$playlistsKey][] = $playlistId; + if (isset($requestablePlaylists[$playlistId])) { + $media[$mediaId][$isRequestableKey] = true; + } + if (isset($onDemandPlaylists[$playlistId])) { + $media[$mediaId][$isOnDemandKey] = true; + } + } + + $this->client->index($this->indexUid)->updateDocumentsInBatches( + array_values($media) ); } @@ -59,6 +173,7 @@ final class Index <<<'DQL' SELECT s FROM App\Entity\Station s WHERE s.media_storage_location = :storageLocation + AND s.is_enabled = 1 DQL )->setParameter('storageLocation', $this->storageLocation) ->toIterable();