fix: 修复Server搜索问题
All checks were successful
deploy to server / build-and-deploy (push) Successful in 3m7s
All checks were successful
deploy to server / build-and-deploy (push) Successful in 3m7s
- 将meilisearch搜索服务移至Server端
This commit is contained in:
85
server/services/search.ts
Normal file
85
server/services/search.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { MeiliSearch } from 'meilisearch';
|
||||
import type { SearchParams } from 'meilisearch';
|
||||
|
||||
export class MeiliService {
|
||||
private client: MeiliSearch;
|
||||
private indexes: string[];
|
||||
|
||||
constructor() {
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
|
||||
const host = runtimeConfig.public?.meili?.host;
|
||||
const apiKey = runtimeConfig.public?.meili?.searchKey;
|
||||
|
||||
if (!host) throw new Error('Meilisearch host not configured');
|
||||
if (!apiKey) throw new Error('Meilisearch server key missing');
|
||||
|
||||
this.client = new MeiliSearch({ host, apiKey });
|
||||
this.indexes = runtimeConfig.public.meili.indexes || [];
|
||||
}
|
||||
|
||||
/** 构建索引名: products_zh-CN */
|
||||
private buildIndexNames(locale?: string) {
|
||||
if (!locale) return this.indexes;
|
||||
return this.indexes.map((i) => `${i.trim()}_${locale}`);
|
||||
}
|
||||
|
||||
/** Server 封装的搜索方法 */
|
||||
async search<
|
||||
K extends keyof MeiliIndexMap = keyof MeiliIndexMap,
|
||||
T extends MeiliIndexMap[K] = MeiliIndexMap[K],
|
||||
>(query: string, params: SearchParams = {}, locale?: string) {
|
||||
const trimmedQuery = query.trim();
|
||||
if (!trimmedQuery) return [];
|
||||
|
||||
const activeIndexes = this.buildIndexNames(locale);
|
||||
if (!activeIndexes.length) {
|
||||
logger.warn('No Meilisearch indexes configured.');
|
||||
return [];
|
||||
}
|
||||
|
||||
const requests = activeIndexes.map(async (indexUID) => {
|
||||
const response = await this.client
|
||||
.index(indexUID)
|
||||
.search<T>(trimmedQuery, {
|
||||
limit: params.limit ?? 10,
|
||||
...params,
|
||||
});
|
||||
|
||||
return {
|
||||
indexUid: indexUID,
|
||||
rawIndex: indexUID.replace(`_${locale}`, ''),
|
||||
hits: response.hits,
|
||||
estimatedTotalHits: response.estimatedTotalHits ?? response.hits.length,
|
||||
processingTimeMs: response.processingTimeMs ?? 0,
|
||||
} as SearchSection<T>;
|
||||
});
|
||||
|
||||
// 并行安全执行
|
||||
const results = await Promise.allSettled(requests);
|
||||
|
||||
results
|
||||
.filter(
|
||||
(result): result is PromiseRejectedResult =>
|
||||
result.status === 'rejected'
|
||||
)
|
||||
.forEach((result) => {
|
||||
logger.error('Meilisearch query failed', result.reason);
|
||||
});
|
||||
|
||||
const fulfilled = results.filter(
|
||||
(r): r is PromiseFulfilledResult<SearchSection<T>> =>
|
||||
r.status === 'fulfilled'
|
||||
);
|
||||
|
||||
return fulfilled.map((r) => r.value).filter((s) => s.hits.length > 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** 单例 */
|
||||
let instance: MeiliService | null = null;
|
||||
|
||||
export const getMeiliService = () => {
|
||||
if (!instance) instance = new MeiliService();
|
||||
return instance;
|
||||
};
|
||||
Reference in New Issue
Block a user