feat: modify the response format for search

This commit is contained in:
Phan An 2020-12-25 12:52:28 +01:00
parent cacca23c02
commit 588b30d9bb
4 changed files with 118 additions and 25 deletions

View file

@ -2,7 +2,7 @@ openapi: 3.0.0
info: info:
title: Koel API title: Koel API
version: 5.0.0 version: 5.0.0
description: 'The API for [Koel](http://koel.dev), the music streaming application that works.' description: 'The API for [Koel](https://koel.dev), the music streaming application that works.'
contact: contact:
name: An Phan name: An Phan
url: 'https://phanan.net' url: 'https://phanan.net'
@ -11,7 +11,7 @@ info:
name: MIT name: MIT
url: 'https://github.com/koel/koel/blob/master/LICENSE.md' url: 'https://github.com/koel/koel/blob/master/LICENSE.md'
servers: servers:
- url: 'http://localhost:8000' - url: 'https://koel.test'
description: Local description: Local
tags: tags:
- name: interaction - name: interaction
@ -852,8 +852,7 @@ paths:
$ref: '#/components/schemas/Song' $ref: '#/components/schemas/Song'
operationId: post-os-s3-song operationId: post-os-s3-song
description: Create a new song or update an existing one with data sent from AWS description: Create a new song or update an existing one with data sent from AWS
security: security: []
- appKey: []
requestBody: requestBody:
content: content:
application/json: application/json:
@ -908,8 +907,7 @@ paths:
'204': '204':
description: No Content description: No Content
description: Remove a song whose information matches the data sent from AWS S3 (`bucket` and `key`) description: Remove a song whose information matches the data sent from AWS S3 (`bucket` and `key`)
security: security: []
- appKey: []
requestBody: requestBody:
content: content:
application/json: application/json:
@ -1103,6 +1101,84 @@ paths:
description: Download a whole playlist. This is NOT an XmlHttpRequest. The response will be a download response of either one media file or a zip file containg multiple media files. description: Download a whole playlist. This is NOT an XmlHttpRequest. The response will be a download response of either one media file or a zip file containg multiple media files.
security: security:
- api-token: [] - api-token: []
/api/search:
get:
summary: 'Search for songs, albums, and artists'
tags:
- search
responses:
'200':
description: OK
content:
application/json:
schema:
type: object
properties:
results:
type: object
required:
- songs
- artists
- albums
properties:
songs:
type: array
description: An array of max 6 best-matching songs' IDs
items:
type: string
artists:
type: array
description: An array of max 6 best-matching artists' IDs
items:
type: integer
albums:
type: array
description: An array of max 6 best-matching albums' IDs
items:
type: number
required:
- results
operationId: get-api-search
description: 'Seach for songs, albums, and artists, with a maximum of {count} results each.'
security:
- Bearer Token: []
parameters:
- schema:
type: string
in: query
name: q
description: The keywords to search
required: true
- schema:
type: integer
minimum: 1
default: 6
in: query
name: count
description: 'The maximum number of results for songs, artists, and albums'
parameters: []
/api/search/songs:
get:
summary: Search for songs
tags:
- search
responses: {}
operationId: get-api-search-songs
description: Get all songs that matches a search query.
security:
- Bearer Token: []
requestBody:
content:
application/json:
schema:
type: object
properties:
songs:
type: array
description: An array of matching songs' IDs
items: {}
required:
- songs
components: components:
schemas: schemas:
User: User:
@ -1638,10 +1714,6 @@ components:
Bearer Token: Bearer Token:
type: http type: http
scheme: bearer scheme: bearer
appKey:
name: The applcation key (APP_KEY in .env)
type: apiKey
in: query
api-token: api-token:
name: The API token as a query parameter name: The API token as a query parameter
type: apiKey type: apiKey

View file

@ -22,8 +22,14 @@ class ExcerptSearchController extends Controller
throw new InvalidArgumentException('A search query is required.'); throw new InvalidArgumentException('A search query is required.');
} }
$count = (int) $request->get('count', SearchService::DEFAULT_EXCERPT_RESULT_COUNT);
if ($count < 0) {
throw new InvalidArgumentException('Invalid count parameter.');
}
return [ return [
'results' => $this->searchService->excerptSearch($request->get('q')), 'results' => $this->searchService->excerptSearch($request->get('q'), $count),
]; ];
} }
} }

View file

@ -3,7 +3,6 @@
namespace App\Http\Controllers\API\Search; namespace App\Http\Controllers\API\Search;
use App\Http\Controllers\API\Controller; use App\Http\Controllers\API\Controller;
use App\Models\Song;
use App\Services\SearchService; use App\Services\SearchService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use InvalidArgumentException; use InvalidArgumentException;
@ -24,11 +23,7 @@ class SongSearchController extends Controller
} }
return [ return [
'songs' => $this->searchService->searchSongs($request->get('q')) 'songs' => $this->searchService->searchSongs($request->get('q')),
->get()
->map(static function (Song $song): string {
return $song->id;
}),
]; ];
} }
} }

View file

@ -2,15 +2,20 @@
namespace App\Services; namespace App\Services;
use App\Models\Album;
use App\Models\Artist;
use App\Models\Song;
use App\Repositories\AlbumRepository; use App\Repositories\AlbumRepository;
use App\Repositories\ArtistRepository; use App\Repositories\ArtistRepository;
use App\Repositories\SongRepository; use App\Repositories\SongRepository;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Laravel\Scout\Builder; use Laravel\Scout\Builder;
class SearchService class SearchService
{ {
public const DEFAULT_EXCERPT_RESULT_COUNT = 6;
private $songRepository; private $songRepository;
private $albumRepository; private $albumRepository;
private $artistRepository; private $artistRepository;
@ -26,23 +31,38 @@ class SearchService
} }
/** @return array<mixed> */ /** @return array<mixed> */
public function excerptSearch(string $keywords): array public function excerptSearch(string $keywords, int $count): array
{ {
return [ return [
'songs' => self::getTopResults($this->songRepository->search($keywords)), 'songs' => self::getTopResults($this->songRepository->search($keywords), $count)
'artists' => self::getTopResults($this->artistRepository->search($keywords)), ->map(static function (Song $song): string {
'albums' => self::getTopResults($this->albumRepository->search($keywords)), return $song->id;
}),
'artists' => self::getTopResults($this->artistRepository->search($keywords), $count)
->map(static function (Artist $artist): int {
return $artist->id;
}),
'albums' => self::getTopResults($this->albumRepository->search($keywords), $count)
->map(static function (Album $album): int {
return $album->id;
}),
]; ];
} }
/** @return Collection|array<Model> */ /** @return Collection|array<Model> */
private static function getTopResults(Builder $query, int $count = 6): Collection private static function getTopResults(Builder $query, int $count): Collection
{ {
return $query->take($count)->get(); return $query->take($count)->get();
} }
public function searchSongs(string $keywords): Builder /** @return Collection|array<string> */
public function searchSongs(string $keywords): Collection
{ {
return $this->songRepository->search($keywords); return $this->songRepository
->search($keywords)
->get()
->map(static function (Song $song): string {
return $song->id;
});
} }
} }