ElasticSearch

Marcin
Lewandowski

ElasticSearch

Marcin
Lewandowski

Marcin Lewandowski

  • programista z ponad 15 letnim doświadczeniem,
  • tworzę i rozwijam systemy dla logistyki, 
  • prowadzę bloga czterytygodnie.pl

Uczestnicy

  • Imię i nazwisko
  • Jaki jest dla mnie cel szkolenia ?
  • Czy udało mi się wypełnić Kwestionariusz Oczekiwań przed szkoleniem ?
  • Gdzie i w jaki sposób będę wykorzystywać wiedzę ze szkolenia ?
  • Co aktualnie wiem o temacie szkolenia ?
  • Jakie wykorzystuję oprogramowanie związane z tematem szkolenia ?

Marcin
Lewandowski

Informacje organizacyjne

Marcin
Lewandowski

09:00 do 09:15 - Rozpoczęcie dnia, sprawy organizacyjne
09:15 do 10:45 - Blok szkoleniowy
10:45 do 11.00 - Przerwa kawowa ( 15 min. )
11:00 do 13:00 - Blok szkoleniowy
13:00 do 13:45 - Przerwa obiadowa ( 45 min. )
13:45 do 14:45 - Blok szkoleniowy
14:45 do 15:00 - Przerwa kawowa ( 15 min. )
15:00 do 16:00 - Blok szkoleniowy

16:00 do 17:00 - Praca indywidualna uczestników

Marcin
Lewandowski

Plan szkolenia

1. Introduction to Elasticsearch

2. Writing Search Queries

3. Performing Text Analysis

4. Defining Mappings

5. Expanding Your Searches

6. The Distributed Model

7. Manipulating Search Results

8. Performing Aggregations

9. Handling Data Relationships

10. Summary and Conclusion

Wprowadzenie do ElasticSearch

Wprowadzenie do ElasticSearch

  • Silnik wyszukiwania pełnotekstowego oparty o bazę dokumentów,
  • Wykorzystuje Apache Lucene,
  • REST API,
  • Dokumenty w formacie JSON,
  • Schemat indeksu budowany dynamicznie,
  • Wysoki poziom dostępności i skalowalności rozwiązania

Wprowadzenie do ElasticSearch

ELK

Elastic Stack

Elasticsearch

Logstash

Kibana

Beats

X Pack

Elastic cloud

Kibana

Wprowadzenie do ElasticSearch

  • Przeglądanie danych,
  • Wizualizacja danych
    • Dashboard,
    • Mapy,
    • Wykresy,
  • Raporty ( Lens )
  • SIEM ( dla działów bezpieczeństwa )
  • Zarządzanie i monitorowanie klastrem
    • Alerty,
    • Kontrola dostępu,
    • AL ( Machine Learning )

Logstash

Wprowadzenie do ElasticSearch

  • Narzędzie do zasilania danymi ElasticSearch,
  • Możliwe podpięcie rozwiązania pod wiele źródeł danych,
  • Transformacja danych wejściowych na określony format dzięki rozbudowanej liście filtrów,
  • Elastyczne rozwiązanie dzięki bogatej liście pluginów,
  • Skalowanie horyzontalne + kolejki

Logstash

Wprowadzenie do ElasticSearch

Logstash

Wprowadzenie do ElasticSearch

X-Pack

Wprowadzenie do ElasticSearch

Odpowiedzialny za bezpieczeństwo danych.

 

  • W podstawowej wersji mamy możliwość zabezpieczenia klastra w oparciu o użytkowników i role

X-Pack

Wprowadzenie do ElasticSearch

Alternatywy

 

  • Readonly REST - https://readonlyrest.com
  • Search Guard - https://search-guard.com
  • Reverse proxy - https://logz.io/blog/securing-elk-nginx/

Beats

Wprowadzenie do ElasticSearch

Lekka aplikacja napisana w Go służąca do przesyłania danych specjalny przygotowanych pod ElasticSearch.

 

Najważniejsze cech:

  • lekka aplikacja generująca znikome obciążenie na serwerze,
  • gotowe moduły dla najczęstszych rozwiązań znajdujących się na serwerach np. Nginx, Apache, Docker, RabbitMQ, Kafka, MySQL, MariaDB

Beats

Wprowadzenie do ElasticSearch

Dostępne Beats

  • Filebeat - dostarcza logi,
  • Metricbeat - dostarcza metryki,
  • Heartbeat - monitoruje uptime serwisów
  • Packetbeat - informacje o sieci (netflow)
  • Auditbeat - informacje związane z kwestiami bezpieczeństwa (Linux Audit Framework)
  • Winlogbeat - dostarcza informacje o zdarzeniach z systemu Windows

Architektura

ElasticSearch

Architektura

SERWER

( cluster, node, shard, replica )

DANE

( index, type, document )

Architektura

ElasticSearch

Architektura serwera

NODE (węzeł)

CLUSTER (klaster) 

SHARD / REPLICA

zbiór jednego lub więcej węzłów

pojedyncza instalacja serwera

Architektura

ElasticSearch

Architektura danych

do wersji 7

INDEX (indeks)

TYPE (typ) 

sposób grupowania dokumentów

kolekcja dokumentów

DOCUMENT (dokument) 

podstawowa jednostka indeksowania

DATABASE (baza danych)

TABLE (tabela)

RECORD (rekord / wiersz)

Architektura danych

po wersji 7.x

INDEX (indeks)

kolekcja dokumentów

DOCUMENT (dokument) 

podstawowa jednostka indeksowania

TABLE (tabela)

RECORD (rekord / wiersz)

Przygotowanie

środowiska

Linux

Instalacja serwera

Zalogowanie na administratora

sudo su

Import klucza PGP

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg

Instalacja pakietu apt-transport-https

sudo apt-get install apt-transport-https

Dodanie repozytorium z ElasticSearch

echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list

Aktualizacja listy pakietów i instalacja Elasticsearch

sudo apt-get update && sudo apt-get install elasticsearch

Linux

Instalacja serwera

Sprawdzamy czy serwis wystartował

systemctl status elasticsearch.service 

Startujemy serwis

systemctl start elasticsearch.service 

Linux

Instalacja serwera

Instalacja Kibany

sudo apt-get install kibana

Dodajemy serwis do autostartu

systemctl enable kibana

Kibana

Startujemy serwis

systemctl start kibana

Sprawdzamy czy mamy dostęp do Kibany pod adresem

localhost:5601

Linux

Instalacja serwera

Docker

Instalacja Docker

apt-get install docker.io

Instalacja Docker Compose

apt-get install docker-compose

Wymagania:

  • Docker, dostarcza kontenery,
  • Docker Compose, pozwala stworzyć plik z konfiguracją środowiska

Linux

Instalacja serwera

Wyszukujemy odpowiedni obraz na Docker Hub np.

Docker

Startujemy kontener

docker run -d -p 9201:9200 -p 5602:5601 nshou/elasticsearch-kibana

Sprawdzamy czy mamy dostęp do Kibany pod adresem

localhost:5602

Sprawdzamy czy kontener został uruchomiony

docker ps

Linux

Instalacja serwera

docker-compose.yml

Plik z konfiguracją środowiska testowego

version: '3'
 
services:
  es:
    image: nshou/elasticsearch-kibana
    ports:
      - "9201:9200"
      - "5602:5601"
  • wykorzystujemy obraz nshou/elasticsearch-kibana, który ma dodaną Kibanę,
  • przekierowujemy porty,
    • 9200 - ElasticSearch,
    • 5601 - Kibana

Linux

Instalacja serwera

docker-compose.yml

Uruchomienie środowiska

docker-compose up

Uruchomienie środowiska w tle

docker-compose up -d

Sprawdzenie uruchomionych kontenerów

docker ps

Zatrzymanie środowiska

ctrl + c

Zatrzymanie środowiska

docker-compose down

Indeks

Indeks

  • zbiór wszystkich dokumentów
  • REST API
  • domyślnie zawiera 5 shardów i 5 replik
  • odwrócony indeks

Indeks odwrócony

Zadanie


PUT shop

Dodajemy indeks

Odpowiedź

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

Kibana

CURL


curl -XPUT 'localhost:9200/shop'

Błędy przy dodawaniu indeksu

Indeks istnieje

{
  "error": {
    "root_cause": [
      {
        "type": "resource_already_exists_exception",
        "reason": "index [shop/BlQB1sKYTA6sV6KOpCp4cw] already exists",
        "index_uuid": "BlQB1sKYTA6sV6KOpCp4cw",
        "index": "shop"
      }
    ],
    "type": "resource_already_exists_exception",
    "reason": "index [shop/BlQB1sKYTA6sV6KOpCp4cw] already exists",
    "index_uuid": "BlQB1sKYTA6sV6KOpCp4cw",
    "index": "shop"
  },
  "status": 400
}

Składnia

{
  "error": "Incorrect HTTP method for uri [/shop] and method [POST], allowed: [GET, HEAD, DELETE, PUT]",
  "status": 405
}

Zadanie

Sprawdzamy listę indeksów

Kibana

GET _stats

Odpowiedź

{
  "_shards": {
    ...
  },
  "_all": {
    ...
  },
  "indices": {
    "shop": {
      ...
    }
  }
}

CURL

curl -XGET 'localhost:9200/_stats?pretty'

Zadanie

Usuwanie indeksu

Kibana

DELETE shop

Odpowiedź

{
  "acknowledged": true
}

CURL

curl -XDELETE 'localhost:9200/shop?pretty'

Lista indeksów

{
  "_shards": {
    ...
  },
  "_all": {
    ...
  },
  "indices": {}
}

Indeks nie istnieje

{
  "error": {
    "root_cause": [
      {
        "type": "index_not_found_exception",
        "reason": "no such index",
        "resource.type": "index_or_alias",
        "resource.id": "shop",
        "index_uuid": "_na_",
        "index": "shop"
      }
    ],
    "type": "index_not_found_exception",
    "reason": "no such index",
    "resource.type": "index_or_alias",
    "resource.id": "shop",
    "index_uuid": "_na_",
    "index": "shop"
  },
  "status": 404
}

Błędy przy usuwaniu indeksu

Typy

Typy

  • REST API
  • grupowanie dokumentów w ramach indeksu

ElasticSearch 5

ElasticSearch 6

wielu typów w ramach jednego indeksu

tylko jeden typ w indeksie

ElasticSearch 7

rezygnacja z typów

Zadanie

POST shop/products
{
}

Dodajemy nowy typ

Kibana

curl -XPOST 'localhost:9200/shop/products?pretty' -d '{}'

CURL

{
  "_index": "shop",
  "_type": "products",
  "_id": "9nCx-GYB4OAq-pq-3A1z",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

Odpowiedź

Mapping

Mapping

określa typy pól w dokumentach

Zadanie

GET shop/_mapping

Sprawdzamy mapping

Kibana

curl -XGET 'localhost:9200/shop/_mapping?pretty'

CURL

{
  "shop" : {
    "mappings" : {
    }
  }
}

Odpowiedź

Generowanie Mappingu

dwie metody generowania

automatyczne

przez użytkownika

mapping generowany na podstawie przesyłanych dokumentów

mapping określa użytkownik przesyłając schemat mappingu

Zadanie

Dodajemy dokument do indeksu

Kibana

POST shop
{
  "name": "Xiaomi Redmi 4X",
  "price": 1200,
  "category": "smartfon",
  "creation_date": "2018-01-30"
}

Odpowiedź

{
  "_index": "shop",
  "_type": "products",
  "_id": "BXDj-GYB4OAq-pq-SQ5Y",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

Zadanie

Dodajemy dokument do indeksu o określonym identyfikatorze

POST shop/1
{
  "name": "Xiaomi Redmi 4X",
  "price": 1200,
  "category": "smartfon",
  "creation_date": "2018-01-30"
}

Odpowiedź

{
  "_index": "shop",
  "_type": "products",
  "_id": "1",
  "_version": 1,
  "result": "created",
  "_shards": {
    "total": 2,
    "successful": 1,
    "failed": 0
  },
  "_seq_no": 0,
  "_primary_term": 1
}

Zadanie

Sprawdzamy automatycznie wygenerowany mapping

Kibana

GET shop/_mapping
"category": {
  "type": "text",
  "fields": {
    "keyword": {
      "type": "keyword",
      "ignore_above": 256
    }
  }
},
"creation_date": {
  "type": "date"
},
"name": {
  "type": "text",
  "fields": {
    "keyword": {
      "type": "keyword",
      "ignore_above": 256
    }
  }
},
"price": {
  "type": "long"
}
POST shop
{
  "name": "Xiaomi Redmi 4X",
  "price": 1200,
  "category": "smartfon",
  "creation_date": "2018-01-30"
}

Zadanie

Dodajemy produkt z typem float

Kibana

POST shop
{
  "name": "Xiaomi Redmi 4X",
  "price": 1199.99,
  "category": "smartfon",
  "creation_date": "2018-01-30"
}
GET shop/_mapping

Sprawdzamy czy zmienił się mapping

...
"price": {
  "type": "long"
}

Odpowiedź

Zadanie

Dodajemy produkt z nowym polem float

POST shop
{
  "name": "Xiaomi Redmi 4X",
  "price": 1500.00,
  "promotion": 1199.99,
  "category": "smartfon",
  "creation_date": "2018-01-30"
}
GET shop/_mapping

Sprawdzamy czy zmienił się mapping

...
"promotion": {
  "type": "float"
}

Odpowiedź

Ręczne definiowanie mappingu

  • lepsza kontrola nad typami
  • definiujemy w momencie tworzenia indeksu
PUT shop
{
    "mappings": {
        "properties": {
          ... mapping ...
        }
    }
}

Aktualizacja mappingu

  • aktualizacja istniejących pól jest niemożliwe
  • dodawanie definicji dla nowych pól jest dozwolone
  • dozwolone są operacje:
  • dodania parametrów do pola typu Object datatype
  • dodanie parametru ignore_above
  • dodanie multi-fields do istniejącego pola

Zadanie

Definiujemy mapping

Usuwamy indeks

DELETE shop
PUT shop
{
    "mappings": {
        "properties": {
          "category": {
            "type": "text"
          },
          "creation_date": {
            "type": "date"
          },
          "name": {
            "type": "text"
          },
          "price": {
            "type": "double"
          }
        }
    }
}

Tworzymy indeks

Sprawdzamy mapping

GET shop/_mapping
  "shop": {
    "mappings": {
      "properties": {
        "category": {
          "type": "text"
        },
        "creation_date": {
          "type": "date"
        },
        "name": {
          "type": "text"
        },
        "price": {
          "type": "double"
        }
      }
    }
  }

Obiekty zagnieżdżone

( nested )

"manufacturer": {
  "type": "nested",
  "properties": {
    ... lista pól ...
  }
}
  • pole przechowujące obiekt JSON lub tablicę obiektów
  • ukryte dokumenty
  • specyficzny sposób wyszukiwania

Definicja pola nested

Zadanie

Dodajemy pole typu nested do indeksu

PUT shop/_mapping
{
  "properties": {
    "manufacturer": {
      "properties": {
        "id": { 
          "type": "integer",
          "store": true,
          "index": true
        },
        "name": {
          "type": "text"
        }
      }
    }
  }
}

Aktualizujemy mapping

GET shop/_mapping
{
  "shop": {
    "mappings": {
        "properties": {
          "category": {
            "type": "text"
          },
          "creation_date": {
            "type": "date"
          },
          "manufacturer": {
            "properties": {
              "id": {
                "type": "integer",
                "store": true
              },
              "name": {
                "type": "text"
              }
            }
          },
          "name": {
            "type": "text"
          },
          "price": {
            "type": "double"
          }
        }
    }
  }
}

Sprawdzamy modyfikacje

Dokumenty

Dokumenty

Proces dodawania do indeksu

analyzer: {
    "char_filter": ["html_strip"],
    "tokenizer": "standard",
    "filter": ["lowercase"]
}

Testowanie poszczególnych elementów

GET /_analyze
{
  "text": ["Xiaomi <strong>Redmi</strong> 4X"]
}

Do testów poszczególnych elementów używamy endpoint-u _analyze 

Dokumenty

char_filter

GET /_analyze
{
  "char_filter": [
    "html_strip"
  ],
  "text": ["Xiaomi <strong>Redmi</strong> 4X"]
}

Dokumenty

Filtry znaków

char_filter - HTML strip character filter

PUT test_char_filter
{
    "settings": {
        "analysis": {
            "char_filter": {
                "accept_strong": {
                    "type": "html_strip",
                    "escaped_tags": [
                        "strong"
                    ]
                }
            }
        }
    }
}

Możliwa jest tworzenie własnych ustawień dla filtrów znaków.

GET /test_char_filter/_analyze
{
  "char_filter": [
    "accept_strong"
  ],
  "text": ["Xiaomi <strong>Redmi</strong> 4X"]
}

Filtry znaków

mapping - Mapping Character Filter

Filtr mapowania znaków pozwala przypisać określonym znakom odpowiedniki.

GET /_analyze
{
  "char_filter": [
    {
      "type": "mapping",
      "mappings": [
          "1 => jeden",
          "2 => dwa"
      ]
    }
  ],
  "text": ["Akceptowane cyfry to: 1 lub 2"]
}

Zamiast wpisywania całego mapping-u ręcznie, możliwe jest zdefiniowanie go w pliku i podanie ścieżki do pliku. Robimy to w parametrze: mappings_path

Filtry znaków

mapping - Mapping Character Filter

Filtr mapowania znaków pozwala przypisać określonym znakom odpowiedniki.

GET /_analyze
{
  "char_filter": [
    {
      "type": "mapping",
      "mappings": [
          "1 => jeden",
          "2 => dwa"
      ]
    }
  ],
  "text": ["Akceptowane cyfry to: 1 lub 2"]
}

Zamiast wpisywania całego mapping-u ręcznie, możliwe jest zdefiniowanie go w pliku i podanie ścieżki do pliku. Robimy to w parametrze: mappings_path

Filtry znaków

pattern_replace - Pattern Replace Character Filter

Filtr wyrażeń regularnych.

GET /_analyze
{
  "char_filter": [
    {
      "type": "pattern_replace",
      "pattern": "(\\d+)",
      "replacement": "$1 (cyfra)"
    }
  ],
  "text": ["Akceptowane cyfry to: 1 lub 2"]
}

Tokenizer

  • najważniejszy element analizera
  • odpowiedzialny za podział tekstu na tokeny
  • korzystamy tylko z wbudowanych tokenizerów

Tokenizer

Dostępne tokenizery

Tokenizer

standard

GET /_analyze
{
  "tokenizer": "standard",
  "text": "Xiaomi Redmi 4X"
}
{
  "tokens": [
    {
      "token": "Xiaomi",
      "start_offset": 0,
      "end_offset": 6,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "Redmi",
      "start_offset": 7,
      "end_offset": 12,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "4X",
      "start_offset": 13,
      "end_offset": 15,
      "type": "<ALPHANUM>",
      "position": 2
    }
  ]
}

Tokenizer standardowy

Efekt działania

Filtry tokenów

Filtry tokenów

GET /_analyze
{
  "tokenizer" : "standard",
  "filter" : ["lowercase"],
  "text" : "Xiaomi Redmi 4X"
}

lowercase

{
  "tokens" : [
    {
      "token" : "xiaomi",
      "start_offset" : 0,
      "end_offset" : 6,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "redmi",
      "start_offset" : 7,
      "end_offset" : 12,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "4x",
      "start_offset" : 13,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 2
    }
  ]
} 

Standard Tokenizer

Standard Token Filter, Lower Case Token Filter

Lower Case Tokenizer

Whitespace Tokenizer

Pattern Tokenizer

Lower Case Token Filter, Stop Token Filter

Standard Tokenizer

Lower Case Token Filter, ASCII Folding Token Filter, Stop Token Filter, Fingerprint Token Filter

Lower Case Tokenizer

dodaje obsługę stop words

zwraca dane wejściowe jako pojedynczy token

obsługują specyficznie każdy wspierany język

Analizery

Dostępne analizery

Analizery

Dostępne analizery

GET /_analyze
{
  "analyzer": "standard",
  "text": "Xiaomi Redmi 4X"
}

Analizer standardowy

Tworzymy własny analizer

{
  "settings": {
    "analysis": {
      "analyzer": {
        ...
      }
    }
  }
}

Struktura

Analizer

"my_analyzer": {
  "type": "custom",
  "char_filter": [
    "html_strip"
  ],
  "tokenizer": "standard",
  "filter": [
    "lowercase"
  ]
}
PUT shop/_settings
{
  "analysis": {
    "analyzer": {
      "my_analyzer": {
        "type": "custom",
        "char_filter": ["html_strip"],
        "tokenizer": "standard",
        "filter": ["lowercase"]
      }
    }
  }
}
POST /shop/_close
POST /shop/_open

Zamykamy indeks

Otwieramy indeks

Analizery

Zadanie

Testujemy stworzony analizer

GET /shop/_analyze
{
  "analyzer": "standard",
  "text": ["Xiaomi <strong>Redmi</strong> 4X"]
}
GET /shop/_analyze
{
  "analyzer": "my_analyzer",
  "text": ["Xiaomi <strong>Redmi</strong> 4X"]
}
{
  "tokens": [
    {
      "token": "xiaomi",
      "start_offset": 0,
      "end_offset": 6,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "redmi",
      "start_offset": 15,
      "end_offset": 29,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "4x",
      "start_offset": 30,
      "end_offset": 32,
      "type": "<ALPHANUM>",
      "position": 2
    }
  ]
}
{
  "tokens": [
    {
      "token": "xiaomi",
      "start_offset": 0,
      "end_offset": 6,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "strong",
      "start_offset": 8,
      "end_offset": 14,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "redmi",
      "start_offset": 15,
      "end_offset": 20,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "strong",
      "start_offset": 22,
      "end_offset": 28,
      "type": "<ALPHANUM>",
      "position": 3
    },
    {
      "token": "4x",
      "start_offset": 30,
      "end_offset": 32,
      "type": "<ALPHANUM>",
      "position": 4
    }
  ]
}

Wyszukiwanie

Rodzaje Wyszukiwania

Precyzyjne

Pełnotekstowe

brak analizy wyszukiwanej frazy

przed rozpoczęciem wyszukiwania fraza jest analizowana

Wyszukiwanie

Mapping

Dane testowe

Otwieramy poniższe linki i zapisujemy jako pliki Ctrl + S.

Nie zmieniamy nazwy plików ;)

Wyszukiwanie

Dane testowe

Wyszukiwanie

Dane testowe

Import indeksu

curl -XPUT 'http://127.0.0.1:9200' -H 'Content-Type: application/json' -d @movies_mapping_v1.txt

Import się nie powiedzie ze względu na pierwszą linię, którą trzeba usunąć PUT movies_v1.

Import danych

curl -XPOST 'http://127.0.0.1:9200/_bulk' -H 'Content-Type: application/json' --data-binary @movies_data-1.txt

Sprawdzamy czy dane znajdują się w indeksie

POST movies_v1/_search
{
  ...
  "hits": {
    "total": 20,
    "max_score": 1,
    "hits": [
      {
        "_index": "produkty",
        "_type": "product",
        "_id": "gpPNDGcBIwsD9fejqIT1",
        "_score": 1,
        "_source": {
          "utworzono": "2018-08-23",
          "nazwa": "iPad Pro",
          "url": "ipad-pro",
          "cena": 3500,
          "producent": {
            "name": "Apple",
            "id": 2
          },
          "kategoria": "Tablety",
          "opis": "Zamień komputer..."
        }
      },
    ]
  }
}

Wyszukiwanie

Rezultat wyszukiwania

Przeszukiwanie określonego pola ( Term )

Wyszukujemy frazę "zielona mila"

Wyszukujemy frazę "zielona"

GET movies_v1/_search
{
  "query": {
    "term": {
      "title": "zielona mila"
    }
  }
}
GET produkty/_search
{
  "query": {
    "term": {
      "title": "zielona"
    }
  }
}

Wyszukiwanie

Wyszukiwanie wielu fraz ( Terms )

Wyszukujemy frazę "zielona" lub "mila"

GET produkty/_search
{
  "query": {
    "terms": {
      "nazwa": ["zielona", "mila"]
    }
  }
}
"hits" : [
      {
        "_score" : 1.0,
        "_source" : {
          "title" : "Zielona mila"
        }
      },
      {
        "_score" : 1.0,
        "_source" : {
          "title" : "8 Mila"
        }
      }, 
...

Wyszukiwanie

Prefix

Wyszukiwanie tokenów zaczynających się na "zielon"

GET movies_v1/_search
{
  "query": {
    "prefix": {
      "nazwa": "zielon"
    }
  }
}

Wyszukiwanie

  • wyszukiwanie tokenów zaczynających się na określoną wartość,
  • zapytanie dość kosztowne dlatego przy częstym wykorzystywaniu na określonym polu warto dodać index_prefixes w mappingu

Ids

GET movies_v1/_search
{
  "query": {
    "ids": {
      "values": [6836, 5573, 1359]
    }
  }
}

Wyszukiwanie dokumentów po ich identyfikatorze

Wyszukiwanie

Range

GET movies_v1/_search
{
  "_source": ["title", "year"], 
  "query": {
    "range": {
      "year": {
        "gte": 2010,
        "lte": 2012
      }
    }
  }
}  

Wyszukiwanie dokumentów w ramach zdefiniowanego zakresu.

Wyszukiwanie

GET movies_v1/_search
{
  "_source": ["title", "rate"], 
  "query": {
    "range": {
      "rate": {
        "gte": 8
      }
    }
  }
}

Fuzzy Query

GET movies_v1/_search
{
  "_source": ["title"], 
  "query": {
    "fuzzy": {
      "title": {
        "value": "zielona"
      }
    }
  }
} 

Wykorzystuje odległość Levenshteina

  • ile kroków należy wykonać, aby przekształcić jedną wartość na drugą np. zmiana, dodanie, usunięcie znaku
  • Standardowe ustawienia:
    • 2 znaki - musi być zgodność 100%
    • 3 - 5 znaków - możliwy jeden krok,
    • > 5 znaków - możliwe 2 kroki

Wyszukiwanie

GET movies_v1/_search
{
  "_source": ["title"], 
  "query": {
    "term": {
      "title": {
        "value": "zielona"
      }
    }
  }
} 

7 wyników

16 wyników

Wildcard

GET movies_v1/_search
{
  "_source": ["title"], 
  "query": {
    "wildcard": {
      "title": {
        "value": "?arr*"
      }
    }
  }
} 

Zapytanie umożliwiające elastyczne wyszukiwanie poprzez zastępowanie znaków znakami specjalnymi.

 

Znaki specjalne możliwe do wykorzystania:

* - zastępuje wiele znaków

? - zastępuje jeden znak

Wyszukiwanie

Regexp

GET movies_v1/_search
{
  "_source": ["title"], 
  "query": {
    "regexp": {
      "title": {
        "value": "zie.*"
      }
    }
  }
} 

Wyszukiwanie oparte o wyrażenia regularne. Zapytanie bardzo zasobożerne i nie jest zalecane używanie tego na produkcji.

Wyszukiwanie

Wyszukiwanie pełnotekstowe

Wyszukiwanie w określonym polu

Wyszukiwanie frazy "zielona mila"

GET movies_v1/_search
{
  "_source": ["title"], 
  "query": {
    "match": {
      "title": {
        "query": "zielona mila"
      }
    }
  }
} 

Wyszukiwanie

Match

zmiana operatora na AND

GET movies_v1/_search
{
  "_source": ["title"], 
  "query": {
    "match": {
      "title": {
        "query": "zielona mila",
        "operator": "and"
      }
    }
  }
} 

Wyszukiwanie w określonej kolejności

Wyszukiwanie frazy "harry potter"

GET movies_v1/_search
{
  "_source": ["title"], 
  "query": {
    "match_phrase": {
      "title": {
        "query": "harry potter"
      }
    }
  }
} 

Wyszukiwanie

Match Phrase

dodanie operatora na sloop

GET movies_v1/_search
{
  "_source": ["title"], 
  "query": {
    "match_phrase": {
      "title": {
        "query": "harry sally",
        "slop": 1
      }
    }
  }
} 

Wyszukiwanie frazy

"harry Hendersons"

GET movies_v1/_search
{
  "_source": ["title", "title_org"], 
  "query": {
    "multi_match": {
      "fields": [
        "title",
        "title_org"
      ],
      "query": "harry Hendersons"
    }
  }
} 

Wyszukiwanie

Multi Match

Przeszukiwanie wielu pól jednocześnie.

GET movies_v1/_search
{
  "_source": ["title", "title_org"], 
  "query": {
    "multi_match": {
      "fields": [
        "title^3",
        "title_org"
      ],
      "query": "harry Hendersons"
    }
  }
} 

Boost na polu title

GET movies_v1/_search
{
  "_source": ["title", "title_org"], 
  "query": {
    "nested": {
      "path": "genres",
      "query": {
        "match": {
          "genres.name": "komedia"
        }
      }
    }
  }
} 

Wyszukiwanie

Nested

Wyszukiwania złożone

Bool

Struktura zapytania

GET produkty/_search
{
  "query": {
    "bool" : {
      "must" : {},
      "filter": {},
      "must_not" : {},
      "should" : {}
    }
  }
}

Zadanie

Wyszukujemy wszystkie produkty z kategorii "tablety"

bool - must

GET produkty/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "kategoria": "tablety"
        }
      }
    }
  }
}

Zadanie

Wyszukujemy wszystkie produkty z kategorii "tablety" w przedziale cenowym od 1000 do 1500

bool - must

GET produkty/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "kategoria": "tablety"
          }
        },
        {
          "range": {
            "cena": {
              "gte": 1000,
              "lte": 1500
            }
          }
        }
      ]
    }
  }
}

Zadanie

Wyszukujemy wszystkie produkty z kategorii "tablety" bez wpływu na "_score"

Bool - filter

GET produkty/_search
{
  "query": {
    "bool": {
      "filter": {
        "match": {
          "kategoria": "tablety"
        }
      }
    }
  }
}

Zadanie

Wyszukujemy wszystkie produkty z kategorii "tablety" bez producenta "Apple",

gdzie producent jest polem nested nie włączanym do dokumentu

Bool - must_not ( nested )

GET produkty/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "kategoria": "tablety"
        }
      },
      "must_not": {
        "nested": {
          "path": "producent",
          "query": {
            "match": {
              "producent.name": "Apple"
            }
          }
        }
      }
    }
  }
}

Zadanie

Wyszukujemy wszystkie produkty z kategorii "tablety" bez producenta "Apple"

Bool - must_not

GET produkty_producent/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "kategoria": "tablety"
        }
      },
      "must_not": {
        "match": {
          "producent.name": "Apple"
        }
      }
    }
  }
}

Zadanie

Wyszukujemy wszystkie produkty od producenta "Apple" lub "Samsung"

Bool - should

GET produkty_producent/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "producent.name": "Apple"
          }
        },
        {
          "match": {
            "producent.name": "Samsung"
          }
        }
      ]
    }
  }
}

Reindeksacja
danych

Reindeksacja danych

POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "movies_v1"
  },
  "dest": {
    "index": "movies_v2"
  }
} 

Zakładamy index movies_v2 i dodajemy "index_prefixes": { }  w polu title

{
  "task" : "a9qwXzi2RG6HwYAmSwp6Hg:47188"
}
GET _tasks/a9qwXzi2RG6HwYAmSwp6Hg:47188 
{
  "completed" : true,
  "task" : {
    "node" : "a9qwXzi2RG6HwYAmSwp6Hg",
    "id" : 47188,
    "type" : "transport",
    "action" : "indices:data/write/reindex",
    "status" : {
      "total" : 18221,
      "updated" : 0,
      "created" : 18221,
      "deleted" : 0,
      "batches" : 19,
      "version_conflicts" : 0,
      "noops" : 0,
      "retries" : {
        "bulk" : 0,
        "search" : 0
      },
      "throttled_millis" : 0,
      "requests_per_second" : -1.0,
      "throttled_until_millis" : 0
    }, 

Agregacja
danych

Agregacja
danych

GET movies_v1/_search
{
  "_source": ["title"], 
  "aggs": {
    "pogrypowane": {
      "terms": {
        "field": "type",
        "size": 10
      }
    }
  }
} 
GET movies_v1/_search
{
  "_source": ["title"], 
  "aggs": {
    "pogrypowane": {
      "terms": {
        "field": "type",
        "size": 10
      },
      "aggs": {
        ...
      }
    }
  }
} 

Budujemy wyszukiwarkę

Dziękuję za uwagę :)

Marcin
Lewandowski

ElasticSearch

By Marcin Lewandowski

ElasticSearch

ElasticSearch

  • 1,317