Add meilisearch
This commit is contained in:
@@ -20,3 +20,7 @@ API_KEYS=SCOROTEST,ROBOTO,SCORO
|
||||
API_KEY_SCORO_TEST=SCOROTEST
|
||||
API_KEY_ROBOT=ROBOTO
|
||||
API_KEY_SCORO=SCORO
|
||||
MEILI_HTTP_ADDR="http://meilisearch:7700"
|
||||
MEILI_MASTER_KEY="ghvjkgisbgkbgskegblfqbgjkebbhgwkjfb"
|
||||
|
||||
# vi: ft=sh
|
||||
|
||||
12203
back/package-lock.json
generated
12203
back/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,7 @@
|
||||
"fs": "^0.0.1-security",
|
||||
"jsdom": "^22.1.0",
|
||||
"json-logger-service": "^9.0.1",
|
||||
"meilisearch": "^0.35.0",
|
||||
"node-fetch": "^2.6.12",
|
||||
"nodemailer": "^6.9.5",
|
||||
"opensheetmusicdisplay": "^1.8.4",
|
||||
|
||||
@@ -2,9 +2,10 @@ import { Module } from "@nestjs/common";
|
||||
import { PrismaModule } from "src/prisma/prisma.module";
|
||||
import { ArtistController } from "./artist.controller";
|
||||
import { ArtistService } from "./artist.service";
|
||||
import { SearchModule } from 'src/search/search.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaModule, SearchModule],
|
||||
controllers: [ArtistController],
|
||||
providers: [ArtistService],
|
||||
})
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Prisma, Artist } from "@prisma/client";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Prisma, Artist } from '@prisma/client';
|
||||
import { PrismaService } from 'src/prisma/prisma.service';
|
||||
import { MeiliService } from 'src/search/meilisearch.service';
|
||||
|
||||
@Injectable()
|
||||
export class ArtistService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private search: MeiliService,
|
||||
) {}
|
||||
|
||||
async create(data: Prisma.ArtistCreateInput): Promise<Artist> {
|
||||
return this.prisma.artist.create({
|
||||
const ret = await this.prisma.artist.create({
|
||||
data,
|
||||
});
|
||||
await this.search.index('artists').addDocuments([ret]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
async get(
|
||||
@@ -42,8 +48,10 @@ export class ArtistService {
|
||||
}
|
||||
|
||||
async delete(where: Prisma.ArtistWhereUniqueInput): Promise<Artist> {
|
||||
return this.prisma.artist.delete({
|
||||
const ret = await this.prisma.artist.delete({
|
||||
where,
|
||||
});
|
||||
await this.search.index('artists').deleteDocument(ret.id);
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
29
back/src/search/meilisearch.service.ts
Normal file
29
back/src/search/meilisearch.service.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import MeiliSearch, { DocumentOptions, Settings } from 'meilisearch';
|
||||
|
||||
@Injectable()
|
||||
export class MeiliService extends MeiliSearch implements OnModuleInit {
|
||||
constructor() {
|
||||
super({
|
||||
host: process.env.MEILI_ADDR || 'http://meilisearch:7700',
|
||||
apiKey: process.env.MEILI_MASTER_KEY,
|
||||
});
|
||||
}
|
||||
|
||||
async definedIndex(uid: string, opts: Settings) {
|
||||
let task = await this.createIndex(uid, { primaryKey: 'id' });
|
||||
await this.waitForTask(task.taskUid);
|
||||
task = await this.index(uid).updateSettings(opts);
|
||||
await this.waitForTask(task.taskUid);
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
await this.definedIndex('songs', {
|
||||
searchableAttributes: ['name', 'artist'],
|
||||
filterableAttributes: ['artistId', 'genreId'],
|
||||
});
|
||||
await this.definedIndex('artists', {
|
||||
searchableAttributes: ['name'],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -25,27 +25,23 @@ 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" })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiOperation({ description: 'Search a song' })
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid token' })
|
||||
async searchSong(
|
||||
@Request() req: any,
|
||||
@Query("include") include: string,
|
||||
@Param("query") query: string,
|
||||
@Param('query') query: string,
|
||||
@Param('artistId') artistId: number,
|
||||
): Promise<Song[] | null> {
|
||||
try {
|
||||
const ret = await this.searchService.songByGuess(
|
||||
query,
|
||||
req.user?.id,
|
||||
mapInclude(include, req, SongController.includableFields),
|
||||
);
|
||||
const ret = await this.searchService.searchSong(query, artistId);
|
||||
if (!ret.length) throw new NotFoundException();
|
||||
else return ret;
|
||||
} catch (error) {
|
||||
|
||||
@@ -4,11 +4,12 @@ import { SearchController } from "./search.controller";
|
||||
import { HistoryModule } from "src/history/history.module";
|
||||
import { PrismaModule } from "src/prisma/prisma.module";
|
||||
import { SongService } from "src/song/song.service";
|
||||
import { MeiliService } from "./meilisearch.service";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, HistoryModule],
|
||||
controllers: [SearchController],
|
||||
providers: [SearchService, SongService],
|
||||
exports: [SearchService],
|
||||
providers: [SearchService, SongService, MeiliService],
|
||||
exports: [SearchService, MeiliService],
|
||||
})
|
||||
export class SearchModule {}
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
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 { 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 {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private history: HistoryService,
|
||||
private search: MeiliService,
|
||||
) {}
|
||||
|
||||
async songByGuess(
|
||||
query: string,
|
||||
userID: number,
|
||||
include?: Prisma.SongInclude,
|
||||
): Promise<Song[]> {
|
||||
return this.prisma.song.findMany({
|
||||
async searchSong(query: string, artistId?: number): Promise<Song[]> {
|
||||
if (query.length === 0) {
|
||||
return await this.prisma.song.findMany({
|
||||
where: {
|
||||
name: { contains: query, mode: "insensitive" },
|
||||
artistId,
|
||||
},
|
||||
include,
|
||||
});
|
||||
}
|
||||
return (await this.search
|
||||
.index('songs')
|
||||
.search(query, { filter: `artistId = ${artistId}` })) as any;
|
||||
}
|
||||
|
||||
async genreByGuess(
|
||||
query: string,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { SongService } from "./song.service";
|
||||
import { SongController } from "./song.controller";
|
||||
import { PrismaModule } from "src/prisma/prisma.module";
|
||||
import { HistoryModule } from "src/history/history.module";
|
||||
import { Module } from '@nestjs/common';
|
||||
import { SongService } from './song.service';
|
||||
import { SongController } from './song.controller';
|
||||
import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
import { HistoryModule } from 'src/history/history.module';
|
||||
import { SearchModule } from 'src/search/search.module';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, HistoryModule],
|
||||
imports: [PrismaModule, HistoryModule, SearchModule],
|
||||
providers: [SongService],
|
||||
controllers: [SongController],
|
||||
})
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Prisma, Song } from "@prisma/client";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { MeiliService } from "src/search/meilisearch.service";
|
||||
import { generateSongAssets } from "src/assetsgenerator/generateImages_browserless";
|
||||
|
||||
@Injectable()
|
||||
export class SongService {
|
||||
// number is the song id
|
||||
private assetCreationTasks: Map<number, Promise<void>>;
|
||||
constructor(private prisma: PrismaService) {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private search: MeiliService,
|
||||
) {
|
||||
this.assetCreationTasks = new Map();
|
||||
}
|
||||
|
||||
@@ -34,9 +38,19 @@ export class SongService {
|
||||
}
|
||||
|
||||
async createSong(data: Prisma.SongCreateInput): Promise<Song> {
|
||||
return this.prisma.song.create({
|
||||
const song = await this.prisma.song.create({
|
||||
data,
|
||||
});
|
||||
// Inculde the name of the artist in the song document to make search easier.
|
||||
const artist = song.artistId
|
||||
? await this.prisma.artist.findFirst({
|
||||
where: { id: song.artistId },
|
||||
})
|
||||
: null;
|
||||
await this.search
|
||||
.index("songs")
|
||||
.addDocuments([{ ...song, artist: artist?.name }]);
|
||||
return song;
|
||||
}
|
||||
|
||||
async song(
|
||||
@@ -69,8 +83,10 @@ export class SongService {
|
||||
}
|
||||
|
||||
async deleteSong(where: Prisma.SongWhereUniqueInput): Promise<Song> {
|
||||
return this.prisma.song.delete({
|
||||
const ret = await this.prisma.song.delete({
|
||||
where,
|
||||
});
|
||||
await this.search.index("songs").deleteDocument(ret.id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ networks:
|
||||
|
||||
volumes:
|
||||
scoro_logs:
|
||||
meilisearch:
|
||||
|
||||
|
||||
services:
|
||||
@@ -86,3 +87,12 @@ services:
|
||||
- "./front/nginx.conf.template.dev:/etc/nginx/templates/default.conf.template:ro"
|
||||
ports:
|
||||
- "4567:4567"
|
||||
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.4
|
||||
ports:
|
||||
- "7000:7000"
|
||||
volumes:
|
||||
- meilisearch:/meili_data
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
@@ -3,6 +3,8 @@ networks:
|
||||
|
||||
volumes:
|
||||
scoro_logs:
|
||||
meilisearch:
|
||||
db:
|
||||
|
||||
services:
|
||||
back:
|
||||
@@ -53,3 +55,12 @@ services:
|
||||
- "back"
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.4
|
||||
ports:
|
||||
- "7000:7000"
|
||||
volumes:
|
||||
- meilisearch:/meili_data
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
@@ -5,6 +5,7 @@ networks:
|
||||
volumes:
|
||||
db:
|
||||
scoro_logs:
|
||||
meilisearch:
|
||||
|
||||
|
||||
services:
|
||||
@@ -59,3 +60,12 @@ services:
|
||||
- "back"
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
meilisearch:
|
||||
image: getmeili/meilisearch:v1.4
|
||||
ports:
|
||||
- "7000:7000"
|
||||
volumes:
|
||||
- meilisearch:/meili_data
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
Reference in New Issue
Block a user