W poprzednim wpisie zajmowaliśmy się tematem mapowania, ale pewnie już wiesz, że nawet gdy nie utworzymy wcześniej mappingu, możemy dodawać do Elasticsearcha nowe dokumenty, a sam silnik poradzi sobie na swój sposób z mapowaniem, choć niekoniecznie będzie to zadowalające.

Domyślne reguły mapowania

Domyślnie, kiedy Elasticsearch znajdzie nowe pole w dokumencie, doda to pole do mapowania. Jakimi regułami kieruje się, ilustruje to poniższa tabela:

JSON datatype Elasticsearch datatype
null No field is added.
true or false boolean field
floating point number float field
integer long field
object object field
array Depends on the first non-null value in the array.
string Either a date field (if the value passes date detection), a double or long field (if the value passes numeric detection) or a text field, with a keyword sub-field.

Źródło: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html

Oczywiście, tworząc nasz mapping, możemy zmienić domyślne zachowanie Elasticsearcha i poprzez zmianę wartości parametru dynamic na false, uniemożliwimy automatyczne tworzenie mapowania w przypadku znalezienia nowych pól.
Jeśli chcesz wpłynąć na zachowanie dynamicznego mappingu Elasticsearcha, możesz to zrobić za pomocą tzw. szablonu dynamicznego (Dynamic Template). Zaraz stworzymy taki szablon dla dwóch przypadków:

  1. Chcemy zmienić domyślny typ, jakiego używa silnik Elasticsearcha przy dynamicznym mapowaniu, używając match_mapping_type.
  2. Chcemy ustawić domyślny typ do mapowania w zależności od nazw pól w dokumencie, używając match i unmatch.

Jaki typ mapowania wybrać?

Kiedy tworzyć szablon typu 1? Np. kiedy chcesz, aby wartości liczbowe typu integer nie były domyślnie mapowane na typ long, ale właśnie na integer.
Przykład:
PUT orders_dynamic_template_test
{
"mappings": {
"test_type": {
"dynamic_templates": [
{
"long_to_integers": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
}
]
}
}
}

Po dodaniu szablonu, spodziewaj się poniższego potwierdzenia:

{
"acknowledged": true,
"shards_acknowledged": true
}

Następnie dodajemy nowy dokument do naszego indeksu:

PUT orders_dynamic_template_test/test_type/1
{
"amountOfProducts": 23
}

Teraz jest pora na sprawdzenie mapowania w naszym indeksie. Zrób to za pomocą polecenia:

GET orders_dynamic_template_test/_mapping

Wówczas powinien pokazać się poniższy wynik:

{
"orders_dynamic_template_test": {
"mappings": {
"test_type": {
"dynamic_templates": [
{
"long_to_integers": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
}
],
"properties": {
"amountOfProducts": {
"type": "integer"
}
}
}
}
}
}

Jak widzisz, pole amountOfProducts jest typu integer (dodatkowo dostajemy informacje o Dynamic Template). Dodajmy teraz nowy dokument do innego indeksu, używając domyślnego mapowania dynamicznego.

PUT test_without_dynamic_template/test/1
{
"amountOfProducts": 23
}

GET test_without_dynamic_template/_mapping

{
"test_without_dynamic_template": {
"mappings": {
"test": {
"properties": {
"amountOfProducts": {
"type": "long"
}
}
}
}
}
}

Zgodnie z oczekiwaniami, amountOfProducts zostało zmapowane jako long, bo tak domyślnie działa Elasticsearch. Swoją drogą, źle nazwałem to pole, bo powinno nazywać się numberOfProducts, ale liczę, że mi wybaczysz :).

Szablon typu 2, używający match i unmatch, będzie potrzebny, jeśli w nazwie pól dokumentów, które ładujemy do indeksu mamy „zaszyty” typ danych, lub możemy po nazwie pola rozpoznać, jakiego typu powinna być jego wartość. Dla przykładu, zmapujemy wszystkie pola, których nazwy zaczynają się frazą „float” na typ float.
Wykonaj polecenie:

PUT orders_template_match_test
{
"mappings": {
"test": {
"dynamic_templates": [
{
"floats_from_string ": {
"match_mapping_type": "string",
"match": "float*",
"mapping": {
"type": "float"
}
}
}
]
}
}
}

Po raz kolejny powinniśmy dostać informację o udanym dodaniu szablonu.

Teraz dodaj do indeksu orders_template_match_test pole, którego nazwa rozpoczyna się od „float” oraz pobierz mapowanie. Zrobisz to poleceniem:

PUT orders_template_match_test/test/1
{
"float_amountOfProducts": "23.11"
}

GET orders_template_match_test/_mapping

{
"orders_template_match_test": {
"mappings": {
"test": {
"dynamic_templates": [
{
"floats_from_string ": {
"match": "float*",
"match_mapping_type": "string",
"mapping": {
"type": "float"
}
}
}
],
"properties": {
"float_amountOfProducts": {
"type": "float"
}
}
}
}
}
}

Jak widzisz, pomimo tego, że pole float_amountOfProducts zostało dodane jako JSON-owy String, to dzięki naszemu dynamicznemu szablonowi zostało zmapowane na typ float.

Jeśli potrzebujesz więcej informacji na temat dynamicznego mapowania, to rzuć okiem na dokumentację Elasticsearcha: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-mapping.html
A w razie pytań, pisz do nas; formularz kontaktowy czeka.