Poradnik dotyczący integracji Meilisearch z Medusa.js

Autor Viktor Holik

Featured image

Około rok temu ja i mój zespół rozpoczęliśmy projekt Medusa eCommerce, który wymagał zaawansowanych możliwości filtrowania produktów. Potrzebowaliśmy funkcjonalności takich jak filtrowanie predykcyjne na podstawie kategorii lub atrybutów, wyszukiwanie pełnotekstowe, tolerancję błędów oraz trafne wyniki wyszukiwania. Tworzenie tych funkcji od podstaw w PostgreSQL lub podobnych bazach danych jest czasochłonne i wymaga znacznej wiedzy specjalistycznej.

Na szczęście istnieje rozwiązanie open source, które doskonale spełnia te wymagania — Meilisearch.

W tym poradniku opiszę, jak działa Meilisearch i przedstawię praktyczne przykłady jego integracji z Medusa.js. Zaczynajmy!

Integracja z Medusa.js

Na początek zobaczmy, jak Medusa integruje się z API Meilisearch.

Gdy backend się uruchamia, Medusa używa loadera, który masowo dodaje produkty za pomocą API Meilisearch. Następnie wszelkie zmiany, usunięcia czy tworzenie produktów są zarządzane przez subskrybenta Medusa, który odpowiednio obsługuje te aktualizacje.

meiliserach-medusa-1

Aby zacząć używać Meilisearch w swoim projekcie Medusa, dołącz medusa-plugin-meilisearch do swojego medusa-config.js:

const plugins = [
  // ...
  {
    resolve: `medusa-plugin-meilisearch`,
    options: {
      config: {
        host: process.env.MEILISEARCH_HOST,
        apiKey: process.env.MEILISEARCH_API_KEY,
      },
      settings: {
        // ustawienia indeksu...
      },
    },
  },
]

Konfiguracja indeksu

W tym poradniku używam domyślnej konfiguracji wtyczki, z wyjątkiem filterableAttributes i sortableAttributes.

Indeks w Meilisearch to grupa dokumentów z powiązanymi ustawieniami. Określając klucz indeksu, można skonfigurować specyficzne ustawienia dla tego indeksu:

{
  resolve: `medusa-plugin-meilisearch`,
  options: {
   config: {
    host: process.env.MEILISEARCH_HOST,
    apiKey: process.env.MEILISEARCH_API_KEY,
   },
   settings: {
    products: {
     indexSettings: {
      filterableAttributes: ["categories.handle", "variants.prices.amount", "variants.prices.currency_code"],
      sortableAttributes: ["title", "variants.prices.amount"],
      searchableAttributes: ["title", "description", "variant_sku"],
      displayedAttributes: ["*"],
      primaryKey: "id",
      transformer: (product) => ({
        // Niestandardowa logika transformacji
      })
     },
    },
   },
  },
},

Ustawiając filterableAttributes, umożliwiasz filtrowanie dokumentów za pomocą tych kluczy. Pamiętaj, że dodaje to również wyszukiwanie fasetowe (przewidywanie filtrów). Funkcja transformująca pozwala modyfikować dane w preferowanym formacie przed ich aktualizacją w Meilisearch. Każdy klucz w indexSettings jest opcjonalny.

Użycie na witrynie sklepowej

Zainicjujmy naszego klienta Meilisearch. Zainstaluj pakiet Meilisearch i dodaj następujący kod w folderze z narzędziami:

Pamiętaj, aby nie ujawniać publicznie klucza API.

// lib/meilisearch
import { MeiliSearch } from 'meilisearch'

export const meilisearchClient = new MeiliSearch({
  host: process.env.MEILISEARCH_HOST!,
  apiKey: process.env.MEILISEARCH_API_KEY!,
})

Stwórzmy teraz funkcję narzędziową obsługującą filtrowanie produktów:

// lib/meilisearch
type FiltersQuery = {
  categories?: string[]
  orderBy?: string
  order?: 'asc' | 'desc'
  page?: number
  minPrice?: number
  maxPrice?: number
  query?: string
  currencyCode?: string
}

const PAGE_SIZE = 15

export async function getProductsFromMeilisearch({
  categories,
  maxPrice,
  minPrice,
  orderBy,
  order = 'asc',
  page = 1,
  query,
  currencyCode = 'usd',
}: FiltersQuery) {
  // Aby wdrożyć...
}

Meilisearch wykorzystuje składnię podobną do SQL do filtrowania produktów, dzięki czemu jest prosty w użyciu. Opracujmy funkcję filtrującą produkty na podstawie określonych parametrów.

export async function getProductsFromMeilisearch({
  categories,
  maxPrice,
  minPrice,
  orderBy,
  order = 'asc',
  page = 1,
  query,
  currencyCode = 'usd',
}: FiltersQuery) {
  const offset = (page - 1) * PAGE_SIZE

  const queries: string[] = []

  if (categories) {
    queries.push(`categories.handle IN [${categories.join(', ')}]`)
  }

  if (minPrice) {
    queries.push(
      `(variants.prices.amount >= ${minPrice} AND variants.prices.currency_code = "${currencyCode}")`
    )
  }

  if (maxPrice) {
    queries.push(
      `(variants.prices.amount <= ${maxPrice} AND variants.prices.currency_code = "${currencyCode}")`
    )
  }

  const result = await meilisearchClient.index('products').search(query, {
    limit: PAGE_SIZE,
    offset: offset,
    sort: orderBy ? [`${orderBy}:${order}`] : undefined,
    filter: queries.join(' AND '),
    facets: ['categories.handle'],
  })

  return result
}

Teraz powinieneś być w stanie zobaczyć swoje przefiltrowane produkty oraz aspekty w odpowiedziach. Dzięki tej funkcji możesz bezproblemowo zaimplementować paginację, filtrowanie, predykcyjne filtrowanie i wiele więcej.

{
  hits: [
    {
      id: 'cool-t-shirt',
      title: 'Medusa T-Shirt',
      description: 'Reimagine the feeling of a classic T-shirt. With our cotton T-shirts, everyday essentials no longer have to be ordinary.',
      categories: [Array],
      handle: 't-shirt',
      subtitle: null,
      is_giftcard: false,
      weight: 400,
      images: [Array],
      options: [Array],
      variants: [Array],
      tags_value: [],
      variant_sku: [],
      variant_title: [Array],
      variant_upc: [],
      variant_ean: [],
      variant_mid_code: [],
      variant_hs_code: [],
      variant_options: [],
      variant_options_value: [Array]
    }
  ],
  query: 'T shirt',
  processingTimeMs: 0,
  limit: 15,
  offset: 0,
  estimatedTotalHits: 1,
  facetDistribution: { 'categories.handle': { 't-shirt': 1 } },
  facetStats: {}
}

Podsumowanie

Celem tego artykułu jest dostarczenie podstawowej wiedzy na temat używania Meilisearch z Medusa.js. Zalecam rozpoczęcie od potrzeb twojego projektu i eksperymentowanie z tymi integracjami. Mam nadzieję, że ten przewodnik okaże się pomocny.

Przekształć z nami swój eCommerce, dzięki Medusa.js

Porozmawiajmy o Twoim projekcie

Inne posty na blogu

Frontend Developer: Mistrz efektywności i UX — Kluczowe strategie dla optymalnego performance aplikacji

W dzisiejszym nieustannie rozwijającym się świecie cyfrowym, gdzie użytkownicy oczekują szybkości, wydajności i bezproblemowego korzystania z aplikacji internetowych, rola Frontend Developer’a staje się niezmiernie istotna...

Wspierają nas inwestorzy Medusa - Catch The Tornado

Do naszej firmy dołączyli Piotr i Tomasz Karwatkowie.

Opowiedz nam o swoim projekcie

Myślisz o nowym projekcie? Zrealizujmy go!

Naciskając „Wyślij wiadomość” udzielasz nam, tj. Rigby, zgody na email marketing naszych usług w ramach komunikacji dotyczącej Twojego projektu. Zgodę możesz wycofać, np. pisząc na adres hello@rigbyjs.com.
Więcej
placeholder

Grzegorz Tomaka

Co-CEO & Co-founder

LinkedIn icon
placeholder

Jakub Zbaski

Co-CEO & Co-founder

LinkedIn icon