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 |
|
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 |
|
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 |
|
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)