Files
jinshen-website/app/composables/useMeilisearch.ts
R2m1liA 359aaec8a9 style: 格式化项目代码
- 根据prettier配置格式化整个项目的代码
2025-09-17 15:50:29 +08:00

133 lines
3.4 KiB
TypeScript

import { MeiliSearch } from 'meilisearch';
import type { SearchParams, SearchResponse } from 'meilisearch';
interface RawSearchSection {
indexUid: string;
response: SearchResponse<Record<string, unknown>>;
}
export interface SearchHit extends Record<string, unknown> {
indexUid: string;
objectID?: string | number;
}
export interface SearchSection {
indexUid: string;
hits: SearchHit[];
estimatedTotalHits: number;
processingTimeMs: number;
}
const parseIndexes = (indexes: string | string[] | undefined): string[] => {
if (!indexes) {
return [];
}
if (Array.isArray(indexes)) {
return indexes.map((item) => item.trim()).filter(Boolean);
}
return indexes
.split(',')
.map((item) => item.trim())
.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) {
console.warn('Meilisearch host is not configured.');
return null;
}
const apiKey = runtimeConfig.public?.meili?.searchKey;
meiliClient = new MeiliSearch({
host,
apiKey: apiKey || undefined,
});
return meiliClient;
};
const search = async (
query: string,
params: SearchParams = {}
): Promise<SearchSection[]> => {
const trimmedQuery = query.trim();
if (!trimmedQuery) {
return [];
}
const client = ensureClient();
if (!client) {
return [];
}
const activeIndexes = indexes.value;
if (!activeIndexes.length) {
console.warn('No Meilisearch indexes configured.');
return [];
}
const requests = activeIndexes.map(async (indexUid) => {
const response = await client.index(indexUid).search(trimmedQuery, {
limit: params.limit ?? 10,
...params,
});
const safeResponse = JSON.parse(JSON.stringify(response));
return {
indexUid,
response: {
hits: safeResponse.hits,
estimatedTotalHits:
safeResponse.estimatedTotalHits ?? safeResponse.hits.length,
processingTimeMs: safeResponse.processingTimeMs ?? 0,
query: safeResponse.query,
},
} satisfies RawSearchSection;
});
console.log((await requests[0])?.response.hits[0]?.locale);
const settled = await Promise.allSettled(requests);
settled
.filter(
(result): result is PromiseRejectedResult =>
result.status === 'rejected'
)
.forEach((result) => {
console.error('Meilisearch query failed', result.reason);
});
return settled
.filter((result) => result.status === 'fulfilled')
.map((result) => {
const fulfilled = result as PromiseFulfilledResult<RawSearchSection>;
return {
indexUid: fulfilled.value.indexUid,
hits: fulfilled.value.response.hits.map((hit) => ({
...hit,
indexUid: fulfilled.value.indexUid,
})),
estimatedTotalHits:
fulfilled.value.response.estimatedTotalHits ??
fulfilled.value.response.hits.length,
processingTimeMs: fulfilled.value.response.processingTimeMs ?? 0,
};
});
};
return {
search,
indexes,
};
};