Wyszukiwarka z podpowiedziami na Elasticsearch

Utworzono: kwiecień 2015 Zaktualizowano: 1 października 2026

z pracy

Python, Elasticsearch

Podpowiedzi już po dwóch literach, odporne na literówki i brak polskich znaków.

Ludzie szukają w dużym katalogu po nazwach, które pamiętają tylko trochę, z literówkami i bez polskich znaków, a podpowiedzi mają wyskakiwać już w trakcie pisania. Zapytanie leci przy pisaniu, więc odpowiedź musi przyjść w ułamku sekundy, a do tego liczy się kolejność, bo z dziesiątek pasujących pozycji widać tylko kilka pierwszych.

Czemu nie LIKE

Pierwsze, co przychodzi do głowy, to LIKE w bazie, i przy małej tabeli z dokładnymi nazwami to wystarcza. Tu nie: z procentem na początku nie korzysta z indeksu, a „zol” nigdy nie znajdzie „Żółtego”. Trigramy w bazie, np. pg_trgm w PostgreSQL, radzą sobie z literówkami, ale podpowiedzi po początkach słów i ranking trzeba by składać samemu. Dlatego osobny silnik wyszukiwania, choć to drugi system, który trzeba trzymać w zgodzie z bazą.

Jak to działa

Osobny indeks w Elasticsearch. Przy indeksowaniu nazwy są cięte na początki słów (edge n-gramy), zamieniane na małe litery i pozbawiane polskich znaków, a pytanie przechodzi tę samą obróbkę, tylko bez cięcia. Do trafności dochodzi popularność, czyli to, jak często daną pozycję wybierano, a literówki łapie zapytanie z fuzziness.

Najwięcej robią ustawienia indeksu, osobny analizator do indeksowania i osobny do szukania:

json
PUT /products
{
  "settings": {
    "analysis": {
      "filter": { "prefix": { "type": "edge_ngram", "min_gram": 2, "max_gram": 15 } },
      "analyzer": {
        "autocomplete": { "tokenizer": "standard", "filter": ["lowercase", "asciifolding", "prefix"] },
        "autocomplete_search": { "tokenizer": "standard", "filter": ["lowercase", "asciifolding"] }
      }
    }
  },
  "mappings": {
    "product": {
      "properties": {
        "name": { "type": "string", "analyzer": "autocomplete", "search_analyzer": "autocomplete_search" },
        "popularity": { "type": "integer" }
      }
    }
  }
}

Baza zostaje źródłem prawdy, indeks aktualizują zadania w tle, a raz na jakiś czas przebudowujemy go od zera. W przeglądarce zapytanie idzie dopiero po krótkiej przerwie w pisaniu, a nie po każdym klawiszu, więc Elasticsearch nie dostaje pytania o każdą literę.