W trakcie całego życia Solr, jako silnika wyszukiwania, dostaliśmy możliwość pracy z rdzeniami, następnie z kolekcjami i aliasami – alternatywnymi nazwami kolekcji. Aliasy pozwalały na prostą separację prawdziwych nazw kolekcji oraz tego, co używa aplikacja. Dzięki temu możemy, posiadając wystarczającą ilość zasobów sprzętowych, re-indeksować dane bez konieczności zatrzymywania aplikacji korzystającej z Solr lub powodowania przestojów. W Solr mamy opcję korzystania z dwóch typów aliasów:
- standardowych aliasów grupujących kolekcję pod wirtualną nazwą,
- aliasy routed, które automatycznie przekierowują żądania.
Aliasy typu routed mogą być dalej podzielone na dwie kategorie – aliasy oparte o czas oraz aliasy oparte o wartość pola. W dzisiejszym wpisie skupimy się na tej drugiej kategorii.
Dlaczego Potrzebujemy Aliasów Opartych O Wartość Pola
Problemem standardowych aliasów jest indeksowanie. Kiedy tworzymy alias grupujący więcej, niż jedną kolekcję nie jesteśmy w stanie kontrolować, gdzie będą zaindeksowane nasze dokumenty. Na przykład, w Solr 8.2, nasze dokumenty zostaną zaindeksowane w pierwszej kolekcji zdefiniowanej przez alias. Zobaczmy to na przykładzie:
Zacznijmy od stworzenia dwóch kolekcji korzystając z API v2 Solr. Zaczynamy od pierwszej kolekcji:
curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/v2/c' -d '{
"create" : {
"name" : "test_1",
"numShards" : 1,
"replicationFactor" : 1
}
}'
A następnie tworzymy drugą kolekcję:
curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/v2/c' -d '{
"create" : {
"name" : "test_2",
"numShards" : 1,
"replicationFactor" : 1
}
}'
W tym momencie posiadamy w Solr dwie kolekcje – jedną nazwaną test_1 oraz drugą nazwaną test_2. Możemy zatem stworzyć alias nazwany test, który będzie grupował obie kolekcje. W tym celu użyjemy następującego polecenia:
curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/v2/c' -d '{
"create-alias" : {
"name" : "test",
"collections" : [ "test_1", "test_2" ]
}
}'
Wreszcie możemy zaindeksować dokument wykorzystując powyżej stworzony alias:
curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/solr/test/update?commit=true' -d '[
{
"id" : 1,
"name" : "Test indexing"
}
]'
Powyższe polecenia zostanie pomyślnie wykonane w Solr 8.2, a sam dokument zostanie zaindeksowany w kolekcji test_1. Sprawdzenie tego pozostawiam Tobie, jako czytelnikowi 🙂
Aby poradzić sobie z problemem nieokreśloności, jeżeli chodzi o indeksowanie dokumentów możemy skorzystać z aliasów opartych o czas lub o wartość pola.
Aliasy oparte o wartość pola
Pomysł leżący u podstaw aliasów opartych o wartość pola, to grupowanie dokumentów w kolekcjach w oparciu o pewną wspólną wartość. Routing na poziomie pojedynczej kolekcji powoduje, iż dany shard przechowuje dane posiadające tą samą wartość routingu – na przykład dane pojedynczego użytkownika. W przypadku aliasów opartych o wartość pola Solr idzie o krok dalej – pojedyncza kolekcja będzie posiadała dane związane z daną wartością pola – na przykład dane użytkowników pojedynczej firmy. Potrzebne kolekcje będą tworzone automatycznie, a więc nie musimy się tym przejmować zarówno na etapie indeksowania, jak i zapytań.
Tworzone aliasów opartych o wartość pola
Tworzenie aliasów opartych o wartość pola jest trochę inne jeżeli porównamy to do standardowych aliasów. Nie zaczynamy procesu tworzenia od stworzenia kolekcji – te będą tworzone automatycznie. Zamiast tego zaczynamy od stworzenia definicji aliasu.
Stwórzmy nasz pierwszy alias oparty o wartość korzystając z następującego polecenia:
curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/v2/c' -d '{
"create-alias" : {
"name" : "test_cra_company",
"router" : {
"name" : "category",
"field" : "company_name",
"maxCardinality" : 2
},
"create-collection" : {
"numShards" : "1",
"replicationFactor" : "1",
"config" : "_default"
}
}
}'
Zatrzymajmy się na chwilę. Skorzystaliśmy z polecenia create-alias aby stworzyć nowy alias nazwany test_cra_company. Dodatkowo podaliśmy kilka atrybutów w celu zdefiniowania zachowania aliasu. Przyjrzyjmy się tym właściwościom.
- name – typ routera, który będzie wykorzystywany. W tej chwili Solr wspiera dwa typy: time oraz category. Pierwszy typ tworzy aliasy oparte o czas, drugi oparte o wartość pola.
- field – nazwa pola, które będzie wykorzystane do routingu.
- maxCardinality – maksymalna liczba unikalnych wartości w polu. Pozwala na ograniczenie liczby stworzonych kolekcji.
Ponieważ nasz alias będzie tworzył kolekcje za nas, oprócz samego aliasu dodaliśmy sekcję create-collection zawierającą szereg parametrów określających jak będą wyglądać tworzone kolekcje. Warto pamiętać, iż jest taka możliwość.
W powyższych przykładach, aby ograniczyć skomplikowanie przykłaów, skorzystaliśmy z domyślej konfiguracji kolekcji. Należy pamiętać, aby nie robić tego w środowisku produkcyjnym w przypadku korzystania z aliasów opartych o wartość pola.
Jak działa indeksowanie kiedy korzystamy z aliasów opartych o wartość pola?
Kiedy Solr otrzymuje żądanie indeksowania korzysta z instancji klasy UpdateRequestProcessor. W przypadku SolrCloud, kiedy korzystamy ze standardowych aliasów Solr korzysta z instancji klasy DistributedUpdateProcessor. To ta klasa decyduje, gdzie dokument będzie finalnie umieszczony. W przypadku, kiedy korzystamy z aliasów opartych o wartość pola, przed instancją klasy DistributedUpdateProcessor Solr korzysta z klasy RoutedAliasUpdateProcessor. To ona decyduje, gdzie finalnie trafi nasz dokument, czyli tak naprawdę do której kolekcji.
W naszym przypadku, z powyżej stworzonym aliasem, zanim zostanie stworzona docelowa kolekcja, lub docelowe kolekcje specjalna kolkcja o nazwie test_cra_company__CRA__NEW_CATEGORY_ROUTED_ALIAS_WAITING_FOR_DATA__TEMP
zostanie stworzona. W chwili kiedy dane zaczną być indeksowane Solr zacznie tworzyć kolekcje nazwane test_cra_company__CRA__<WARTOŚĆ_POLA>
, na przykład test_cra_company__CRA__company1
jeżeli wartość pola company_name
będzie miała wartość company1
. To powoduje, iż mamy pewne ograniczenia jeżeli chodzi o wartość pola używanego do tworzenia kolekcji, ale porozmawiamy o tym później.
Indeksowanie danych używając aliasów opartych o wartość pola
Zobaczmy teraz jak sprawują się aliasy oparte o wartość pola w akcji. Aby prezprowadzić prosty test zaindeksujemy dwa dokumenty korzystając z następujących komend:
curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/solr/test_cra_company/update?commit=true' -d '[
{
"id" : 1,
"name" : "Test indexing",
"company_name" : "company1"
}
]'
curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/solr/test_cra_company/update?commit=true' -d '[
{
"id" : 2,
"name" : "Test indexing",
"company_name" : "company2"
}
]'
Pamiętasz atrybut maxCardinality, który ustawiliśmy na wartość 2? Jeżeli próbowalibyśmy zaindeksować dokument, który będzie miał kolejną, trzecią z rzędu wartość pola company_name Solr powinien zwrócić błąd. Na przykład skorzystajmy z następującego polecenia:
curl -XPOST -H 'Content-type:application/json' 'http://localhost:8983/solr/test_cra_company/update?commit=true' -d '[
{
"id" : 3,
"name" : "Test indexing",
"company_name" : "company3"
}
]'
Solr zwróci nam następujący błąd:
{
"responseHeader":{
"status":400,
"QTime":1},
"error":{
"metadata":[
"error-class","org.apache.solr.common.SolrException",
"root-error-class","org.apache.solr.common.SolrException"],
"msg":"Max cardinality 2 reached for Category Routed Alias: test_cra_company",
"code":400}}
Zapytania
Możemy także spróbować wyszukiwania, np. takiego, które zwróci wszystkie dokumenty:
http://localhost:8983/solr/test_cra_company/select?q=*:*
W tym wypadku Solr zwróci następujący rezultat:
{
"responseHeader": {
"zkConnected": true,
"status": 0,
"QTime": 12,
"params": {
"q": "*:*"
}
},
"response": {
"numFound": 2,
"start": 0,
"maxScore": 1,
"docs": [
{
"id": "1",
"name": [
"Test indexing"
],
"company_name": [
"company1"
],
"_version_": 1647547697554522000
},
{
"id": "2",
"name": [
"Test indexing"
],
"company_name": [
"company2"
],
"_version_": 1647547721335177200
}
]
}
}
Możemy korzystać z filtrów:
http://localhost:8983/solr/test_cra_company/select?q=*:*&fq=company_name:company2
W tym wypadku odpowiedź będzie zawierać tylko dokument, którego się spodziewamy:
{
"responseHeader": {
"zkConnected": true,
"status": 0,
"QTime": 25,
"params": {
"q": "*:*",
"fq": "company_name:company2"
}
},
"response": {
"numFound": 1,
"start": 0,
"maxScore": 1,
"docs": [
{
"id": "2",
"name": [
"Test indexing"
],
"company_name": [
"company2"
],
"_version_": 1647547721335177200
}
]
}
}
Jak widać wszystko działa tak, jakbyśmy się tego spodziewali.
Gwoli ścisłości, jeżeli chcielibyśmy wiedzieć, jakie kolekcje zostały stworzone to mamy je widoczne na poniższym obrazku:
Ograniczenia
Pisząc o funkcjonalności aliasów opartych o wartość pola należy wspomnieć o ich ograniczeniach.
Po pierwsze należy pamiętać o nazewnictwie. Do tworzenia kolekcji Solr korzysta z wartości znajdujących się w zdefiniowanym polu. Oznacza to, iż niektóre ze znaków UTF-8 mogą nie być obsługiwane i najlepiej trzymać się podstawowych znaków ASCII. W przeciwnym razie Solr może nie być w stanie stworzyć odpowiedniej kolekcji co może prowadzić do poważnych konsekwencji.
Drugim ograniczeniem jest usuwanie aliasu lub kolekcji w nim zawartych. Nie ma żadnej zautomatyzowanej funkcjonalności, która pozwoliłaby na usunięcie kolekcji z aliasu. Na przykład, jeżeli chcielibyśmy usunąć pojedynczą wartość z aliasu, czyli tak naprawdę kolekcję konieczne byłoby:
- Upewnienie się, że nie będziemy już wysyłać dokumentów z wartością pola, którą chcemy usunąć.
- Zmodyfikowanie aliasu, tak aby nie zawierał już kolekcji odpowiedzialnej za przechowywanie danych, które chcemy usunąć.
- Usunięcie kolekcji korzystając z API Solr – musimy pamiętać o tym, że kolekcja musi być usunięcia z aliasu, zanim Solr pozwoli nam na fizyczne usunięcie kolekcji.
W chwili publikowania tego wpisu, czyli w wersji 8.2 Solr, podczas indeksowania żądanie jest przekazywane do odpowiedniej kolekcji, natomiast wyszukiwanie uruchamiane jest na wszystkich kolekcjach, które grupowane są w ramach danego aliasu. Wysyłanie zapytań do kolekcji, które odpowiadają za przechowywanie danych dla danej wartości pola jest wymienione jako jedno z usprawnień możliwych do implementacji w przyszłości.
Ostatnia rzecz o jakiej należy pamiętać to tworzenie kolekcji. Nasz alias będzie tworzył kolekcję w chwili kiedy do Solr zostaną dostarczone dane z wartością pola dla którego kolekcja jeszcze nie istnieje. Należy pamiętać, iż tworzenie kolekcji trwa, czasem nawet do trzech sekund i zależy od obciążenia naszego klastra. Aplikacja indeksująca dane musi być zaimplementowana w sposób biorący to ograniczenie pod uwagę.
Podsumowanie
Jak widzimy aliasy oparte o wartość pola dostarczają bardzo fajnej metody automatycznego tworzenia kolekcji w oparciu o wartość dostarczaną w jednym z pól dokumentów. Jeżeli jest to coś czego potrzebujemy warto zastanowić się nad wykorzystaniem tej funkcjonalności, zwłaszcza w nowszych wersjach Solr. Czy ta funkcjonalność jest skończona i dopracowania – nie, dalej istnieją ograniczenia i możliwości optymalizacji o których wspomnieliśmy. Miejmy nadzieję, że zmiany te będą dostępne w następnych wersjach Solr.