Elasticsearch - Wyszukiwanie pełnotekstowe w aplikacjach
Elasticsearch Wyszukiwanie pełnotekstowe
technologieElasticsearch: Wyszukiwanie pełnotekstowe w aplikacjach
Elasticsearch to rozproszony silnik wyszukiwania i analizy danych, który zrewolucjonizował sposób, w jaki aplikacje obsługują wyszukiwanie pełnotekstowe. Zbudowany na bazie Apache Lucene, oferuje niezwykłą szybkość, skalowalność i elastyczność. W tym artykule przedstawimy, jak działa Elasticsearch od środka, jak go wdrożyć i jak wykorzystać jego pełny potencjał w aplikacjach produkcyjnych.
Czym jest Elasticsearch?#
Elasticsearch to open-source'owa baza danych typu NoSQL, zaprojektowana specjalnie do wyszukiwania pełnotekstowego i analizy danych w czasie rzeczywistym. Kluczowe cechy to:
- Rozproszony silnik - automatyczny sharding i replikacja danych
- Wyszukiwanie pełnotekstowe - zaawansowane algorytmy rankingowe (BM25)
- Analiza w czasie rzeczywistym - agregacje i metryki na żywo
- RESTful API - komunikacja przez HTTP/JSON
- Skalowalność horyzontalna - łatwe dodawanie węzłów do klastra
- Bliskość czasu rzeczywistego - dokumenty dostępne w ciągu ~1 sekundy od indeksacji
Elasticsearch znajduje zastosowanie wszędzie tam, gdzie potrzebne jest szybkie i inteligentne wyszukiwanie - od sklepów internetowych, przez systemy logowania, po platformy analityczne.
Indeks odwrócony - serce Elasticsearch#
Kluczem do wydajności Elasticsearch jest indeks odwrócony (inverted index). W odróżnieniu od tradycyjnych baz danych, które przeszukują dokumenty sekwencyjnie, indeks odwrócony mapuje termy (słowa) na dokumenty, które je zawierają.
Jak działa indeks odwrócony?#
Załóżmy, że mamy trzy dokumenty:
| Dokument | Treść | |----------|-------| | Doc 1 | "Elasticsearch jest szybki" | | Doc 2 | "Szybkie wyszukiwanie danych" | | Doc 3 | "Elasticsearch obsługuje wyszukiwanie pełnotekstowe" |
Indeks odwrócony wygląda tak:
| Term | Dokumenty | |------|-----------| | elasticsearch | Doc 1, Doc 3 | | jest | Doc 1 | | szybki | Doc 1, Doc 2 | | wyszukiwanie | Doc 2, Doc 3 | | danych | Doc 2 | | obsługuje | Doc 3 | | pełnotekstowe | Doc 3 |
Dzięki temu wyszukanie frazy "wyszukiwanie elasticsearch" natychmiast zwraca dokumenty 2 i 3, bez konieczności skanowania całej bazy. To właśnie sprawia, że Elasticsearch potrafi przeszukiwać miliony dokumentów w milisekundach.
Uruchomienie Elasticsearch z Docker#
Najszybszy sposób na uruchomienie Elasticsearch to Docker. Poniżej konfiguracja z Docker Compose:
version: '3.8'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- xpack.security.enabled=false
- "ES_JAVA_OPTS=-Xms1g -Xmx1g"
ports:
- "9200:9200"
volumes:
- es_data:/usr/share/elasticsearch/data
networks:
- elastic
kibana:
image: docker.elastic.co/kibana/kibana:8.12.0
container_name: kibana
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
networks:
- elastic
volumes:
es_data:
driver: local
networks:
elastic:
driver: bridge
Po uruchomieniu (docker-compose up -d) Elasticsearch będzie dostępny na http://localhost:9200, a Kibana na http://localhost:5601.
# Sprawdź, czy klaster działa
curl -X GET "localhost:9200/_cluster/health?pretty"
Mapping i analizatory#
Czym jest mapping?#
Mapping to schemat danych w Elasticsearch - definiuje typy pól i sposób ich indeksowania. Jest analogiczny do schematu tabeli w relacyjnych bazach danych, ale bardziej elastyczny.
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "polish",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"description": {
"type": "text",
"analyzer": "polish"
},
"price": {
"type": "float"
},
"category": {
"type": "keyword"
},
"created_at": {
"type": "date"
},
"in_stock": {
"type": "boolean"
},
"tags": {
"type": "keyword"
}
}
}
}
Typy pól#
- text - analizowany tekst do wyszukiwania pełnotekstowego
- keyword - dokładne wartości (filtry, sortowanie, agregacje)
- integer/float/long - wartości numeryczne
- date - daty i znaczniki czasu
- boolean - wartości logiczne
- nested - zagnieżdżone obiekty z zachowaniem relacji
- geo_point - współrzędne geograficzne
Analizatory tekstu#
Analizatory przekształcają tekst przed zapisem do indeksu odwróconego. Proces analizy składa się z trzech etapów:
- Character filters - transformacja znaków (np. usunięcie HTML)
- Tokenizer - podział tekstu na tokeny (słowa)
- Token filters - modyfikacja tokenów (lowercasing, stemming, synonimiki)
PUT /products
{
"settings": {
"analysis": {
"analyzer": {
"custom_polish": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"polish_stop",
"polish_stem",
"synonym_filter"
]
}
},
"filter": {
"polish_stop": {
"type": "stop",
"stopwords": "_polish_"
},
"polish_stem": {
"type": "stemmer",
"language": "polish"
},
"synonym_filter": {
"type": "synonym",
"synonyms": [
"laptop,notebook,komputer przenośny",
"telefon,smartfon,komórka"
]
}
}
}
}
}
Dzięki analizatorom wyszukanie "komputerów" znajdzie również dokumenty zawierające "komputer", "komputery" czy "notebook".
Wyszukiwanie pełnotekstowe - zapytania#
Indeksowanie dokumentów#
Przed wyszukiwaniem musimy dodać dokumenty do indeksu:
POST /products/_doc/1
{
"name": "Laptop Dell XPS 15",
"description": "Wydajny laptop do pracy i rozrywki z procesorem Intel i7",
"price": 5999.99,
"category": "laptopy",
"tags": ["dell", "xps", "intel", "laptop"],
"in_stock": true,
"created_at": "2024-01-15"
}
POST /products/_doc/2
{
"name": "MacBook Pro 14 M3",
"description": "Profesjonalny notebook Apple z chipem M3 Pro do zadań kreatywnych",
"price": 8999.00,
"category": "laptopy",
"tags": ["apple", "macbook", "m3", "laptop"],
"in_stock": true,
"created_at": "2024-02-01"
}
Dla masowego indeksowania używaj Bulk API:
POST /products/_bulk
{"index": {"_id": "3"}}
{"name": "Samsung Galaxy S24", "description": "Flagowy smartfon z AI", "price": 4499.00, "category": "telefony", "tags": ["samsung", "galaxy", "smartfon"], "in_stock": true}
{"index": {"_id": "4"}}
{"name": "iPhone 15 Pro", "description": "Najnowszy telefon Apple z tytanową obudową", "price": 5499.00, "category": "telefony", "tags": ["apple", "iphone", "smartfon"], "in_stock": false}
Podstawowe zapytania#
Match Query - wyszukiwanie pełnotekstowe
GET /products/_search
{
"query": {
"match": {
"description": "wydajny laptop do pracy"
}
}
}
Multi-Match Query - wyszukiwanie w wielu polach
GET /products/_search
{
"query": {
"multi_match": {
"query": "laptop apple",
"fields": ["name^3", "description", "tags^2"],
"type": "best_fields",
"fuzziness": "AUTO"
}
}
}
Operator ^3 oznacza, że pole name ma trzykrotnie większą wagę w rankingu wyników.
Bool Query - łączenie warunków
GET /products/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"description": "laptop"
}
}
],
"filter": [
{
"range": {
"price": {
"gte": 3000,
"lte": 7000
}
}
},
{
"term": {
"in_stock": true
}
}
],
"should": [
{
"match": {
"tags": "intel"
}
}
],
"must_not": [
{
"term": {
"category": "akcesoria"
}
}
]
}
}
}
- must - warunki wymagane (wpływają na score)
- filter - warunki wymagane (bez wpływu na score, cache'owane)
- should - warunki opcjonalne (podnoszą score)
- must_not - warunki wykluczające
Fuzzy Search - wyszukiwanie z tolerancją błędów
GET /products/_search
{
"query": {
"match": {
"name": {
"query": "laptpo",
"fuzziness": "AUTO"
}
}
}
}
Fuzziness pozwala znaleźć wyniki mimo literówek - "laptpo" znajdzie "laptop".
Phrase Search i Highlighting#
GET /products/_search
{
"query": {
"match_phrase": {
"description": {
"query": "laptop do pracy",
"slop": 2
}
}
},
"highlight": {
"fields": {
"description": {
"pre_tags": ["<strong>"],
"post_tags": ["</strong>"],
"fragment_size": 150
}
}
}
}
Parametr slop określa dozwoloną liczbę pozycji między słowami frazy.
Agregacje - analiza danych#
Agregacje to potężny mechanizm Elasticsearch do analizy danych - odpowiednik GROUP BY w SQL, ale znacznie bardziej rozbudowany.
Bucket Aggregations#
GET /products/_search
{
"size": 0,
"aggs": {
"categories": {
"terms": {
"field": "category",
"size": 10
}
},
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "key": "budget", "to": 2000 },
{ "key": "mid-range", "from": 2000, "to": 5000 },
{ "key": "premium", "from": 5000, "to": 8000 },
{ "key": "luxury", "from": 8000 }
]
}
}
}
}
Metric Aggregations#
GET /products/_search
{
"size": 0,
"aggs": {
"avg_price": {
"avg": { "field": "price" }
},
"price_stats": {
"stats": { "field": "price" }
},
"category_avg_price": {
"terms": {
"field": "category"
},
"aggs": {
"avg_price": {
"avg": { "field": "price" }
}
}
}
}
}
Agregacje są niezwykle przydatne do tworzenia filtrów facetowych w sklepach internetowych - np. dynamiczne filtry cenowe, kategorie z liczbą produktów, popularne tagi.
ELK Stack - Elasticsearch, Logstash, Kibana#
ELK Stack (znany też jako Elastic Stack) to zestaw narzędzi do centralizacji logów i analizy danych:
Komponenty#
- Elasticsearch - przechowywanie i wyszukiwanie danych
- Logstash - zbieranie, transformacja i ładowanie logów
- Kibana - wizualizacja i eksploracja danych
- Beats - lekkie agenty do zbierania danych (Filebeat, Metricbeat, Heartbeat)
Konfiguracja Logstash#
input {
beats {
port => 5044
}
}
filter {
if [type] == "nginx" {
grok {
match => {
"message" => '%{IPORHOST:remote_addr} - %{USER:remote_user} \[%{HTTPDATE:time_local}\] "%{WORD:method} %{URIPATHPARAM:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status} %{NUMBER:body_bytes_sent}'
}
}
date {
match => ["time_local", "dd/MMM/yyyy:HH:mm:ss Z"]
}
geoip {
source => "remote_addr"
}
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "logs-nginx-%{+YYYY.MM.dd}"
}
}
Kibana - wizualizacja#
Kibana pozwala na tworzenie interaktywnych dashboardów z danymi z Elasticsearch. Najważniejsze funkcje:
- Discover - eksploracja surowych danych z filtrowaniem
- Visualize - tworzenie wykresów (pie, bar, line, heatmap)
- Dashboard - łączenie wizualizacji w interaktywne panele
- Dev Tools - konsola do wykonywania zapytań REST API
- Lens - intuicyjne tworzenie wizualizacji drag-and-drop
Przypadki użycia#
Wyszukiwanie w e-commerce#
Elasticsearch jest idealny do wbudowania zaawansowanego wyszukiwania w sklepy internetowe:
- Autocomplete - podpowiedzi podczas wpisywania
- Filtry facetowe - dynamiczne filtry ceny, kategorii, producenta
- Wyszukiwanie rozmyte - tolerancja na literówki
- Synonimiki - "telefon" = "smartfon" = "komórka"
- Personalizacja - boosting wyników na podstawie historii użytkownika
GET /products/_search
{
"suggest": {
"product-suggest": {
"prefix": "sam",
"completion": {
"field": "suggest",
"size": 5,
"fuzzy": {
"fuzziness": 1
}
}
}
}
}
Centralizacja logów#
Zbieranie logów z wielu mikroserwisów do jednego miejsca:
- Logi aplikacyjne (errory, warningi, debug)
- Logi dostępu (Nginx, Apache)
- Metryki systemowe (CPU, RAM, dysk)
- Śledzenie żądań (tracing)
Analityka biznesowa#
- Analiza trendów sprzedażowych w czasie
- Monitoring KPI w czasie rzeczywistym
- Raportowanie i dashboardy
- Analiza zachowań użytkowników
Wyszukiwanie geoprzestrzenne#
GET /stores/_search
{
"query": {
"geo_distance": {
"distance": "10km",
"location": {
"lat": 52.2297,
"lon": 21.0122
}
}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 52.2297,
"lon": 21.0122
},
"order": "asc"
}
}
]
}
Porównanie z alternatywami#
Elasticsearch vs Algolia#
| Cecha | Elasticsearch | Algolia | |-------|--------------|---------| | Hosting | Self-hosted / Cloud | SaaS | | Koszt | Open-source (infrastruktura) | Pay per search | | Konfiguracja | Zaawansowana | Minimalna | | Skalowalność | Nieograniczona | Automatyczna | | Customizacja | Pełna | Ograniczona | | Analityka | Agregacje, ELK | Wbudowana | | Latencja | ~10-50ms | ~1-20ms | | Przypadek użycia | Kompleksowe systemy | Szybki search-as-a-service |
Algolia jest lepsza dla prostych przypadków wyszukiwania w aplikacjach front-endowych, gdzie ważna jest natychmiastowa konfiguracja. Elasticsearch wygrywa przy złożonych wymaganiach, analizie danych i pełnej kontroli nad infrastrukturą.
Elasticsearch vs Meilisearch#
| Cecha | Elasticsearch | Meilisearch | |-------|--------------|-------------| | Język | Java | Rust | | Pamięć | Wymagająca (JVM) | Lekki | | Konfiguracja | Złożona | Prosta | | Funkcje | Kompletne | Podstawowe | | Agregacje | Zaawansowane | Podstawowe filtry | | Produkcyjność | Enterprise-ready | Dojrzewający | | Tolerancja błędów | Fuzzy + phonetic | Typo-tolerant z automatu |
Meilisearch to doskonały wybór dla mniejszych projektów, gdzie potrzebne jest szybkie wyszukiwanie z minimalną konfiguracją. Elasticsearch pozostaje standardem dla systemów enterprise i zaawansowanej analityki.
Optymalizacja wydajności#
1. Optymalizacja mappingu#
PUT /products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "5s"
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "polish"
},
"internal_id": {
"type": "keyword",
"index": false
}
}
}
}
- Ustaw
index: falsedla pól, po których nie wyszukujesz - Wyłącz
_sourcedla dużych dokumentów (jeśli nie potrzebujesz oryginału) - Używaj
keywordzamiasttextdla pól, które nie wymagają analizy
2. Optymalizacja zapytań#
GET /products/_search
{
"_source": ["name", "price", "category"],
"query": {
"bool": {
"filter": [
{ "term": { "category": "laptopy" } },
{ "range": { "price": { "gte": 3000 } } }
]
}
},
"size": 20,
"from": 0
}
- Używaj
filterzamiastmustdla warunków bez scoringu - filtrowane wyniki są cache'owane - Ogranicz pola w
_sourcedo wymaganych - Unikaj deep pagination (
from> 10000) - użyjsearch_after
3. Bulk operations#
POST /_bulk
{"index": {"_index": "products", "_id": "1"}}
{"name": "Produkt 1", "price": 100}
{"index": {"_index": "products", "_id": "2"}}
{"name": "Produkt 2", "price": 200}
Zawsze używaj Bulk API do masowych operacji - jest znacznie wydajniejsze niż indeksowanie po jednym dokumencie.
4. Monitorowanie klastra#
# Zdrowie klastra
curl -X GET "localhost:9200/_cluster/health?pretty"
# Statystyki indeksów
curl -X GET "localhost:9200/_cat/indices?v"
# Węzły i ich obciążenie
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,cpu,load_1m"
# Hot threads - diagnoza wolnych operacji
curl -X GET "localhost:9200/_nodes/hot_threads"
5. Index Lifecycle Management (ILM)#
Automatyczne zarządzanie cyklem życia indeksów - szczególnie przydatne dla logów:
PUT /_ilm/policy/logs_policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "7d"
}
}
},
"warm": {
"min_age": "30d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 }
}
},
"delete": {
"min_age": "90d",
"actions": {
"delete": {}
}
}
}
}
}
Checklista wdrożenia Elasticsearch#
Przed uruchomieniem na produkcji upewnij się, że:
- [ ] Mapping zdefiniowany (nie polegaj na dynamic mapping)
- [ ] Analizatory skonfigurowane dla obsługiwanych języków
- [ ] Liczba shardów dopasowana do rozmiaru danych
- [ ] Repliki włączone (min. 1 replika)
- [ ] JVM heap ustawiony na max 50% RAM (nie więcej niż 32GB)
- [ ] Snapshot i restore skonfigurowane
- [ ] Monitoring klastra włączony
- [ ] ILM dla indeksów logowych
- [ ] Security (autentykacja i autoryzacja) włączone
- [ ] Testy obciążeniowe przeprowadzone
Podsumowanie#
Elasticsearch to potężny silnik wyszukiwania, który:
- Zapewnia błyskawiczne wyszukiwanie - milisekundowe odpowiedzi na złożone zapytania
- Obsługuje wyszukiwanie pełnotekstowe - z analizą morfologiczną, synonimami i fuzzy search
- Skaluje się horyzontalnie - od jednego węzła do klastrów z setkami TB danych
- Dostarcza analitykę w czasie rzeczywistym - agregacje i dashboardy w Kibana
- Integruje się z ekosystemem - ELK stack, Beats, klienty dla każdego języka
Dla mniejszych projektów warto rozważyć Meilisearch lub Algolia, ale gdy potrzebujesz pełnej kontroli, zaawansowanych agregacji i skalowalności enterprise - Elasticsearch pozostaje niekwestionowanym liderem.
Potrzebujesz wsparcia?#
W MDS Software Solutions Group pomagamy w:
- Wdrożeniu Elasticsearch do wyszukiwania w aplikacjach
- Konfiguracji ELK Stack do centralizacji logów
- Optymalizacji wydajności zapytań i klastrów
- Migracji z innych rozwiązań wyszukiwania
- Budowie systemów analitycznych na bazie Elastic Stack
Skontaktuj się z nami, aby omówić Twój projekt!
Zespół ekspertów programistycznych specjalizujących się w nowoczesnych technologiach webowych.
