W poprzednim wpisie zaprezentowałem jak wykorzystać Synonym Token Filter. W przykładach, które przytoczyłem wszystko działało perfekcyjnie. Okazało się jednak, że są sytuacje, w których Synonym Token Filter nie zadziała tak jak byśmy się tego spodziewali. Uświadomił mnie o tym Zbyszko (dzięki! 🙌), w jednym z komentarzy na Facebooku. Przestudiowałem załączone materiały i stwierdziłem, że całość zasługuje na odrębny wpis. I oto jest 🙂
Kiedy Synonym Token Filter zadziała?
Na początek potwierdźmy, że poprzedni artykuł nie kłamał i Synonym Token Filter w ogóle działa.
Dodajmy przykładowy dokument do naszego indeksu:
|
PUT /bloggers/_doc/1 { "content" : "Piotr Prądzyński jest autorem bloga Programista Na Swoim." } |
Następnie wyszukajmy go najprostszym możliwym zapytaniem, z tym, że zamiast imienia i nazwiska użyjemy pseudonimu:
|
GET /bloggers/_search { "query": { "match": { "content": "prondzyn" } } } |
Jak można się było spodziewać, prondzyn
nie został znaleziony:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 0, "relation" : "eq" }, "max_score" : null, "hits" : [ ] } } |
Powiedzmy więc Elasticsearchowi, żeby wyszukiwanie pseudonimu prondzyn
traktował tak samo jak wyszukiwanie imienia i nazwiska Piotr Prądzyński
:
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 33
|
PUT /bloggers_with_synonyms { "settings": { "index": { "analysis": { "filter": { "synonym_filter": { "type": "synonym", "synonyms": [ "prondzyn, Piotr Prądzyński" ] } }, "analyzer": { "synonym_analyzer": { "tokenizer": "standard", "filter": [ "synonym_filter" ] } } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "synonym_analyzer" } } } } |
Dodajmy ten sam dokument do ulepszonego indeksu:
|
PUT /bloggers_with_synonyms/_doc/1 { "content" : "Piotr Prądzyński jest autorem bloga Programista Na Swoim." } |
I wyszukajmy ponownie:
|
GET /bloggers_with_synonyms/_search { "query": { "match": { "content": "prondzyn" } } } |
Mamy to:
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
|
{ "took" : 13, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 0.70970416, "hits" : [ { "_index" : "bloggers_with_synonyms", "_type" : "_doc", "_id" : "1", "_score" : 0.70970416, "_source" : { "content" : "Piotr Prądzyński jest autorem bloga Programista Na Swoim." } } ] } } |
Czyli faktycznie, w tej sytuacji, przy prostym zapytaniu typu match
, Synonym Token Filter działa prawidłowo.
Kiedy Synonym Token Filter nie zadziała?
Spróbujmy teraz wyszukać frazę (match_phrase
) zamiast pojedynczego słowa:
|
GET /bloggers_with_synonyms/_search { "query": { "match_phrase": { "content": "Piotr Prądzyński jest autorem" } } } |
Na razie działa, ale w tym momencie Elasticsearch nie musiał korzystać z synonimów:
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
|
{ "took" : 2, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.5069062, "hits" : [ { "_index" : "bloggers_with_synonyms", "_type" : "_doc", "_id" : "1", "_score" : 1.5069062, "_source" : { "content" : "Piotr Prądzyński jest autorem bloga Programista Na Swoim." } } ] } } |
Spróbujmy teraz podobnego zapytania, ale z wykorzystaniem synonimu:
|
GET /bloggers_with_synonyms/_search { "query": { "match_phrase": { "content": "prondzyn jest autorem" } } } |
Nie działa 🙁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 0, "relation" : "eq" }, "max_score" : null, "hits" : [ ] } } |
Dlaczego tak się stało?
Odpowiedź na to pytanie została bardzo ładnie opisana przez Michaela McCandlessa, współautora książki Lucene in Action, w artykule: Lucene’s TokenStreams are actually graphs!
Artykuł jest krótki i przejrzysty, więc nie będę go duplikował i opisywał tu wszystkich szczegółów, ale generalnie chodzi o to, że strumień tokenów to tak naprawdę graf. Kiedy tworzymy synonimy składające się z różnej liczby tokenów to zaburzamy strukturę grafu. W takim przypadku liczba węzłów w grafie z wykorzystaniem synonimu nie jest równa liczbie węzłów w grafie oryginalnym. Tak też stało się w naszym przykładzie kiedy to pseudonim składał się z jednego tokena, a imię i nazwisko z dwóch.
Synonym Graph Token Filter na ratunek!
Co robić? Jak żyć? Na szczęście twórcy Elasticsearcha zdążyli przygotować już rozwiązanie zastępcze: Synonym Graph Token Filter, a Michael McCandless opisał je w artykule: Multi-Token Synonyms and Graph Queries in Elasticsearch.
Zmodyfikujmy teraz nasz przykład, używając grafowego filtra synonimów:
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 33
|
PUT /bloggers_with_graph_synonyms { "settings": { "index": { "analysis": { "filter": { "synonym_filter": { "type": "synonym_graph", "synonyms": [ "prondzyn, Piotr Prądzyński" ] } }, "analyzer": { "synonym_analyzer": { "tokenizer": "standard", "filter": [ "synonym_filter" ] } } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "synonym_analyzer" } } } } |
Dodajmy dokument:
|
PUT /bloggers_with_graph_synonyms/_doc/1 { "content" : "Piotr Prądzyński jest autorem bloga Programista Na Swoim." } |
Wyszukajmy w dokładnie ten sam sposób:
|
GET /bloggers_with_graph_synonyms/_search { "query": { "match_phrase": { "content": "Piotr Prądzyński jest autorem" } } } |
Działa, ale w tym momencie nie zaprzęgaliśmy synonimów do pracy:
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
|
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.5069062, "hits" : [ { "_index" : "bloggers_with_graph_synonyms", "_type" : "_doc", "_id" : "1", "_score" : 1.5069062, "_source" : { "content" : "Piotr Prądzyński jest autorem bloga Programista Na Swoim." } } ] } } |
Zapytajmy ponownie, z wykorzystaniem synonimów:
|
GET /bloggers_with_graph_synonyms/_search { "query": { "match_phrase": { "content": "prondzyn jest autorem" } } } |
Działa! 🙂
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
|
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.5069062, "hits" : [ { "_index" : "bloggers_with_graph_synonyms", "_type" : "_doc", "_id" : "1", "_score" : 1.5069062, "_source" : { "content" : "Piotr Prądzyński jest autorem bloga Programista Na Swoim." } } ] } } |
Czy Synonym Token Filter jest jeszcze potrzebny?
Jak najbardziej. Synonym Graph Token Filter nie jest lekarstwem na całe zło. Ze względu na to, że znajdujący się pod spodem index Lucene nie potrafi przechowywać grafu, to Synonym Graph Token Filter może być użyty wyłącznie w czasie wyszukiwania, co pociąga za sobą większe zużycie CPU i IO. Jeżeli chcemy składować synonimy w indeksie (na przykład ze względu na wydajność), to nadal musimy wykorzystać Synonym Token Filter.
The End
Muszę przyznać, że lubię takie sytuacje 🙂 Momenty, kiedy publikuję coś na blogu, a ktoś na tej podstawie podpowie mi, co można zrobić lepiej. Jest to definitywnie jeden z moich ulubionych benefitów wynikających z blogowania – Twój feedback i nauka na jego podstawie. Teraz również na niego czekam 🙂 Jeżeli tylko masz jakieś uwagi do dzisiejszego wpisu (lub jakiegokolwiek innego) to śmiało pisz do mnie w komentarzach albo gdzie Ci tam tylko wygodnie.
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
22 kwietnia 2020 at 21:42
Bardzo fajny blog i seria o elasticku.
Chiałbym zapytać o elasticka z trochę innej perspektywy…
Jak należy / z czym go najlepiej używać ??
Z jednej strony napisane jest że to silnik do szukania z drugiej że to baza…
W internecie można znaleźć wiele schematów w którch elastik odgrywa różne role w architektórze projektu np.
połączenia elastika z mongoDb, elastik z hibernate OGM, elastik z hibernate OGM i other noSQL db, sam elastik i wiele innych połączeń.
Jak wyciągnąć z tego jak najwięcej ? Zawsze się mówi że to zależy czego potrzebujemy…
Załóżmy że nie wiem czego potrzebuję i miłoby było usłyszeć dlaczego są takie wariancje.
Pozdrawiam
23 kwietnia 2020 at 08:09
Dzięki serdeczne za miłe słowa 🙂
Ta dezorientacja może wynikać poniekąd z historii Elasticsearcha. Został on stworzony jako narzędzie mające ułatwiać pełnotekstowe wyszukiwanie (stąd też informacje o tym, że to silnik wyszukiwania). Elasticsearch istnieje już 10 lat i przez większość początkowych lat jego twórca/twórcy zaznaczali, żeby nie traktować go jako główne miejsce gromadzenia danych. Podkreślano, że jest to silnik wyszukiwania pełnotekstowego i żeby używać go właśnie do tego jako coś stojące obok podstawowej bazy danych (stąd te wszystkie Elasticsearch + cośtam).
Do tej pory Elasticsearch ma braki lub niedociągnięcia w narzędziach wspomagających aktualizowanie indeksu. Bowiem przez większość czasu autorzy rekomendowali po prostu przebudować indeks od zera, kiedy musimy zrobić jakiś większy update. Było również mówione wprost, że indeks w pewnych okolicznościach może się zepsuć i żeby go odbudować z podstawowej bazy danych.
Mimo tych wszystkich zaleceń część osób stosowała i stosuje Elasticsearch jako podstawowe składowisko danych, bo w ich przypadkach być może jest wystarczający. Również sam Elasticsearch przez lata dojrzewał i obecnie stawia na większą niezawodność. Dorabiane są kolejne narzędzia, które pomagają aktualizować indeks bez konieczności całkowitej przebudowy. Nie mówi się już też tak otwarcie, że Elasticsearch nie nadaje się jako główna baza danych. I stąd te wszystkie artykuły, że Elasticsearch to baza danych.
Czy rozjaśniłem nieco sytuację?
28 kwietnia 2020 at 07:28
Hej,
Tak, odpowiedź zdecydowanie dużo mi poukładała w głowie 🙂
Wielkie dzięki!
Oby więcej takich wpisów i blogów! 🙂
Pozdrawiam
28 kwietnia 2020 at 07:31
26 listopada 2019 at 23:40
Znakomity artykuł! Cieszę się, że zainspirowałem 🙂
27 listopada 2019 at 08:06
To ja dziękuję! 🙂