add meili search

This commit is contained in:
liuyi 2025-06-01 18:46:13 +08:00
parent fdd9d80310
commit 996f887d73
8 changed files with 115 additions and 0 deletions

View File

@ -30,6 +30,7 @@
"class-validator": "^0.14.2",
"deepmerge": "^4.3.1",
"lodash": "^4.17.21",
"meilisearch": "^0.50.0",
"mysql2": "^3.14.1",
"reflect-metadata": "^0.1.13",
"rimraf": "^5.0.1",

View File

@ -35,6 +35,9 @@ importers:
lodash:
specifier: ^4.17.21
version: 4.17.21
meilisearch:
specifier: ^0.50.0
version: 0.50.0
mysql2:
specifier: ^3.14.1
version: 3.14.1
@ -2744,6 +2747,9 @@ packages:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
meilisearch@0.50.0:
resolution: {integrity: sha512-9IzIkobvnuS18Eg4dq/eJB9W+eXqeLZjNRgq/kKMswSmVYYSQsXqGgSuCA0JkF+o5RwJlwIsieQee6rh313VhA==}
memfs@3.5.3:
resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==}
engines: {node: '>= 4.0.0'}
@ -7316,6 +7322,8 @@ snapshots:
media-typer@0.3.0:
optional: true
meilisearch@0.50.0: {}
memfs@3.5.3:
dependencies:
fs-monkey: 1.0.6

View File

@ -4,6 +4,9 @@ import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
import { AppInterceptor } from '@/modules/core/providers/app.interceptor';
import { MEILI_CONFIG } from '@/modules/meilisearch/meili.config';
import { MeiliModule } from '@/modules/meilisearch/meili.module';
import { content, database } from './config';
import { DEFAULT_VALIDATION_CONFIG } from './modules/content/constants';
@ -18,6 +21,7 @@ import { DatabaseModule } from './modules/database/database.module';
ContentModule.forRoot(content),
CoreModule.forRoot(),
DatabaseModule.forRoot(database),
MeiliModule.forRoot(MEILI_CONFIG),
],
providers: [
{

View File

@ -0,0 +1,9 @@
import { MeiliConfig } from '@/modules/meilisearch/types';
export const MEILI_CONFIG = (): MeiliConfig => [
{
name: 'default',
host: 'http://localhost:7700',
apiKey: 'masterKey',
},
];

View File

@ -0,0 +1,28 @@
import { DynamicModule, Module } from '@nestjs/common';
import { MeiliService } from '@/modules/meilisearch/meili.service';
import { MeiliConfig } from '@/modules/meilisearch/types';
import { createMeiliOptions } from '@/modules/meilisearch/utils';
@Module({})
export class MeiliModule {
static forRoot(configRegister: () => MeiliConfig): DynamicModule {
return {
global: true,
module: MeiliModule,
providers: [
{
provide: MeiliService,
useFactory: async () => {
const service = new MeiliService(
await createMeiliOptions(configRegister()),
);
await service.createClients();
return service;
},
},
],
exports: [MeiliService],
};
}
}

View File

@ -0,0 +1,42 @@
import { Injectable } from '@nestjs/common';
import { isNil } from 'lodash';
import { MeiliSearch } from 'meilisearch';
import { MeiliConfig } from '@/modules/meilisearch/types';
@Injectable()
export class MeiliService {
protected options: MeiliConfig;
protected clients: Map<string, MeiliSearch> = new Map();
constructor(options: MeiliConfig) {
this.options = options;
}
getOptions() {
return this.options;
}
async createClients() {
for (const option of this.options) {
this.clients.set(option.name, new MeiliSearch(option));
}
}
getClient(name?: string): MeiliSearch {
let key = 'default';
if (!isNil(name)) {
key = name;
}
if (!this.clients.has(key)) {
throw new Error(`No client found for ${name}`);
}
return this.clients.get(key);
}
getClients(): Map<string, MeiliSearch> {
return this.clients;
}
}

View File

@ -0,0 +1,5 @@
import { Config } from 'meilisearch';
export type MeiliConfig = MeiliOption[];
export type MeiliOption = Config & { name: string };

View File

@ -0,0 +1,18 @@
import { MeiliConfig } from '@/modules/meilisearch/types';
export const createMeiliOptions = async (config: MeiliConfig): Promise<MeiliConfig | undefined> => {
if (config.length < 0) {
return config;
}
let options: MeiliConfig = [...config];
const names = options.map(({ name }) => name);
if (!names.includes('default')) {
options[0].name = 'default';
} else if (names.filter((name) => name === 'default').length > 0) {
options = options.reduce(
(o, n) => (o.map(({ name }) => name).includes('default') ? o : [...o, n]),
[],
);
}
return options;
};