W jaki sposób Elasticsearch potraktuje słowa zawierające litery ze znakami diakrytycznymi? Czy je wyszuka? Jak się zachowa? Takie pytania zada sobie prędzej czy później każdy kto zamierza zbudować wyszukiwarkę opartą o Elasticsearch indeksującą polskojęzyczne dokumenty (albo dokumenty z dowolnego innego języka, który wykorzystuje „ogonki”). Dziś postaram się na nie odpowiedzieć, pokazując jednocześnie jak przebiega cały proces przygotowania tekstu do wyszukiwania i jak możemy na niego wpływać.
Analiza tekstu
W jednym z wcześniejszych wpisów wspomniałem, że indeks odwrócony zbudowany jest z tokenów i związanych z nimi metadanych. Nie jest jednak tak, a przynajmniej nie w większości przypadków, że w indeksie ląduje cały tekst słowo po słowie. Inaczej mówiąc, słowo występujące w tekście nie jest tożsame z tokenem w indeksie. Dzieje się tak, ponieważ indeksowany tekst podlega analizie i to od jej wyniku zależy jak wyglądał będzie wynikowy zbiór tokenów.
Dla przykładu prześledźmy analizę najczęściej słyszanego przez programistów zdania. Na początek potraktujmy je domyślnym analizatorem Elasticsearcha (standard
):
|
POST _analyze { "analyzer": "standard", "text": "As soon as possible!" } |
W efekcie otrzymamy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
{ "tokens": [ { "token": "as", "start_offset": 0, "end_offset": 2, "type": "<ALPHANUM>", "position": 0 }, { "token": "soon", "start_offset": 3, "end_offset": 7, "type": "<ALPHANUM>", "position": 1 }, { "token": "as", "start_offset": 8, "end_offset": 10, "type": "<ALPHANUM>", "position": 2 }, { "token": "possible", "start_offset": 11, "end_offset": 19, "type": "<ALPHANUM>", "position": 3 } ] } |
Co jeśli użyjemy analizatora dedykowanego językowi angielskiemu? (english
):
|
POST _analyze { "analyzer": "english", "text": "As soon as possible!" } |
Dostaniemy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
{ "tokens": [ { "token": "soon", "start_offset": 3, "end_offset": 7, "type": "<ALPHANUM>", "position": 1 }, { "token": "possibl", "start_offset": 11, "end_offset": 19, "type": "<ALPHANUM>", "position": 3 } ] } |
Co tu się stanęło? Zaraz się dowiemy.
W tym miejscu trzeba jeszcze pamiętać, że analizie poddawany jest nie tylko indeksowany dokument, ale także wyszukiwana fraza. Zazwyczaj, ale nie zawsze, żeby otrzymać satysfakcjonujące rezultaty wystarczy zastosować ten sam sposób analizy zarówno podczas indeksowania, jak i wyszukiwania. Nie ma jednak problemu żeby zastosować odrębne analizatory kiedy tylko zajdzie taka potrzeba.
Analizatory
Czym jest wspomniany już analizator (ang. analyzer)? Jest to komponent, którego zadaniem jest przyjęcie surowego tekstu i zwrócenie zbioru tokenów na podstawie zdefiniowanego zestawu reguł.
Każdy analizator składa się z trzech bloków: filtrów znaków (ang. character filters), tokenizatora (ang. tokenizer) oraz filtrów tokenów (ang. token filters). Tekst wejściowy przechodzi po kolei przez każdy z wymienionych bloków, przy czym tylko tokenizator jest wymaganym elementem. O ile tokenizator musi być dokładnie jedne, to obie sekcje filtrów mogą zawierać ich zero lub więcej.
Elasticsearch dostarcza szeroki wachlarz wbudowanych analizatorów, które najczęściej zaspokajają potrzeby użytkowników. Kiedy jednak czegoś brakuje, to łatwo i szybko możemy zbudować własny konstruując samodzielnie trzy wyżej wymienione bloki.
Filtry znaków
Filtry znaków służą do wstępnej obróbki tekstu (strumienia znaków) zanim zostanie on przekazany do tokenizatora.
Najnowsza dokumentacja wymienia trzy wbudowane, dostępne dla nas, filtry. Najmniej generyczny służy do pozbywania się znaczników HTML z tekstu. Przydatny szczególnie, kiedy przychodzi nam działać z zawartością stron internetowych. Pozostałe dwa umożliwiają podmienianie wybranych znaków lub wzorców tekstu.
Tokenizatory
Kolejnym krokiem analizy tekstu jest przerobienie strumienia znaków na tokeny, a za zadanie to odpowiedzialny jest tokenizator. Dodatkowo odpowiada on również za określenie i zapamiętanie metadanych (kolejność, pozycja itp.) poszczególnych termów, wykorzystywanych później przez niektóre rodzaje zapytań, czy narzędzie służące do zakreślania wyszukanego tekstu.
Elasticsearch dostarcza zbiór gotowych do użycia tokenizatorów i dzieli je na trzy kategorie:
- zorientowane na wydobycie całych słów z tekstu,
- zorientowane na wydobycie pofragmentowanych słów z tekstu oraz
- zorientowane na obsługę ustrukturyzowanego tekstu (np. identyfikatory, adresy email, kody pocztowe).
Zobaczmy na przykładzie jak działa kilka wybranych tokenizatorów. Sposób sprawdzania jest analogiczny do przytoczonego powyżej. Do testów użyjemy jednak trochę bardziej złożonego zdania.
|
POST _analyze { "tokenizer": "standard", "text": "\"Use the Force, Luke.\" is the most famous Obi-1 Kenobi's sentence." } |
I tak tokenizator:
standard
podzieli nasze zdanie na takie tokeny: Use
, the
, Force
, Luke
, is
, the
, most
, famous
, Obi
, 1
, Kenobi's
, sentence
letter
da nam: Use
, the
, Force
, Luke
, is
, the
, most
, famous
, Obi
, Kenobi
, s
, sentence
lowercase
=> use
, the
, force
, luke
, is
, the
, most
, famous
, obi
, kenobi
, s
, sentence
whitespace
=> "Use
, the
, Force,
, Luke."
, is
, the
, most
, famous
, Obi-1
, Kenobi's
, sentence.
keyword
=> "Use the Force, Luke." is the most famous Obi-1 Kenobi's sentence.
Filtry tokenów
Ostatnią składową analizatora są filtry tokenów. W odróżnieniu od filtrów znaków nie działają one na strumieniu znaków, ale na strumieniu… tokenów. Przypadek? Nie sądzę 😉 Jak się zapewne domyślasz strumień tokenów jest efektem działania wybranego przez nas tokenizatora.
O ile filtrów znaków Elasticsearch dostarcza zaledwie trzy, to na liście filtrów tokenów widnieje ich około pięćdziesięciu. Nie będę ich tu wszystkich teraz wymieniał, bo łatwo je znaleźć w dokumentacji, ale wspomnę o kilku najczęściej używanych lub po prostu intrygujących 😉
Pracując z językiem polskim (lub dowolnym innym z „ogonkami”) definitywnie przyda nam się filtr asciifolding
konwertujące znaki spoza bloku „Basic Latin” w Unicodzie do odpowiedników w ASCII. Dzięki temu będę znaleziony zarówno kiedy wyszukacie „Prądzyński”, jak i „Pradzynski” 🙂
Filtry lowercase
lub uppercase
mogą się przydać kiedy chcemy, aby nasze wyszukiwanie działało niezależnie od wielkości wprowadzonych liter. Dzięki temu znajdzie się również „prądzyński”, a łącząc lowercase
z asciifolding
pojawi się też „pradzynski” 😉
Filtr length
w zależności od ustawień usunie zbyt krótkie lub długie tokeny. stop
usunie popularne w danym języku słowa (tzw. stopwords, o których wspominałem już we wcześniejszych artykułach). Stąd też „tajemnicze” zniknięcie słówek „as” w powyższym przykładzie. trim
z kolei, usunie białe znaki wokół tokenów, a keep
pozostawi tylko słowa znajdujące się na zdefiniowanej liście.
Elasticsearch dostarcza również bukiet filtrów wykonujących stemming, czyli usunięcie końcówki fleksyjnej i pozostawienie tylko tematu wyrazu. Przykład: „plażing” zostanie sprowadzony do słowa „plaża” 😉 Dostępnych jest kilka algorytmów wykonujących stemming, które obsługują kilkadziesiąt najpopularniejszych języków. Język polski dostępny jest po dograniu odpowiedniej wtyczki. To właśnie stemming spowodował pojawienie się „possibl” w teście analizatora english
.
Normalizatory
Relatywnie nowym tworem w Elasticsearchu są normalizatory (w momencie pisania tego tekstu cały czas są w fazie beta). Idea ich działania jest zbliżona do analizatorów, z tą różnicą, że na wyjściu produkują pojedynczy token. Co za tym idzie, nie używają one tokenizatorów, a zbiór filtrów, z których mogą korzystać jest nieco okrojony. Elasticsearch na razie nie dostarcza wbudowanych normalizatorów. Użyć możemy jedynie tych samodzielnie przygotowanych (co jest tak samo proste, jak przygotowanie własnego analizatora).
The End
Odpowiednie przygotowanie tekstu ma, w przypadku Elasticsearcha, ogromne znaczenie. To dzięki niemu możemy otrzymać więcej niż satysfakcjonujące efekty działania budowanych przez nas wyszukiwarek. Elasticsearch jest już na tyle dojrzałym narzędziem, że korzystanie z dostarczanych przez niego gotowców jest w dużej części przypadków wystarczające. Niemniej jednak zdarzają się sytuacje brzegowe, w których przydaje się wiedzieć jak to wszystko działa pod spodem. Mam nadzieję, że dzisiejszy wpis Cię do tego przybliżył.
Bądź na bieżąco!
Podobają Ci się treści publikowane na moim blogu? Nie chcesz niczego pominąć? Zachęcam Cię do subskrybowania kanału RSS, polubienia fanpage na Facebooku, zapisania się na listę mailingową:
lub śledzenia mnie na Twitterze. Generalnie polecam wykonanie wszystkich tych czynności, bo często zdarza się tak, że daną treść wrzucam tylko w jedno miejsce. Zawsze możesz zrobić to na próbę, a jeśli Ci się nie spodoba – zrezygnować
Dołącz do grup na Facebooku
Chcesz więcej? W takim razie zapraszam Cię do dołączenia do powiązanych grup na Facebooku, gdzie znajdziesz dodatkowe informacje na poruszane tutaj tematy, możesz podzielić się własnymi doświadczeniami i przemyśleniami, a przede wszystkim poznasz ludzi interesujących się tą samą tematyką co Ty.
W grupie Programista Na Swoim znajdziesz wiele doświadczonych osób chętnych do porozmawiania na tematy krążące wokół samozatrudnienia i prowadzenia programistycznej działalności gospodarczej. Vademecum Juniora przeznaczone jest zaś do wymiany wiedzy i doświadczeń na temat życia, kariery i problemów (niekoniecznie młodego) programisty.
Wesprzyj mnie
Jeżeli znalezione tutaj treści sprawiły, że masz ochotę wesprzeć moją działalność online, to zobacz na ile różnych sposobów możesz to zrobić. Niezależnie od tego co wybierzesz, będę Ci za to ogromnie wdzięczny.
Na wsparciu możesz także samemu zyskać. Wystarczy, że rzucisz okiem na listę różnych narzędzi, które używam i polecam. Decydując się na skorzystanie z któregokolwiek linku referencyjnego otrzymasz bonus również dla siebie.
Picture Credits
Dodaj komentarz