Files
jinshen-website/app/composables/useMeilisearch.ts
R2m1liA 660892f9e7
All checks were successful
deploy to server / build-and-deploy (push) Successful in 3m11s
fix: 将原项目的console输出改为logger输出
- 添加eslint,不允许使用console输出
2025-11-08 14:40:41 +08:00

134 lines
3.3 KiB
TypeScript

import { MeiliSearch } from 'meilisearch';
import type { SearchParams } from 'meilisearch';
const parseIndexes = (
indexes: string | string[] | undefined,
locale?: string
): string[] => {
if (!indexes) {
return [];
}
let suffix = '';
if (locale) {
suffix = `_${locale}`;
}
if (Array.isArray(indexes)) {
return indexes.map((item) => `${item.trim()}${suffix}`).filter(Boolean);
}
return indexes
.split(',')
.map((item) => `${item.trim()}${suffix}`)
.filter(Boolean);
};
export const useMeilisearch = () => {
const runtimeConfig = useRuntimeConfig();
const indexes = computed(() =>
parseIndexes(runtimeConfig.public?.meili?.indexes)
);
let meiliClient: MeiliSearch | null = null;
const ensureClient = () => {
if (meiliClient) return meiliClient;
const host = runtimeConfig.public?.meili?.host;
if (!host) {
logger.warn('Meilisearch host is not configured.');
return null;
}
const apiKey = runtimeConfig.public?.meili?.searchKey;
meiliClient = new MeiliSearch({
host,
apiKey: apiKey || undefined,
});
return meiliClient;
};
/**
* 泛型搜索函数
* @template T 文档类型, 如 MeiliProductIndex
* ---
* @param query 搜索关键词
* @param params 其他搜索参数
* @returns 搜索结果数组
*/
async function search<
K extends MeiliSearchItemType = MeiliSearchItemType,
T extends MeiliIndexMap[K] = MeiliIndexMap[K],
>(
query: string,
params: SearchParams = {},
searchLocale?: string
): Promise<SearchSection<T>[]> {
const trimmedQuery = query.trim();
if (!trimmedQuery) {
return [];
}
const client = ensureClient();
if (!client) {
return [];
}
const activeIndexes = indexes.value as K[];
if (!activeIndexes.length) {
logger.warn('No Meilisearch indexes configured.');
return [];
}
const rawIndexMap = Object.fromEntries(
activeIndexes.map((index) => [`${index}_${searchLocale}`, index])
);
const indexesWithLocale = activeIndexes.map(
(index) => index + (searchLocale ? `_${searchLocale}` : '')
);
const requests = indexesWithLocale.map(async (indexUid) => {
const response = await client.index(indexUid).search<T>(trimmedQuery, {
limit: params.limit ?? 10,
...params,
});
return {
indexUid,
response,
} satisfies RawSearchSection<T>;
});
const settled = await Promise.allSettled(requests);
settled
.filter(
(result): result is PromiseRejectedResult =>
result.status === 'rejected'
)
.forEach((result) => {
logger.error('Meilisearch query failed', result.reason);
});
return settled
.filter(
(result): result is PromiseFulfilledResult<RawSearchSection<T>> =>
result.status === 'fulfilled'
)
.map((result) => {
const { indexUid, response } = result.value;
return {
indexUid: indexUid,
rawIndex: rawIndexMap[indexUid],
hits: response.hits,
estimatedTotalHits:
response.estimatedTotalHits ?? response.hits.length,
processingTimeMs: response.processingTimeMs ?? 0,
};
});
}
return {
search,
indexes,
};
};