Logo David Burdelak
UX

Wyszukiwarka i filtry w sklepie internetowym – jak je zaprogramować i zaprojektować?

Projektowanie i programowanie zaawansowanej wyszukiwarki i filtrów w sklepie internetowym

W sklepach, które audytuję, wyszukiwarka i filtry są zwykle najsłabiej dopracowaną częścią serwisu, choć korzystają z nich klienci najbliżsi zakupu: wiedzą, czego szukają, i chcą to szybko znaleźć. Brak wyników dla zapytania z literówką, filtry przeładowujące całą stronę albo tysiące kombinacji filtrów w indeksie Google to problemy, które widzę najczęściej. W tym wpisie pokazuję, jak projektuję i programuję wyszukiwarkę oraz filtry, od wyboru silnika, przez obsługę w przeglądarce, po SEO i pomiar.

Wybór silnika wyszukiwania

Zapytanie LIKE '%fraza%' działa przy kilkuset produktach, ale nie radzi sobie z odmianą słów, literówkami ani oceną trafności, a przy dużych tabelach mocno obciąża bazę. Dobieram rozwiązanie do wielkości katalogu i budżetu:

RozwiązanieKiedy wybieramNa co uważać
Wyszukiwanie pełnotekstowe w MySQL lub PostgreSQLdo kilkudziesięciu tysięcy produktów, bez dodatkowej infrastrukturysłaba obsługa polskiej odmiany i literówek bez dodatkowej konfiguracji
Meilisearch lub Typesenseśrednie sklepy, szybkie wdrożenie z tolerancją literówekosobna usługa do utrzymania i synchronizacji danych
Elasticsearch lub OpenSearchduże katalogi, złożone fasety, własne reguły trafnościwymaga doświadczenia, do polskiego potrzebne dodatkowe analizatory
Algolia i usługi SaaSgdy liczy się czas wdrożenia, a nie kosztcena rośnie z liczbą zapytań i produktów

Niezależnie od silnika dodaję słownik synonimów, który budują sami klienci: frazy z raportu wyszukiwań bez wyników. Gdy ktoś szuka „adidasów”, a w sklepie są „buty sportowe”, synonim rozwiązuje problem bez zmian w opisach produktów.

Podpowiedzi podczas pisania

Podpowiedzi wyświetlam po wpisaniu dwóch znaków i dopiero wtedy, gdy użytkownik przerwie pisanie na ok. 250 ms. Bez tego każde naciśnięcie klawisza wysyła zapytanie, a odpowiedzi mogą wrócić w złej kolejności i pokazać wyniki dla wcześniejszej frazy. Poprzednie zapytanie anuluję przez AbortController:

let controller;
let timer;

input.addEventListener('input', (e) => {
  clearTimeout(timer);
  const q = e.target.value.trim();
  if (q.length < 2) return;

  timer = setTimeout(async () => {
    controller?.abort();
    controller = new AbortController();
    try {
      const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`, { signal: controller.signal });
      renderSuggestions(await res.json());
    } catch (err) {
      if (err.name !== 'AbortError') throw err;
    }
  }, 250);
});

W podpowiedziach pokazuję zdjęcie, cenę i kategorię, a nie tylko nazwę, oraz pozwalam przejść do wyników przyciskiem Enter. Całość musi działać z klawiatury, z poprawnymi rolami ARIA dla listy podpowiedzi.

Strona bez wyników

Pusta strona z komunikatem „Brak wyników” to najprostsza droga do utraty klienta. Na takiej stronie pokazuję poprawioną frazę, jeśli wyszukiwarka wykryła literówkę, popularne produkty z kategorii, do której pasuje zapytanie, oraz kontakt do obsługi. Każde wyszukiwanie bez wyników zapisuję, bo to najlepsza lista brakujących synonimów i produktów, których klienci szukają.

Filtry, adresy i przeładowanie strony

Wybrane filtry zapisuję w parametrach adresu, np. /buty-damskie?rozmiar=38&kolor=czarny. Dzięki temu link do przefiltrowanej listy można wysłać, przycisk Wstecz działa zgodnie z oczekiwaniami, a po odświeżeniu filtry zostają. Wyniki doładowuję bez przeładowania strony, a adres aktualizuję przez history.pushState.

Kilka zasad, których pilnuję w samych filtrach:

  • przy każdej wartości pokazuję liczbę produktów, a wartości dające zero wyników wyszarzam zamiast je ukrywać,
  • aktywne filtry wyświetlam nad listą jako etykiety z możliwością usunięcia pojedynczo lub wszystkich naraz,
  • na telefonie filtry otwierają się w osobnym panelu z przyciskiem „Pokaż produkty”, a nie po każdym zaznaczeniu,
  • podczas doładowywania wyników pokazuję skeleton screen zamiast spinnera, żeby lista nie przeskakiwała.

Przy długich listach decyduję też, czy lepsza będzie paginacja czy infinite scroll. W sklepach zwykle wybieram paginację z przyciskiem „Pokaż więcej”, bo pozwala wrócić do tego samego miejsca na liście.

Filtry a SEO

Każda kombinacja filtrów to nowy adres. Przy kilku filtrach z kilkunastoma wartościami powstają setki tysięcy adresów z prawie tą samą treścią, które pochłaniają crawl budget i rozmywają widoczność kategorii. Dlatego dzielę filtry na dwie grupy:

  • Filtry z popytem w wyszukiwarce, np. „buty do biegania damskie” albo „sofa narożna szara”. Takie kombinacje zamieniam w pełnoprawne podstrony z czytelnym adresem, własnym tytułem, opisem i linkami z kategorii.
  • Pozostałe filtry i sortowanie nie trafiają do indeksu. Adresy z wieloma parametrami mają adres kanoniczny wskazujący na kategorię albo noindex, a linki do nich nie są zwykłymi linkami w kodzie strony.

Strony wyników wyszukiwarki wewnętrznej zawsze wyłączam z indeksowania. Wpisanie dowolnej frazy tworzy nowy adres, a Google traktuje indeksowane wyniki wyszukiwania jako treść niskiej jakości.

Wydajność i pomiar

Najczęściej używane filtry i frazy warto buforować po stronie serwera, bo powtarzają się setki razy dziennie. Liczniki produktów przy filtrach liczę w silniku wyszukiwania albo z wcześniej przygotowanej tabeli, a nie osobnym zapytaniem do bazy dla każdej wartości. Gdy lista filtrów ładuje się wolno, przyczyną jest zwykle właśnie liczba zapytań, a ich ograniczenie to podstawa, na której opiera się wydajna komunikacja z bazą danych w CMS.

W Google Analytics 4 włączam śledzenie wyszukiwania w witrynie i porównuję konwersję osób korzystających z wyszukiwarki z resztą ruchu. Lista najczęstszych fraz i fraz bez wyników to dla mnie podstawowe źródło kolejnych poprawek, a większe zmiany w układzie filtrów sprawdzam, przeprowadzając testy A/B.