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:
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ę.