Jedną z ciekawszych rzeczy, którą wyniosłem z zeszłorocznej edycji konferencji ByteBay była informacja o aliasach indeksów w Elasticsearchu i o potencjalnych sposobach ich użycia. Niedawno miałem okazję wykorzystać je produkcyjnie. Dziś chciałbym podzielić się z Wami czym są owe aliasy i do czego można je wykorzystać.
Czym są aliasy indeksów?
Obstawiam, że ciężko jest się nie domyślić czym są aliasy indeksów. Za tą nazwą nie kryje się nic tajemniczego. Są to po prostu dodatkowe nazwy dla naszych indeksów, po których możemy odpytywać Elasticsearch. W większości przypadków wygląda to tak, że wszędzie tam gdzie możemy wstawić nazwę indeksu, możemy też wstawić jego alias. Dodatkowo mają one kilka ciekawych właściwości, które naprawdę przypadły mi do gustu.
Przykłady użycia
Załóżmy sobie, że tworzymy portal będący bazą wiedzy na temat Gwiezdnych wojen. Do Elasticsearcha wrzucamy cytaty kilku postaci. Każda postać ma swój dedykowany indeks.
Wrzucamy Yodę:
|
POST /yoda/_doc/1 { "quote": "No! Try not! Do or do not, there is no try." } POST /yoda/_doc/2 { "quote": "Judge me by my size, do you?" } |
Szukamy go:
Jest:
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 34
|
{ "took": 3, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "yoda", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "Judge me by my size, do you?" } }, { "_index": "yoda", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No! Try not! Do or do not, there is no try." } } ] } } |
Wrzucamy Dartha Vadera:
|
POST /vader/_doc/1 { "quote": "No, I am your father!" } POST /vader/_doc/2 { "quote": "When I left you I was but the learner. Now I am the master." } |
Szukamy go:
Jest:
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 34
|
{ "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "vader", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "When I left you I was but the learner. Now I am the master." } }, { "_index": "vader", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No, I am your father!" } } ] } } |
Nasz pierwszy alias
Jeden z programistów naszego portalu wymyślił sobie jednak, że chciałby zapytać o cytaty ojca Luke’a:
|
GET /lukes_father/_search |
Nie ma:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
{ "error": { "root_cause": [ { "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "lukes_father", "index_uuid": "_na_", "index": "lukes_father" } ], "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "lukes_father", "index_uuid": "_na_", "index": "lukes_father" }, "status": 404 } |
No ale przecież wiemy, kto jest ojcem Luke’a. Może w takim razie skopiujemy jeszcze raz cytaty Dartha Vadera i wrzucimy je pod indeks lukes_father
?
No ale jak to? Tak się powtarzać?! Każdy kolejny update na danych będziemy musieli robić podwójnie. I tu właśnie wkracza nasz bohaterski alias.
Tworzymy alias lukes_father
dla istniejącego indeksu vader
:
|
POST /_aliases { "actions": [ { "add": { "index": "vader", "alias": "lukes_father" } } ] } |
Pytamy jeszcze raz:
|
GET /lukes_father/_search |
Jest! Mamy to! Warto zauważyć, że mimo pytania poprzez alias, w polach _index
znajduje się nazwa właściwego indeksu:
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 34
|
{ "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "vader", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "When I left you I was but the learner. Now I am the master." } }, { "_index": "vader", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No, I am your father!" } } ] } } |
Przepinanie aliasu
Tym razem nasz nadgorliwy programista chciałby znaleźć cytaty najsilniejszego Jedi:
|
GET /strongest_jedi/_search |
Niestety nie znalazł odpowiedzi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
{ "error": { "root_cause": [ { "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "strongest_jedi", "index_uuid": "_na_", "index": "strongest_jedi" } ], "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "strongest_jedi", "index_uuid": "_na_", "index": "strongest_jedi" }, "status": 404 } |
I wtedy wchodzi alias, cały na biało:
|
POST /_aliases { "actions": [ { "add": { "index": "yoda", "alias": "strongest_jedi" } } ] } |
Od teraz najsilniejszym Jedi jest Yoda:
|
GET /strongest_jedi/_search |
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 34
|
{ "took": 1, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "yoda", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "Judge me by my size, do you?" } }, { "_index": "yoda", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No! Try not! Do or do not, there is no try." } } ] } } |
Yoda jednak w pewnym momencie „schodzi ze sceny”. Aktualizujemy więc najsilniejszego Jedi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
POST /_aliases { "actions": [ { "remove": { "index": "yoda", "alias": "strongest_jedi" } }, { "add": { "index": "vader", "alias": "strongest_jedi" } } ] } |
A kiedy nasz programista zapyta ponownie:
|
GET /strongest_jedi/_search |
To otrzyma zaktualizowane dane:
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 34
|
{ "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "vader", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "When I left you I was but the learner. Now I am the master." } }, { "_index": "vader", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No, I am your father!" } } ] } } |
One alias to rule them all
Kolejnego dnia nasz programista chciałby przygotować zbiorczą podstronę zwierającą cytaty wszystkich postaci z Gwiezdnych wojen. Pyta więc:
|
GET /star_wars_characters/_search |
Ale odpowiedzi nie dostaje:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
{ "error": { "root_cause": [ { "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "star_wars_characters", "index_uuid": "_na_", "index": "star_wars_characters" } ], "type": "index_not_found_exception", "reason": "no such index", "resource.type": "index_or_alias", "resource.id": "star_wars_characters", "index_uuid": "_na_", "index": "star_wars_characters" }, "status": 404 } |
I znów ratują nas aliasy. Jedną z zapowiedzianych ciekawych właściwości, jest możliwość dodania tego samego aliasu do kilku indeksów:
|
POST /_aliases { "actions": [ { "add": { "indices": [ "yoda", "vader" ], "alias": "star_wars_characters" } } ] } |
Pytamy ponownie:
|
GET /star_wars_characters/_search |
I voilà:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
{ "took": 2, "timed_out": false, "_shards": { "total": 10, "successful": 10, "skipped": 0, "failed": 0 }, "hits": { "total": 4, "max_score": 1, "hits": [ { "_index": "vader", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "When I left you I was but the learner. Now I am the master." } }, { "_index": "yoda", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "Judge me by my size, do you?" } }, { "_index": "vader", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No, I am your father!" } }, { "_index": "yoda", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No! Try not! Do or do not, there is no try." } } ] } } |
Po kilku latach działania portalu okazało się, że twórcy Gwiezdnych wojen dostarczyli nam nowego bohatera. Dodajemy więc jego cytaty do dedykowanego indeksu:
|
POST /finn/_doc/1 { "quote": "Finn. Yeah. Finn, I like that. I like that." } POST /finn/_doc/2 { "quote": "That's one hell of a pilot!" } |
Upewniamy się, czy wszystko działa:
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 29 30 31 32 33 34
|
{ "took": 4, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 1, "hits": [ { "_index": "finn", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "That's one hell of a pilot!" } }, { "_index": "finn", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "Finn. Yeah. Finn, I like that. I like that." } } ] } } |
Hmm… po chwili przypomniało nam się o zbiorczej stronie z cytatami wszystkich bohaterów. Dzięki aliasom wystarczy, że dodamy odpowiedni do świeżo stworzonego indeksu:
|
POST /_aliases { "actions": [ { "add": { "index": "finn", "alias": "star_wars_characters" } } ] } |
I kiedy znów zapytamy:
|
GET /star_wars_characters/_search |
To Finn już tam będzie:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
|
{ "took": 3, "timed_out": false, "_shards": { "total": 15, "successful": 15, "skipped": 0, "failed": 0 }, "hits": { "total": 6, "max_score": 1, "hits": [ { "_index": "finn", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "That's one hell of a pilot!" } }, { "_index": "vader", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "When I left you I was but the learner. Now I am the master." } }, { "_index": "yoda", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "Judge me by my size, do you?" } }, { "_index": "finn", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "Finn. Yeah. Finn, I like that. I like that." } }, { "_index": "vader", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No, I am your father!" } }, { "_index": "yoda", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No! Try not! Do or do not, there is no try." } } ] } } |
Prawda, że fajnie?
Edycja danych poprzez alias
Okazuje się również, że aliasy nie służą wyłącznie do odczytu. Możemy je wykorzystywać również do zapisu.
Na przykład nie mam problemu z dodaniem kolejnego cytatu, który padł z ust ojca Luke’a:
|
POST /lukes_father/_doc/3 { "quote": "Just for once let me look on you with my own eyes... You were right. You were right about me. Tell your sister you were right." } |
W odpowiedzi od razu otrzymamy informację, do jakiego indeksu wpadł nowo dodany dokument:
|
{ "_index": "vader", "_type": "_doc", "_id": "3", "_version": 1, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "_seq_no": 0, "_primary_term": 1 } |
Możemy teraz sprawdzić, że niezależnie od tego, czy zapytamy przez alias:
|
GET /lukes_father/_search |
Czy też bezpośrednio przez indeks:
To odpowiedź zawsze będzie wyglądała tak samo:
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 34 35 36 37 38 39 40 41 42 43
|
{ "took": 4, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, "max_score": 1, "hits": [ { "_index": "vader", "_type": "_doc", "_id": "2", "_score": 1, "_source": { "quote": "When I left you I was but the learner. Now I am the master." } }, { "_index": "vader", "_type": "_doc", "_id": "1", "_score": 1, "_source": { "quote": "No, I am your father!" } }, { "_index": "vader", "_type": "_doc", "_id": "3", "_score": 1, "_source": { "quote": "Just for once let me look on you with my own eyes... You were right. You were right about me. Tell your sister you were right." } } ] } } |
W tym miejscu może nasunąć się pytanie: a co z zapisem do aliasu, który wskazuje na kilka indeksów?
Sprawdźmy:
|
POST /star_wars_characters/_doc/4 { "quote": "I am main character in Star Wars." } |
A Elasticsearch na to:
|
{ "error": { "root_cause": [ { "type": "illegal_argument_exception", "reason": "Alias [star_wars_characters] has more than one indices associated with it [[yoda, vader, finn]], can't execute a single index op" } ], "type": "illegal_argument_exception", "reason": "Alias [star_wars_characters] has more than one indices associated with it [[yoda, vader, finn]], can't execute a single index op" }, "status": 400 } |
Logiczne 🙂
Dopóki alias wskazuje na jeden indeks, to nie ma problemu z wykonywaniem działań przeznaczonych do wykonania na pojedynczym indeksie. Jeżeli jednak alias wskazuje na więcej niż jeden indeks, to Elasticsearch sam nie zdecyduje, na którym indeksie ma wykonać akcję.
Od wersji 6.4 możemy mu w tym pomóc przy pomocy parametru is_write_index
. Dzięki niemu, w momencie tworzenia indeksu możemy określić, czy dany indeks jest tym do zapisu. Żeby wszystko śmigało, dokładnie jeden indeks w aliasie może być do zapisu. Reszta musi mieć ustawione is_write_index
na false
.
Do czego to się może przydać?
Aliasy indeksów są dla mnie ciekawą opcją pozwalającą wprowadzić dodatkowy bufor bezpieczeństwa pomiędzy aplikacją korzystającą z Elasticsearch, a naszym indeksem. Wykorzystania aliasu jako pośrednika pozwala nam przykładowo na wprowadzanie zmian w kopii naszego indeksu gdzieś „na boku”, bez grzebania bezpośrednio w indeksie produkcyjnym. Kiedy efekty zmian będą satysfakcjonujące, w mgnieniu oka możemy podmienić indeks na nowy poprzez proste przepięcie aliasu z jednego indeksu na drugi. A w aplikacji produkcyjnej nikt nawet nie zauważy przestoju.
Kolejnym pomysłem jest określanie widoczności danych, w których możemy wyszukiwać. Załóżmy na przykład, że w aplikacji umożliwiamy przeglądanie użytkownikom jakichś statystyk. Statystyki z każdego dnia są wrzucane codziennie do nowego indeksu w Elasticsearchu. Dokładając do tego wszystkiego alias możemy określić, z których dni chcemy udostępniać nasze statystyki, a które zostawiamy tylko dla siebie. A może po jakimś czasie starsze statystyki nie są już wiele warte i nie chcemy ich dłużej pokazywać? Korzystając z aliasu załatwimy sprawę odpinając go od przestarzałych indeksów.
To jeszcze nie wszystko
Jeżeli zainteresował Cię temat aliasów, to zachęcam do doczytania dokumentacji. Znajdziesz tam jeszcze kilka ciekawostek. Szczególnie interesująca i przydatna może być opcja tworzenia aliasów filtrujących. Temat jest bardzo prosty. Wykorzystując parametr filter
podczas tworzenia aliasu sprawiamy, że nie będzie on pokazywał wszystkich dokumentów z indeksu, a jedynie te spełniające określone kryteria filtrujące.
The End
Mam nadzieję, że czytając ten artykuł od razu wpadło Ci do głowy kilka pomysłów na wykorzystanie aliasów w Twoim projekcie. A może masz już za sobą jakieś ciekawe przypadki użycia? W obu przypadkach chętnie przeczytam o tym w komentarzu poniżej.
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