Implement search controler
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import {
|
||||
Controller,
|
||||
DefaultValuePipe,
|
||||
Get,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Query,
|
||||
Request,
|
||||
UseGuards,
|
||||
@@ -25,51 +27,31 @@ import { SongController } from "src/song/song.controller";
|
||||
import { GenreController } from "src/genre/genre.controller";
|
||||
import { ArtistController } from "src/artist/artist.controller";
|
||||
|
||||
@ApiTags('search')
|
||||
@Controller('search')
|
||||
@ApiTags("search")
|
||||
@Controller("search")
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class SearchController {
|
||||
constructor(private readonly searchService: SearchService) {}
|
||||
|
||||
@Get("songs/:query")
|
||||
@ApiOkResponse({ type: _Song, isArray: true })
|
||||
@ApiOperation({ description: 'Search a song' })
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid token' })
|
||||
@ApiOperation({ description: "Search a song" })
|
||||
@ApiUnauthorizedResponse({ description: "Invalid token" })
|
||||
async searchSong(
|
||||
@Request() req: any,
|
||||
@Param('query') query: string,
|
||||
@Param('artistId') artistId: number,
|
||||
): Promise<Song[] | null> {
|
||||
try {
|
||||
const ret = await this.searchService.searchSong(query, artistId);
|
||||
if (!ret.length) throw new NotFoundException();
|
||||
else return ret;
|
||||
} catch (error) {
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
}
|
||||
|
||||
@Get("genres/:query")
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiUnauthorizedResponse({ description: "Invalid token" })
|
||||
@ApiOkResponse({ type: _Genre, isArray: true })
|
||||
@ApiOperation({ description: "Search a genre" })
|
||||
async searchGenre(
|
||||
@Request() req: any,
|
||||
@Query("include") include: string,
|
||||
@Param("query") query: string,
|
||||
): Promise<Genre[] | null> {
|
||||
try {
|
||||
const ret = await this.searchService.genreByGuess(
|
||||
query,
|
||||
req.user?.id,
|
||||
mapInclude(include, req, GenreController.includableFields),
|
||||
);
|
||||
if (!ret.length) throw new NotFoundException();
|
||||
else return ret;
|
||||
} catch (error) {
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
@Param("artistId") artistId: number,
|
||||
@Query("include") include: string,
|
||||
@Query("skip", new DefaultValuePipe(0), ParseIntPipe) skip: number,
|
||||
@Query("take", new DefaultValuePipe(20), ParseIntPipe) take: number,
|
||||
): Promise<Song[] | null> {
|
||||
return await this.searchService.searchSong(
|
||||
query,
|
||||
artistId,
|
||||
mapInclude(include, req, SongController.includableFields),
|
||||
skip,
|
||||
take,
|
||||
);
|
||||
}
|
||||
|
||||
@Get("artists/:query")
|
||||
@@ -81,17 +63,14 @@ export class SearchController {
|
||||
@Request() req: any,
|
||||
@Query("include") include: string,
|
||||
@Param("query") query: string,
|
||||
@Query("skip", new DefaultValuePipe(0), ParseIntPipe) skip: number,
|
||||
@Query("take", new DefaultValuePipe(20), ParseIntPipe) take: number,
|
||||
): Promise<Artist[] | null> {
|
||||
try {
|
||||
const ret = await this.searchService.artistByGuess(
|
||||
query,
|
||||
req.user?.id,
|
||||
mapInclude(include, req, ArtistController.includableFields),
|
||||
);
|
||||
if (!ret.length) throw new NotFoundException();
|
||||
else return ret;
|
||||
} catch (error) {
|
||||
throw new InternalServerErrorException();
|
||||
}
|
||||
return await this.searchService.searchArtists(
|
||||
query,
|
||||
mapInclude(include, req, ArtistController.includableFields),
|
||||
skip,
|
||||
take,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Artist, Prisma, Song, Genre } from '@prisma/client';
|
||||
import { HistoryService } from 'src/history/history.service';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { MeiliService } from './meilisearch.service';
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Artist, Prisma, Song, Genre } from "@prisma/client";
|
||||
import { HistoryService } from "src/history/history.service";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { MeiliService } from "./meilisearch.service";
|
||||
|
||||
@Injectable()
|
||||
export class SearchService {
|
||||
@@ -12,42 +12,68 @@ export class SearchService {
|
||||
private search: MeiliService,
|
||||
) {}
|
||||
|
||||
async searchSong(query: string, artistId?: number): Promise<Song[]> {
|
||||
async searchSong(
|
||||
query: string,
|
||||
artistId?: number,
|
||||
include?: Prisma.SongInclude,
|
||||
skip?: number,
|
||||
take?: number,
|
||||
): Promise<Song[]> {
|
||||
if (query.length === 0) {
|
||||
return await this.prisma.song.findMany({
|
||||
where: {
|
||||
artistId,
|
||||
},
|
||||
take,
|
||||
skip,
|
||||
include,
|
||||
});
|
||||
}
|
||||
return (await this.search
|
||||
.index('songs')
|
||||
.search(query, { filter: `artistId = ${artistId}` })) as any;
|
||||
const ids = (
|
||||
await this.search.index("songs").search(query, {
|
||||
limit: take,
|
||||
offset: skip,
|
||||
...(artistId ? { filter: `artistId = ${artistId}` } : {}),
|
||||
})
|
||||
).hits.map((x) => x.id);
|
||||
|
||||
return (
|
||||
await this.prisma.song.findMany({
|
||||
where: {
|
||||
id: { in: ids },
|
||||
},
|
||||
include,
|
||||
})
|
||||
).sort((x) => ids.indexOf(x.id));
|
||||
}
|
||||
|
||||
async genreByGuess(
|
||||
async searchArtists(
|
||||
query: string,
|
||||
userID: number,
|
||||
include?: Prisma.GenreInclude,
|
||||
): Promise<Genre[]> {
|
||||
return this.prisma.genre.findMany({
|
||||
where: {
|
||||
name: { contains: query, mode: "insensitive" },
|
||||
},
|
||||
include,
|
||||
});
|
||||
}
|
||||
|
||||
async artistByGuess(
|
||||
query: string,
|
||||
userID: number,
|
||||
include?: Prisma.ArtistInclude,
|
||||
skip?: number,
|
||||
take?: number,
|
||||
): Promise<Artist[]> {
|
||||
return this.prisma.artist.findMany({
|
||||
where: {
|
||||
name: { contains: query, mode: "insensitive" },
|
||||
},
|
||||
include,
|
||||
});
|
||||
if (query.length === 0) {
|
||||
return this.prisma.artist.findMany({
|
||||
take,
|
||||
skip,
|
||||
include,
|
||||
});
|
||||
}
|
||||
const ids = (
|
||||
await this.search.index("artists").search(query, {
|
||||
limit: take,
|
||||
offset: skip,
|
||||
})
|
||||
).hits.map((x) => x.id);
|
||||
|
||||
return (
|
||||
await this.prisma.artist.findMany({
|
||||
where: {
|
||||
id: { in: ids },
|
||||
},
|
||||
include,
|
||||
})
|
||||
).sort((x) => ids.indexOf(x.id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +90,6 @@ services:
|
||||
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.4
|
||||
ports:
|
||||
- "7000:7000"
|
||||
volumes:
|
||||
- meilisearch:/meili_data
|
||||
env_file:
|
||||
|
||||
@@ -58,8 +58,6 @@ services:
|
||||
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.4
|
||||
ports:
|
||||
- "7000:7000"
|
||||
volumes:
|
||||
- meilisearch:/meili_data
|
||||
env_file:
|
||||
|
||||
@@ -64,7 +64,7 @@ services:
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.4
|
||||
ports:
|
||||
- "7000:7000"
|
||||
- "7700:7700"
|
||||
volumes:
|
||||
- meilisearch:/meili_data
|
||||
env_file:
|
||||
|
||||
Reference in New Issue
Block a user