Wyszukiwanie pełnotekstowe (ang. full-text search) było czymś, co staraliśmy się osiągnąć w pierwszym projekcie, w którym zdecydowaliśmy się na użycie Elasticsearcha. Szybko okazało się, że pełnotekstowo można w nim wyszukiwać na 7 różnych sposobów. Dziś chciałbym przedstawić pokrótce każdy z nich.
Pamiętaj proszę, że tworząc dzisiejszy wpis testowałem wszystko na Elasticsearchu w wersji 6.3 (najnowsza jaka w tamtym momencie była dostępna). Możliwe jest, że w starszych lub nowszych wydaniach wygląda to nieco inaczej.
Rodzaje pełnotekstowych zapytań
match
Podstawowy typ zapytania pełnotekstowego. Najprostsze wywołanie wygląda tak:
|
GET /_search { "query": { "match" : { "field_name_here" : "this is a test" } } } |
Gdzie field_name_here
jest nazwą pola, w którym będziemy wyszukiwali. Oznacza to ni mniej, ni więcej, że tego typu zapytanie możemy wykonać tylko na jednym, wybranym przez nas polu.
W najprostszej konfiguracji Elasticsearch wykonując powyższe zapytanie wyszuka nam wszystkie dokument, które w polu field_name_here
zawierają co najmniej jedno słowo z wyrażenia: this is a test
. Mówiąc pseudokodem zwrócone zostaną dokumenty spełniające warunek: (this OR is OR a OR test) IN field_name_here
.
Przy pomocy jednego parametru możemy określić minimalną liczbę słów z naszego wyrażenia, które muszą zostać znalezione, żeby Elasticsearch zwrócił nam dany dokument w wynikach wyszukiwania (domyślna wartość to 1):
|
GET /_search { "query": { "match": { "field_name_here": { "query": "this is a test", "minimum_should_match": 3 } } } } |
Łatwo konfigurowalne jest także przestawienie domyślnego operatora z OR
na AND
dzięki czemu otrzymamy wszystkie dokumenty, które zawierają wszystkie słowa z wyrażenia this is a test
(pseudokod: (this AND is AND a AND test) IN field_name_here
):
|
GET /_search { "query": { "match" : { "field_name_here" : { "query" : "this is a test", "operator" : "and" } } } } |
Konfigurowalne jest również wykorzystanie fuzzy matching (na pewno pojawi się odrębny wpis na ten temat), czy wyszukanie synonimów do wprowadzonych wyrażeń oraz kilka innych funkcjonalności.
Więcej: Match Query
match_phrase
Jak łatwo zauważyć, zapytanie typu match
skupia się na wyszukiwaniu poszczególnych słów. Jeżeli chcielibyśmy skupić się na wyszukiwaniu całych fraz, powinniśmy użyć zapytania typu match_phrase
. W najprostszej wersji mamy:
|
GET /_search { "query": { "match_phrase": { "field_name_here": "this is a test" } } } |
Powyższe zapytanie zwróci nam wszystkie dokumenty, które w polu field_name_here
zawierają wyrażenie this is a test
.
Jednym z bardziej przydatnych parametrów do konfiguracji przy tym zapytaniu jest slop
(domyślna wartość to 0), który umożliwia akceptowanie pominięcia słowa lub przestawienia słów w wyrażeniu. Na przykład zapytanie:
|
GET /_search { "query": { "match_phrase": { "field_name_here": { "query": "this is test", "slop" : 1 } } } } |
pozwoli wyszukać zarówno this is a test
, jak i this is the test
oraz kilka innych zbliżonych wyrażeń. Z kolei zapytanie:
|
GET /_search { "query": { "match_phrase": { "field_name_here": { "query": "this is a test", "slop" : 2 } } } } |
akceptuje przestawienie kolejności dwóch słów w wyrażeniu i zwróci choćby dokumenty zawierające wyrażenie is this a test
.
Więcej: Match Phrase Query
match_phrase_prefix
Wariacją zapytania typu match_phrase
jest zapytanie typu match_phrase_prefix
. Jedyna różnica w działaniu polega na tym, że ostatnie słowo z wyszukiwanego wyrażenia jest traktowane jako prefiks. A więc zapytanie:
|
GET /_search { "query": { "match_phrase_prefix": { "field_name_here": "this is a t" } } } |
może wyszukać zarówno this is a test
, jak i this is a team
, czy this is a text
.
Ważną opcją konfiguracji jest parametr max_expansions
, który określa do ilu wyrażeń rozwijany jest nasz prefiks w trakcie wyszukiwania. Domyślnie jest ustawione 50.
Do czego może przydać się zapytanie typu match_phrase_prefix
? Do czegoś, co twórcy Elasticsearcha określają jako search-as-you-type. Kojarzysz wyszukiwarki, które asynchronicznie zwracają rezultaty zaraz po wpisaniu kolejnych liter tekstu? No właśnie. To jest to. A dokładniej, jest to najprostsza opcja implementacji takiego zachowania w Elasticsearchu (o innych być może kiedyś również napiszę).
Więcej: Match Phrase Prefix Query
multi_match
Wspomniałem powyżej, że zapytanie typu match
pozwala wyszukiwać tylko i wyłącznie w jednym polu. Gdy chcemy przeszukać kilka pól na raz możemy użyć zapytania typu multi_match
:
|
GET /_search { "query": { "multi_match": { "query": "this is a test", "fields": [ "field_name_here", "other_field_name_here" ] } } } |
Dopuszczalne są również wildcard-y (nie mam pojęcia jak to przetłumaczyć na język polski):
|
GET /_search { "query": { "multi_match": { "query": "this is a test", "fields": [ "field_name_here", "*_suffix_here" ] } } } |
oraz boosting (podbicie ważności) poszczególnych pól:
|
GET /_search { "query": { "multi_match": { "query": "this is a test", "fields": [ "field_name_here", "other_field_name_here^3" ] } } } |
^3
przy polu other_field_name_here
oznacza, że to pole jest trzy razy ważniejsze od pola field_name_here
podczas ustalania trafności otrzymanych rezultatów.
Z trafnością rezultatów związana jest tutaj jeszcze jedna rzeczy. Ze względu na to, że zapytanie multi_match
pozwala na wyszukiwanie w różnych polach, pozwala również określić w jaki sposób owo wyszukiwanie koreluje ze sobą w poszczególnych polach i jak zliczane są wyniki do określenia trafności. Nie będę tu ich opisywał, ale warto wiedzieć, że mamy pięć podejść: best_fields
(domyślny), most_fields
, cross_fields
, phrase
oraz phrase_prefix
.
Więcej: Multi Match Query
common
Zapytanie typu common
jest, jak dotąd, najmniej przetestowanym przeze mnie typem zapytania pełnotekstowego. Podejmuje ono temat w jaki sposób traktowane są często występujące w tekście słowa. Znów wszystko rozbija się o wspomnianą wcześniej trafność. Nie wdając się zbytnio w szczegóły chodzi o to, że w językach takich jak polski lub angielski pojawiają się słowa, które występują znacznie częściej od innych. W polskim są to na przykład spójniki (np. i, oraz, lub). Częstą praktyką wykorzystywaną przez wyszukiwarki jest po prostu pomijanie tego typu słów (które z angielskiego możemy nazywać stopwords). W Elasticsearch również możliwe jest ich pomijanie. Niestety nie jest ono najefektywniejsze. Może się bowiem zdarzyć, że wyszukiwana fraza w całości składa się ze stopwordów. Przykładem jest choćby dobrze znane: to be, or not to be. Zapytanie typu common
podejmuje ten temat nieco inaczej.
Wykorzystując je określamy poziom odcięcia słów ważniejszych, od tych mniej ważnych. W efekcie ważniejsze słowa wpływają w większym stopniu na określenie trafności wyników wyszukiwania, ale te mniej ważne też się liczą i nie są pomijane.
Więcej: Common Terms Query
query_string
Zapytanie query_string
jest pierwszym z dwóch typów zapytań, które przed wykonaniem parsują składnie wprowadzanego wyrażenia. Przykładowo zapytanie:
|
GET /_search { "query": { "query_string": { "fields": ["title", "description"], "query": "\"star wars\" OR \"marvel movies\"" } } } |
wyszuka nam dokumenty, które w polach title
lub description
zawierają frazę star wars
lub marvel movies
.
Oprócz operatorów logicznych i wyszukiwania fraz mamy również grupowanie wyrażeń ((Ala ma kota) OR (Ola ma psa)
), określanie pola w jakim ma zostać wyszukane dane słowo (author:"Piotr Prądzyński" OR title:"Uczymy się Elasticsearch"
), wildcardy (Manch?ster Uni*
), wyrażenia regularne (nie będę dawał przykładu, bo nie chcę się pomylić 😉 ), szukanie w zakresach (date:[1986-08-05 TO 2015-04-02]
i wiele, wiele innych.
Mnogość konfiguracji oraz fakt, że źle zbudowane zapytanie rzuca wyjątkiem sprawiają, że query_string
nie służy do tego, żeby wystawiać je bezpośrednio zwykłym użytkownikom aplikacji. Przeznaczone jest raczej do użytku „eksperckiego”.
Więcej: Query String Query
simple_query_string
Jeżeli chcemy osiągnąć zbliżony efekt do query_string
i móc spokojnie wystawić takie zapytanie do użytkownika końcowego, to możemy użyć alternatywnego simple_query_string
. Powyższe zapytanie będzie wyglądało teraz tak:
|
GET /_search { "query": { "simple_query_string": { "fields": ["title", "description"] "query": "\"star wars\" | \"marvel movies\"" } } } |
W odróżnieniu od query_string
, źle przygotowane zapytanie typu simple_query_string
nie rzuca wyjątkiem, a więc nie ma obawy, że użytkownik coś nabroi. Dodatkowo ilość udostępnianych opcji (która jest nieco skromniejsza od tej z query_string
) można łatwo zawęzić.
Więcej: Simple Query String Query
The End
Jak widzisz opcji rozwiązania problemu wygodnego wyszukiwania pełnotekstowego jest całkiem sporo. Co ciekawsze wcale nie jest tak, że musisz decydować się wyłącznie na jeden sposób. Powyższe typy można w bardzo elastyczny sposób łączyć ze sobą lub z innymi nie-pełnotekstowymi wyszukiwaniami. Dzięki temu możemy osiągnąć jeszcze bardziej efetk[o|y]wne rezultaty. Wszystko zależy wyłącznie od naszych potrzeb.
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