import { MeiliSearch } from "meilisearch"; import type { SearchParams, MatchesPosition, SearchResponse, MultiSearchQuery, MultiSearchResponse, MultiSearchResult, } from "meilisearch"; import { filterDefined, isDefined } from "./asserts"; import { MeiliDocumentsType } from "shared/meilisearch-graphql-typings/meiliTypes"; const meili = new MeiliSearch({ host: process.env.NEXT_PUBLIC_URL_MEILISEARCH ?? "", apiKey: process.env.NEXT_PUBLIC_MEILISEARCH_KEY, }); interface CustomSearchParams extends Omit< SearchParams, | "cropLength" | "cropMarker" | "cropMarker" | "highlightPostTag" | "highlightPreTag" | "q" | "showMatchesPosition" > {} type CustomHit> = T & { _formatted: Partial; _matchesPosition: MatchesPosition; }; type CustomHits> = CustomHit[]; export interface CustomSearchResponse extends Omit, "hits"> { hits: CustomHits; } export const meiliMultiSearch = async (queries: MultiSearchQuery[]): Promise => await meili.multiSearch({ queries: queries.map((query) => ({ attributesToHighlight: ["*"], ...query, highlightPreTag: "", highlightPostTag: "", showMatchesPosition: true, cropLength: 20, cropMarker: "...", })), }); export const filterHitsWithHighlight = ( searchResult: CustomSearchResponse | MultiSearchResult>, keyToFilter?: keyof T ): CustomSearchResponse => { const result = searchResult as unknown as CustomSearchResponse; if (isDefined(keyToFilter)) { result.hits = result.hits.map((item) => { if ( Object.keys(item._matchesPosition).some((match) => match.startsWith(keyToFilter as string)) ) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore item._formatted[keyToFilter] = filterDefined(item._formatted[keyToFilter]).filter( (translation) => JSON.stringify(translation).includes("") ); } return item; }); } return result; }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export const meiliSearch = async ( indexName: I, query: string, options: CustomSearchParams ) => { const index = meili.index(indexName); return (await index.search["documents"]>(query, { ...options, attributesToHighlight: options.attributesToHighlight ?? ["*"], highlightPreTag: "", highlightPostTag: "", showMatchesPosition: true, cropLength: 20, cropMarker: "...", })) as unknown as CustomSearchResponse["documents"]>; }; export const containsHighlight = (text: string | null | undefined): boolean => isDefined(text) && text.includes("");