Dlaczego ElasticSearch to game-changer
Tradycyjne bazy danych SQL świetnie radzą sobie z dokładnymi zapytaniami, ale gdy użytkownik wpisuje „laptop dell szybki” w wyszukiwarkę sklepu, MySQL może się nie sprawdzić. ElasticSearch to rozwiązanie stworzone specjalnie do wyszukiwania full-text – analizuje tekst, rozpoznaje synonimy i zwraca najbardziej trafne wyniki w milisekundach.
Co się nauczysz
- Jak zainstalować i skonfigurować ElasticSearch
- Podstawowe koncepcje: indeksy, typy, dokumenty
- Tworzenie mappingów dla optymalnego wyszukiwania
- Implementacja zapytań full-text z oceną trafności
- Integracja z aplikacją Java przez REST API
- Analiza i debugowanie wyników wyszukiwania
Wymagania wstępne
Doświadczenie: 6-18 miesięcy z Java/aplikacjami webowymi
Wiedza: Podstawy HTTP, JSON, pojęcie REST API
Narzędzia: Java 8+, dowolne IDE, curl lub Postman
Czym jest ElasticSearch
ElasticSearch to rozproszona platforma wyszukiwania i analityki oparta na Apache Lucene. W przeciwieństwie do tradycyjnych baz danych, ElasticSearch został zaprojektowany od podstaw do wyszukiwania full-text.
Kluczowe różnice od tradycyjnych baz
Tradycyjna baza SQL | ElasticSearch |
---|---|
Tabela | Indeks |
Wiersz | Dokument |
Kolumna | Pole |
Dokładne dopasowania | Wyszukiwanie rozmyte |
Brak oceny trafności | Scoring algorytmy |
Instalacja i pierwszy kontakt
Szybka instalacja
# Pobierz ElasticSearch 2.4 (aktualna wersja w 2016) wget https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/zip/elasticsearch/2.4.0/elasticsearch-2.4.0.zip # Rozpakuj i uruchom unzip elasticsearch-2.4.0.zip cd elasticsearch-2.4.0 ./bin/elasticsearch
Pierwszy test – sprawdzenie klastra
curl -X GET "localhost:9200"
Powinieneś zobaczyć odpowiedź JSON z informacjami o klastrze:
{ "name" : "Blackheart", "cluster_name" : "elasticsearch", "version" : { "number" : "2.4.0", "build_hash" : "ce9f0c7394dee074091dd1bc4e9469251181fc55", "build_timestamp" : "2016-08-29T09:14:17Z", "build_snapshot" : false, "lucene_version" : "5.5.2" }, "tagline" : "You Know, for Search" }
Podstawowe operacje – CRUD
Tworzenie indeksu
# Tworzenie indeksu "sklep" curl -X PUT "localhost:9200/sklep"
Dodawanie dokumentów
# Dodanie produktu do indeksu curl -X POST "localhost:9200/sklep/produkty/1" -H 'Content-Type: application/json' -d' { "nazwa": "Laptop Dell Inspiron 15", "opis": "Szybki laptop dla studentów i profesjonalistów", "cena": 2499, "kategoria": "elektronika", "tagi": ["laptop", "dell", "praca", "nauka"] } '
Dodajmy więcej przykładowych produktów
# Smartfon curl -X POST "localhost:9200/sklep/produkty/2" -H 'Content-Type: application/json' -d' { "nazwa": "iPhone 7", "opis": "Najnowszy smartfon Apple z zaawansowanym aparatem", "cena": 3299, "kategoria": "elektronika", "tagi": ["smartphone", "apple", "telefon", "aparat"] } ' # Książka curl -X POST "localhost:9200/sklep/produkty/3" -H 'Content-Type: application/json' -d' { "nazwa": "Effective Java", "opis": "Przewodnik po najlepszych praktykach programowania w Javie", "cena": 89, "kategoria": "książki", "tagi": ["java", "programowanie", "książka", "bloch"] } '
Wyszukiwanie full-text w praktyce
Podstawowe zapytanie
# Wyszukiwanie produktów zawierających "laptop" curl -X GET "localhost:9200/sklep/_search" -H 'Content-Type: application/json' -d' { "query": { "match": { "nazwa": "laptop" } } } '
Zaawansowane zapytanie multi-field
# Wyszukiwanie w nazwie I opisie curl -X GET "localhost:9200/sklep/_search" -H 'Content-Type: application/json' -d' { "query": { "multi_match": { "query": "szybki laptop", "fields": ["nazwa^2", "opis"] } } } '
Filtrowanie wyników
# Laptop w przedziale cenowym 2000-3000 zł curl -X GET "localhost:9200/sklep/_search" -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": { "match": {"nazwa": "laptop"} }, "filter": { "range": { "cena": { "gte": 2000, "lte": 3000 } } } } } } '
Analiza tekstu – serce wyszukiwania
ElasticSearch automatycznie analizuje tekst przed indeksowaniem. Rozdziela słowa, usuwa znaki interpunkcyjne, sprowadza do małych liter.
Testowanie analizatora
# Sprawdź jak ElasticSearch analizuje tekst curl -X GET "localhost:9200/sklep/_analyze" -d ' { "text": "Szybki laptop Dell dla programistów!", "analyzer": "standard" } '
Mapping – definiowanie struktury
Mapping to schema która mówi ElasticSearch jak traktować poszczególne pola. Domyślnie ES automatycznie wykrywa typy, ale lepiej je zdefiniować jawnie.
Tworzenie mappingu
# Usuń stary indeks curl -X DELETE "localhost:9200/sklep" # Utwórz nowy z mappingiem curl -X PUT "localhost:9200/sklep" -H 'Content-Type: application/json' -d' { "mappings": { "produkty": { "properties": { "nazwa": { "type": "string", "analyzer": "standard" }, "opis": { "type": "string", "analyzer": "standard" }, "cena": { "type": "double" }, "kategoria": { "type": "string", "index": "not_analyzed" }, "tagi": { "type": "string", "index": "not_analyzed" } } } } } '
Integracja z aplikacją Java
Dodawanie zależności
org.elasticsearch elasticsearch 2.4.0 org.elasticsearch.client transport 2.4.0
Prosty klient Java
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.transport.TransportClient; import org.elasticsearch.common.transport.InetSocketTransportAddress; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import java.net.InetAddress; public class ElasticSearchClient { private TransportClient client; public void connect() throws Exception { client = TransportClient.builder().build() .addTransportAddress(new InetSocketTransportAddress( InetAddress.getByName("localhost"), 9300)); } public void searchProducts(String query) { SearchResponse response = client.prepareSearch("sklep") .setTypes("produkty") .setQuery(QueryBuilders.multiMatchQuery(query, "nazwa", "opis")) .get(); System.out.println("Znaleziono: " + response.getHits().getTotalHits()); for (SearchHit hit : response.getHits()) { System.out.println("Score: " + hit.getScore()); System.out.println("Źródło: " + hit.getSourceAsString()); System.out.println("---"); } } public void close() { client.close(); } }
Użycie klienta
public class Main { public static void main(String[] args) throws Exception { ElasticSearchClient esClient = new ElasticSearchClient(); esClient.connect(); // Wyszukaj laptopy esClient.searchProducts("szybki laptop"); esClient.close(); } }
Scoring – jak ElasticSearch ocenia trafność
ElasticSearch używa algorytmu TF-IDF (Term Frequency-Inverse Document Frequency) do oceny trafności wyników.
Wyjaśnienie score na przykładzie
# Wyjaśnienie dlaczego dany dokument ma taki score curl -X GET "localhost:9200/sklep/_search?explain=true" -d ' { "query": { "match": {"nazwa": "laptop"} } } '
Częste problemy początkujących
Monitoring i debugowanie
Sprawdzanie statusu klastra
# Status klastra curl -X GET "localhost:9200/_cluster/health" # Statystyki indeksu curl -X GET "localhost:9200/sklep/_stats" # Lista wszystkich indeksów curl -X GET "localhost:9200/_cat/indices?v"
Analiza performance
# Sprawdź jak długo trwało zapytanie curl -X GET "localhost:9200/sklep/_search?pretty" -d ' { "query": {"match_all": {}}, "profile": true } '
Optymalizacja wydajności
Bulk indexing dla większej wydajności
# Bulk API - dodawanie wielu dokumentów naraz curl -X POST "localhost:9200/sklep/produkty/_bulk" -H 'Content-Type: application/json' -d' {"index":{"_id":"4"}} {"nazwa":"MacBook Pro","opis":"Laptop dla kreatywnych profesjonalistów","cena":6999,"kategoria":"elektronika"} {"index":{"_id":"5"}} {"nazwa":"Logitech MX Master","opis":"Ergonomiczna mysz dla profesjonalistów","cena":299,"kategoria":"akcesoria"} '
MySQL używa LIKE '%tekst%’ co jest bardzo wolne na dużych tabelach. ElasticSearch indeksuje każde słowo osobno i używa algorytmów full-text search, co daje wyniki w milisekundach nawet dla milionów dokumentów. Dodatkowo ES oferuje scoring (ocenę trafności), which MySQL nie posiada.
„Match” dla wyszukiwania full-text – analizuje tekst i znajduje podobne wyniki. „Term” dla dokładnych dopasowań – ID, kategorie, daty. Przykład: match dla „laptop dell”, term dla category: „elektronika”.
ElasticSearch używa algorytmu TF-IDF. Wyższy score mają dokumenty gdzie szukane słowa występują często (TF), ale są rzadkie w całej kolekcji (IDF). Można to modyfikować przez boosting pól lub funkcje scoring.
Nie całkowicie. ES to search engine, nie transactional database. Brak mu ACID guarantees, complex joins, czy strict consistency. Używaj ES do wyszukiwania, ale trzymaj główne dane w PostgreSQL/MySQL.
Użyj analizatora z ASCII folding filter, który zamieni „ą” na „a”, „ł” na „l” itp. Alternatywnie stwórz custom analyzer z Polish tokenizer i stopwords. Dzięki temu „kraków” znajdzie „Kraków”.
Domyślnie max 10000 dokumentów per search, max 1000 pól per mapping, max 32766 znaków per term. Większość limitów można zwiększyć przez konfigurację, ale wpływa to na wydajność.
Użyj profile API dla szczegółowej analizy zapytań. Sprawdzaj slow log (domyślnie >0.5s dla query, >0.2s dla fetch). Monitoruj thread pool, heap usage i cache hit ratio przez /_nodes/stats.
Przydatne zasoby
🚀 Zadanie dla Ciebie
Stwórz indeks „blog” z artykułami zawierającymi pola: tytuł, treść, autor, data publikacji i tagi. Dodaj 5 przykładowych artykułów. Napisz zapytanie które znajdzie artykuły z ostatnich 30 dni zawierające słowo „java” w tytule lub treści, posortowane według daty publikacji malejąco.
Bonus: Dodaj facet search – pokaż liczbę artykułów per autor i per tag dla wyników wyszukiwania.
Masz doświadczenia z ElasticSearch? Jakie największe wyzwania napotkałeś przy implementacji wyszukiwania full-text? Podziel się w komentarzach!