Space Vatican

kiedy indeksujesz dane, świat rzadko jest tak prosty, jak każdy dokument istniejący w izolacji. Czasami lepiej denormalizować wszystkie dane w dokumentach potomnych. Na przykład, jeśli modelujesz książki, dodanie pola autor do książek może być rozsądnym wyborem (nawet jeśli w bazie danych, która jest Twoim autorytatywnym źródłem danych, dane są dzielone na osobne authors i books tabela). To proste i możesz łatwo konstruować zapytania dotyczące zarówno atrybutów książki, jak i nazwiska autora.

to nie zawsze jest praktyczne – w dokumencie nadrzędnym może być zbyt wiele danych, aby zduplikować je w każdym dokumencie podrzędnym. Gdybyś miał typową aplikację na blogu / komentarzu, nie chciałbyś powtarzać całej treści postu na blogu w każdym komentarzu, ponieważ znacznie zwiększyłoby to ilość indeksowanych danych. Jednak bez tego nie możesz łatwo pisać zapytań, aby znaleźć komentarze do postów spełniających określone kryteria (inne niż wykonując krok 2, najpierw znajdując pasujące posty, a następnie pobierając komentarze z pewnym post_id, co często jest nieporęczne lub powolne (lub oba)).

inną opcją jest umieszczenie dokumentów potomnych wewnątrz dokumentu nadrzędnego, na przykład możesz mieć dokumenty formularza

123456789101112
{ "name": "A. N Author", "biography": "A leading wordsmith", "books": }

jedną z wad jest to, że dodanie potomka wymaga reindeksowania całego dokumentu. Znalezienie autorów, którzy napisali książki z określonym gatunkiem jest łatwe, ale znalezienie autorów, którzy mieli książkę science fiction opublikowaną przez penguin, jest trudniejsze.

jeśli nasz indeks zawiera

to najbardziej oczywiste zapytanie

znajduje obu autorów – nie możesz wyrazić, że twoje warunki na published i genre muszą pasować do tej samej książki.

ElasticSearch zapewnia dwie rzeczy, które pomagają w tym. Pierwszym z nich jest koncepcja zagnieżdżonego dokumentu/zapytania. To pozwala powiedzieć, że szukasz autorów, których co najmniej jedna książka spełnia oba Twoje kryteria.

najpierw musisz skonfigurować mapowanie, które mówi, że pole książki będzie zagnieżdżone:

123456789
curl -XPOST localhost:9200/authors/nested_author/_mapping -d '{ "nested_author":{ "properties":{ "books": { "type": "nested" } } }}'

Jeśli następnie wstawimy te same dane co wcześniej do tego nowego indeksu, to to zapytanie

tutaj filtr nested pozwala uruchomić zapytanie przeciwko zagnieżdżonym dokumentom (tj. książkom) i filtrować autorów po tych, którzy mają co najmniej jeden zagnieżdżony dokument pasujący do zapytania. Opcja path mówi nam, do której części dokumentu autorów odnosi się to zapytanie, a następnie opcja query jest zapytaniem, które należy uruchomić przeciwko tym zagnieżdżonym dokumentom. W przeciwieństwie do poprzedniego zapytania wymaga to znalezienia pojedynczej książki spełniającej oba wymagania, więc zwracany jest tylko Alaistair Reynolds

rodzic & dziecko

druga koncepcja Elasticsearch to relacja rodzica i dziecka między dokumentami. Poprzedni przykład można przerobić z autorami jako dokumentami nadrzędnymi i książkami jako dokumentami podrzędnymi.

tym razem indeksuj autorów oddzielnie od ich książek:

następnie skonfiguruj mapowanie dla typu książki i powiedz, że jej typem nadrzędnym jest bare_author. Musisz to zrobić, zanim stworzysz jakiekolwiek książki.

12345
curl -XPOST localhost:9200/authors/book/_mapping -d '{ "book":{ "_parent": {"type": "bare_author"} }}'

kiedy indeksujemy książki, musisz podać id ich rodzica (tzn. podajemy id jednego z wcześniej utworzonych autorów)

Elasticsearch zapewnia filtr has_child, który robi prawie to, co jest napisane na puszce: wybiera dokumenty nadrzędne, w których przynajmniej jedno dziecko spełnia określone zapytanie. To zapytanie znajduje wtedy tylko Alastair Reynolds:

Solr 4.0 najwyraźniej ma możliwość łączenia, chociaż z tego, co mogę powiedzieć, wiąże się to z pewnymi ograniczeniami, w szczególności bez łączenia, jeśli używasz środowiska rozproszonego. Ograniczając się do relacji typu rodzic / dziecko elasticsearch ułatwia sobie życie: dziecko jest zawsze indeksowane w tym samym Odłamku, co jego rodzic, więc has_child nie musi wykonywać niezręcznych operacji Cross shard.

tworzenie list

Możesz również użyć tego do modelowania list konkretnych użytkowników współdzielonych elementów globalnych – np. jeśli chcesz, aby elementy zostały ocenione przez użytkownika. W tym przypadku dokumenty potomne reprezentują fakt, że konkretny użytkownik ocenił konkretny post – są to nic innego jak user_id, post_id i ocena: tabela połączeń w lingo relacyjnej bazy danych.

używanie relacji rodzic / dziecko i has_child pozwala łatwo znaleźć wszystkie posty ulubione przez użytkownika, jednocześnie umożliwiając użytkownikom wyszukiwanie ich ulubionych na podstawie zawartości postu, daty lub innych atrybutów postu lub właściwości elementu podrzędnego. Dodanie elementu do listy ocenianych elementów jest tanie-wymaga tylko indeksowania bardzo małego elementu rating.

z tymi dokumentami

to zapytanie

znajduje tylko „Boliwia oceniona na 4”, ponieważ jest to jedyny post wymieniający Boliwię, który został oceniony na 3 przez użytkownika, którym jesteśmy zainteresowani. Zapytanie najwyższego poziomu w tytule dotyczy postów, gdzie zapytanie wewnątrz filtru has_child opisuje warunki, które dzieci muszą pasować (w tym przypadku muszą należeć do określonego użytkownika i mieć co najmniej określoną ocenę).

zamawianie

co has_child nie pozwala Ci zrobić, to zamówić na podstawie atrybutów dzieci lub zwrócić atrybuty dziecka. Jeśli chcesz zamówić posty oceniane przez użytkownika na podstawie tego, kiedy zostały ocenione lub zmniejszając ocenę, możesz wyszukiwać bezpośrednio według postów/oceny, ale możesz również zastosować niektóre kryteria wyszukiwania do postów. Na przykład możesz chcieć znaleźć tylko ocenione posty na określony temat (nadal zamawiając według oceny, którą dał użytkownik). Z has_child masz pecha. Zagnieżdżone dokumenty również nie pomagają.

od 0.19.10 możesz użyć filtra has_parent. Działa to prawie tak samo jak has child, ale pozwala na określenie zapytania względem elementów nadrzędnych. To zapytanie zwraca oceny użytkownika 1234, na postach, których tytuł pasuje do „bolivia”, w kolejności malejącej oceny

zwraca obiekty oceny-trzeba wtedy pobrać odpowiednie posty z osobnym zapytaniem.

udawanie

jeśli utknąłeś na starszej wersji elasticsearch, możesz uzyskać większość drogi tam z top_children. Jak mówi dokumentacja top_children najpierw pyta dokumenty potomne, a następnie agreguje je do dokumentów nadrzędnych. W naszym przykładzie oznacza to, że elasticsearch najpierw znajdzie dokumenty oceny pasujące do naszego zapytania. Następnie dopasuje każdą ocenę do postu nadrzędnego, agregując zduplikowany post tam, gdzie istnieją.

najciekawsze jest to, że elasticsearch nie wie z wyprzedzeniem, ile dokumentów straci, gdy dojdzie do agregacji. W tym konkretnym przypadku jest to łatwe, ponieważ dwie różne oceny tego samego Użytkownika zawsze odpowiadają dwóm różnym postom, więc nie musimy się przejmować ustawieniami factor i incremental_factor, ponieważ faza agregacji nigdy nic nie robi. Podobnie, tryb punktacji również nie ma znaczenia. Jeśli chcesz podać dokładną liczbę wyników, wystarczy ustawić factor na tyle duże, że pierwszy sweep elasticsearch w dokumentach potomnych znajdzie je wszystkie. Jeśli wiesz, że użytkownik ma 500 ocenionych pozycji na swojej liście i prosisz o pierwsze 10 pozycji, współczynnik 50 powinien załatwić sprawę. Czynnik musi być tylko górną granicą – nie musisz dokładnie wiedzieć, ile pozycji użytkownik ma na swojej liście (co może być niezręczne do wypracowania bez oddzielnego elastycznego zapytania, jeśli użytkownik przeszukuje określony podzbiór swoich ocen).

to, co otrzymujesz po tym wszystkim, to lista dokumentów rodziców (postów) posortowana według wyniku zapytania dokumentów dzieci (ocen). Aby osiągnąć pierwotny cel sortowania postów na podstawie atrybutów dokumentów potomnych, musimy tylko upewnić się, że ten wynik zapytania ma odpowiednią wartość. Na przykład niech zapytanie top_children zawija zapytanie custom_score, aby mieć kontrolę nad wynikiem dla każdego dziecka.

z tymi samymi dokumentami w indeksie, to zapytanie zwraca posty, które użytkownik 1234 ocenił, uporządkowane według ich oceny:

uruchamiamy zapytanie top_children, więc pierwszą rzeczą, którą musimy zrobić, to powiedzieć, jaki jest typ dzieci, które rozważamy (ocena). Następnie dostarczamy zapytanie, które znajduje te dzieci. Jest to zapytanie custom_score, zawijające zapytanie filtered. Zapytanie filtered zapewnia, że znajdujemy tylko oceny podane przez użytkownika, którym jesteśmy zainteresowani, a następnie element script sprawia, że ocena dokumentu oceny jest oceną samą w sobie, dzięki czemu nasze posty są sortowane według ocen. Funkiness z odwrotnymi ukośnikami jest tylko dlatego, że próbuję włączyć dosłowny pojedynczy cytat w przyjaznym dla powłoki łańcuchu rozdzielonym pojedynczymi cudzysłowami – rzeczywisty json, który wysyłamy, ma tylko "script": "doc.value".

Ruby fun

niestety biblioteka opon tak naprawdę nie obsługuje w tej chwili żadnej z tych zabawnych rzeczy – jest trochę moratorium na dodawanie tego rodzaju funkcji, ponieważ w tej chwili każdy mały typ zapytania i opcja kończy się oddzielnymi metodami rozrzuconymi po całej oponie, czego opiekun zrozumiale nie lubi. Możesz to jakoś zhakować.

opona nie pozwala na ustawienie identyfikatora nadrzędnego dokumentu podczas indeksowania. Jest to proste do dodania i jest wstrzymywane tylko przez wspomniane moratorium. Mój widelec dodaje tę zdolność. Z tym kończy się

kolejnym nieszczęściem jest to, że tworzenie automatycznego indeksu opony zakłada jeden typ na indeks, ale aby relacja rodzic/dziecko istniała, oba typy muszą znajdować się w tym samym indeksie. Skończyło się na tym, że zrobiłem coś takiego, aby stworzyć moje indeksy.

co nie jest tak piękne, ale robi swoje.

na koniec musisz wykonać zapytanie. W przypadku braku top_children faktycznie jest częścią API tire można fudge to tak

ten trochę nieprzyjemności buduje zapytanie jako hash, a następnie wrzuca go do opony, gdy patrzy w drugą stronę. Oczywiście możesz to tak skonstruować, aby łatwo było dodać inne warunki (czy to na postu, czy na ocenie) do wyszukiwania. Możesz również zbudować JSON ręcznie i użyć Post.search :payload => my_json(jest błąd z opcją payload, która koliduje z rozszerzeniem rejestratora Tire-contrib)

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.