import { MeiliSearch } from 'meilisearch'; import type { SearchParams, SearchResponse } from 'meilisearch'; interface RawSearchSection { indexUid: string; response: SearchResponse>; } export interface SearchHit extends Record { 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 => { 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; 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, }; };