. */ namespace Poniverse\Ponyfm\Library; use DB; use Elasticsearch\Client; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Poniverse\Ponyfm\Models\Album; use Poniverse\Ponyfm\Models\Playlist; use Poniverse\Ponyfm\Models\Track; use Poniverse\Ponyfm\Models\User; class Search { protected $elasticsearch; protected $index; public function __construct(Client $connection, string $indexName) { $this->elasticsearch = $connection; $this->index = $indexName; } /** * @param string $query * @param int $resultsPerContentType * @return array */ public function searchAllContent(string $query) { $results = $this->elasticsearch->msearch([ 'index' => $this->index, 'body' => [ //===== Tracks=====// ['type' => 'track'], [ 'query' => [ 'multi_match' => [ 'query' => $query, 'fields' => [ 'title^3', 'artist^2', 'genre', 'track_type', 'show_songs^2', ], 'tie_breaker' => 0.3, ], ], 'size' => 11 ], //===== Albums =====// ['type' => 'album'], [ 'query' => [ 'multi_match' => [ 'query' => $query, 'fields' => [ 'title^2', 'artist', 'tracks', ], 'tie_breaker' => 0.3, ], ], 'size' => 3 ], //===== Playlists =====// ['type' => 'playlist'], [ 'query' => [ 'multi_match' => [ 'query' => $query, 'fields' => [ 'title^3', 'curator', 'tracks^2', ], 'tie_breaker' => 0.3, ], ], 'size' => 3 ], //===== Users =====// ['type' => 'user'], [ 'query' => [ 'multi_match' => [ 'query' => $query, 'fields' => [ 'display_name', 'tracks', ], 'tie_breaker' => 0.3, ], ], 'size' => 3 ], ] ]); $tracks = $this->transformTracks($results['responses'][0]['hits']['hits']); $albums = $this->transformAlbums($results['responses'][1]['hits']['hits']); $playlists = $this->transformPlaylists($results['responses'][2]['hits']['hits']); $users = $this->transformUsers($results['responses'][3]['hits']['hits']); return [ 'tracks' => $tracks, 'albums' => $albums, 'playlists' => $playlists, 'users' => $users ]; } protected function transformTracks(array $searchHits) { $tracks = $this->transformToEloquent(Track::class, $searchHits); $tracks = $tracks->map(function (Track $track) { return Track::mapPublicTrackSummary($track); }); return $tracks; } protected function transformAlbums(array $searchHits) { $albums = $this->transformToEloquent(Album::class, $searchHits); $albums = $albums->map(function (Album $album) { return Album::mapPublicAlbumSummary($album); }); return $albums; } protected function transformPlaylists(array $searchHits) { $playlists = $this->transformToEloquent(Playlist::class, $searchHits); $playlists = $playlists->map(function (Playlist $playlist) { return Playlist::mapPublicPlaylistSummary($playlist); }); return $playlists; } protected function transformUsers(array $searchHits) { $users = $this->transformToEloquent(User::class, $searchHits); $users = $users->map(function (User $user) { return User::mapPublicUserSummary($user); }); return $users; } /** * Transforms the given Elasticsearch results into a collection of corresponding * Eloquent models. * * This method assumes that the given class uses soft deletes. * * @param string $modelClass The Eloquent model class to instantiate these results as * @param array $searchHits * @return \Illuminate\Database\Eloquent\Collection */ protected function transformToEloquent(string $modelClass, array $searchHits) { if (empty($searchHits)) { return new Collection(); } $ids = []; $caseStatement = 'CASE id '; $i = 0; foreach ($searchHits as $result) { $ids[$result['_id']] = $result['_score']; $caseStatement .= "WHEN ${result['_id']} THEN $i "; $i++; } $caseStatement .= 'END'; /** @var Builder $modelInstances */ $modelInstances = $modelClass::query(); if (method_exists($modelClass, 'withTrashed')) { $modelInstances = $modelInstances->withTrashed(); } $modelInstances = $modelInstances ->whereIn('id', array_keys($ids)) ->orderBy(DB::raw($caseStatement)) ->get(); return $modelInstances; } }