From 02ccf58457a97156240bbcd849e80cbef49c8639 Mon Sep 17 00:00:00 2001 From: liuyi Date: Sun, 1 Jun 2025 21:24:43 +0800 Subject: [PATCH] add meili search --- src/modules/content/content.module.ts | 7 +++++ src/modules/content/dtos/post.dto.ts | 3 ++ src/modules/content/services/index.ts | 1 + src/modules/content/services/post.service.ts | 30 +++++++++++++++++-- .../content/services/search.service.ts | 2 +- src/modules/content/types.ts | 2 +- 6 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/modules/content/content.module.ts b/src/modules/content/content.module.ts index 6e2540f..cf8d2cf 100644 --- a/src/modules/content/content.module.ts +++ b/src/modules/content/content.module.ts @@ -6,6 +6,7 @@ import * as controllers from '@/modules/content/controllers'; import * as entities from '@/modules/content/entities'; import * as repositories from '@/modules/content/repositories'; import * as services from '@/modules/content/services'; +import { SearchService } from '@/modules/content/services'; import { SanitizeService } from '@/modules/content/services/SanitizeService'; import { PostService } from '@/modules/content/services/post.service'; @@ -31,23 +32,29 @@ export class ContentModule { repositories.CategoryRepository, repositories.TagRepository, services.CategoryService, + { token: services.SearchService, optional: true }, ], useFactory( postRepository: repositories.PostRepository, categoryRepository: repositories.CategoryRepository, tagRepository: repositories.TagRepository, categoryService: services.CategoryService, + searchService: SearchService, ) { return new PostService( postRepository, categoryRepository, categoryService, tagRepository, + searchService, config.SearchType, ); }, }, ]; + if (config.SearchType === 'meili') { + providers.push(services.SearchService); + } return { module: ContentModule, imports: [ diff --git a/src/modules/content/dtos/post.dto.ts b/src/modules/content/dtos/post.dto.ts index 11a5d2b..dc5048e 100644 --- a/src/modules/content/dtos/post.dto.ts +++ b/src/modules/content/dtos/post.dto.ts @@ -32,6 +32,9 @@ export class QueryPostDto implements PaginateOptions { @IsOptional() isPublished?: boolean; + @IsOptional() + search?: string; + @IsEnum(PostOrder, { message: `The sorting rule must be one of ${Object.values(PostOrder).join(',')}`, }) diff --git a/src/modules/content/services/index.ts b/src/modules/content/services/index.ts index 7531cb4..a1c2abc 100644 --- a/src/modules/content/services/index.ts +++ b/src/modules/content/services/index.ts @@ -1,3 +1,4 @@ export * from './category.service'; export * from './tag.service'; export * from './comment.service'; +export * from './search.service'; diff --git a/src/modules/content/services/post.service.ts b/src/modules/content/services/post.service.ts index 55da4aa..49bb9f1 100644 --- a/src/modules/content/services/post.service.ts +++ b/src/modules/content/services/post.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { isNil } from '@nestjs/common/utils/shared.utils'; -import { isArray, isFunction, omit } from 'lodash'; +import { isArray, isFunction, omit, pick } from 'lodash'; import { EntityNotFoundError, In, IsNull, Not, SelectQueryBuilder } from 'typeorm'; import { PostOrder } from '@/modules/content/constants'; @@ -9,6 +9,7 @@ import { CreatePostDto, QueryPostDto, UpdatePostDto } from '@/modules/content/dt import { PostEntity } from '@/modules/content/entities/post.entity'; import { CategoryRepository } from '@/modules/content/repositories'; import { PostRepository } from '@/modules/content/repositories/post.repository'; +import { SearchService } from '@/modules/content/services/search.service'; import { SearchType } from '@/modules/content/types'; import { SelectTrashMode } from '@/modules/database/constants'; import { QueryHook } from '@/modules/database/types'; @@ -29,10 +30,17 @@ export class PostService { protected categoryRepository: CategoryRepository, protected categoryService: CategoryService, protected tagRepository: TagRepository, + protected searchService?: SearchService, protected searchType: SearchType = 'mysql', ) {} async paginate(options: QueryPostDto, callback?: QueryHook) { + if (!isNil(this.searchService) && !isNil(options.search) && this.searchType === 'meili') { + return this.searchService.search( + options.search, + pick(options, ['trashed', 'page', 'limit']), + ); + } const qb = await this.buildListQuery(this.repository.buildBaseQB(), options, callback); return paginate(qb, options); } @@ -62,7 +70,11 @@ export class PostService { publishedAt, }; const item = await this.repository.save(createPostDto); - return this.detail(item.id); + const result = await this.detail(item.id); + if (!isNil(this.searchService)) { + await this.searchService.create(result); + } + return result; } async update(data: UpdatePostDto) { @@ -88,7 +100,11 @@ export class PostService { ...omit(data, ['id', 'publish', 'tags', 'category']), publishedAt, }); - return this.detail(data.id); + const result = await this.detail(data.id); + if (!isNil(this.searchService)) { + await this.searchService.update([result]); + } + return result; } async delete(ids: string[], trash?: boolean) { @@ -105,8 +121,15 @@ export class PostService { ...(await this.repository.remove(directs)), ...(await this.repository.softRemove(softs)), ]; + if (!isNil(this.searchService)) { + await this.searchService.delete(directs.map((item) => item.id)); + await this.searchService.update(softs); + } } else { result = await this.repository.remove(items); + if (!isNil(this.searchService)) { + await this.searchService.delete(ids); + } } return result; } @@ -118,6 +141,7 @@ export class PostService { .withDeleted() .getMany(); const trashes = items.filter((item) => !isNil(item.deleteAt)); + await this.searchService.update(trashes); const trashedIds = trashes.map((item) => item.id); if (trashedIds.length < 1) { return []; diff --git a/src/modules/content/services/search.service.ts b/src/modules/content/services/search.service.ts index 8ca2596..9538a38 100644 --- a/src/modules/content/services/search.service.ts +++ b/src/modules/content/services/search.service.ts @@ -14,7 +14,7 @@ import { SelectTrashMode } from '@/modules/database/constants'; import { MeiliService } from '@/modules/meilisearch/meili.service'; export class SearchService implements OnModuleInit { - index = 'content'; + private index = 'content'; protected client: MeiliSearch; diff --git a/src/modules/content/types.ts b/src/modules/content/types.ts index dc9daef..d8595e7 100644 --- a/src/modules/content/types.ts +++ b/src/modules/content/types.ts @@ -1,6 +1,6 @@ import { SelectTrashMode } from '@/modules/database/constants'; -export type SearchType = 'mysql'; +export type SearchType = 'mysql' | 'meili'; export interface ContentConfig { SearchType?: SearchType;