Aliasy i Routing Na Podstawie Wartości Pola

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ń.

Uproszczona wersja routingu korzystającego z kategorii

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.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *