Kapsamlı Elasticsearch Geliştirici Rehberi
Elasticsearch’ün en çok kullanılan özelliklerini, best practice’leri ve gerçek dünyada kanıtlanmış pattern’leri kapsayan derin ve pratik bir referans kaynağı.
İçindekiler
- Temel Kavramlar ve Mimari
- Cluster, Node ve Shard’lar
- Index Yönetimi
- Mapping ve Veri Tipleri
- Metin Analizi (Analyzer, Tokenizer, Filter)
- CRUD ve Bulk İşlemleri
- Query DSL Derinlemesine
- Aggregation’lar
- Relevance (İlgililik) ve Skorlama
- Sayfalama (Pagination) Stratejileri
- Veri Modelleme Pattern’leri
- Index Lifecycle Management (ILM)
- Performans Best Practice’leri
- Search-as-You-Type ve Otomatik Tamamlama
- Geo Sorguları
- Güvenlik
- İzleme ve Sorun Giderme
- Yaygın Anti-Pattern’ler
- Client Kod Örnekleri
- Hızlı Referans (Cheat Sheet)
1. Temel Kavramlar ve Mimari
Elasticsearch, Apache Lucene üzerine inşa edilmiş, dağıtık (distributed), RESTful bir arama ve analiz motorudur. Veriyi JSON dokümanlar olarak saklar ve near-real-time (neredeyse gerçek zamanlı) arama sunar.
| Kavram | Açıklama |
|---|---|
| Document (Doküman) | Bir index içinde saklanan JSON nesnesi. İlişkisel veritabanındaki “satır"a benzer ama şema esnekliği vardır. |
| Index | Ortak bir mapping’e sahip dokümanlar koleksiyonu. Gevşek anlamda “tablo"ya benzer. |
| Mapping | Bir doküman tipinin şema tanımı: alan isimleri, veri tipleri, analyzer’lar. |
| Shard | Tek bir Lucene index’i. Her ES index’i, yatay ölçeklenme için bir veya daha fazla shard’a bölünür. |
| Replica | Yüksek erişilebilirlik ve okuma throughput’u için bir shard’ın kopyası. |
| Node | Çalışan tek bir Elasticsearch örneği. |
| Cluster | Tüm veri setinizi birlikte tutan node’lar topluluğu. |
| Segment | Bir shard içindeki değişmez (immutable) Lucene dosyası. Segment’ler zamanla merge edilir (birleştirilir). |
Neden Elasticsearch?
- İlgililik skorlaması ile tam metin arama (5.x’ten beri varsayılan olarak BM25)
- Near-real-time indexleme (refresh interval, varsayılan 1sn)
- Sharding ile yatay ölçeklenebilirlik
- Güçlü aggregation framework’ü (arama ile aynı veri üzerinde analitik)
- Dynamic mapping ile schema-on-write, ancak production için strict mapping önerilir
Doküman Yaşam Döngüsü
- Doküman bir coordinating node‘a gönderilir.
- Doğru primary shard‘a yönlendirilir (
_routingüzerinden, varsayılan olarak doküman_id‘sinin hash’i). - Bir bellek içi buffer ve translog‘a (dayanıklılık için) indexlenir.
- Her
refresh_interval‘da (varsayılan1s), buffer yeni bir segment’e yazılır ve aranabilir hale gelir. - Her
index.translog.durabilityflush aralığında, veri diske fsync edilir (flush). - Arka planda çalışan merge işlemi küçük segment’leri daha büyük olanlarla birleştirir ve silinmiş dokümanları temizler.
Kritik nokta: Bir doküman hemen indexlenir ancak bir sonraki refresh’e kadar aranabilir değildir. “Near real-time” ifadesinin anlamı budur.
2. Cluster, Node ve Shard’lar
Node Rolleri
node.roles: [ master, data, ingest, ml, remote_cluster_client ]
| Rol | Amaç |
|---|---|
master | Cluster state yönetimi, index oluşturma/silme, shard allocation kararları |
data (data_hot, data_warm, data_cold, data_frozen) | Veriyi saklar, CRUD/arama/aggregation işlemlerini çalıştırır |
ingest | Ingest pipeline’larını çalıştırır (indexlemeden önce ön işleme) |
ml | Machine learning işleri |
coordinating only | Hiçbir rol atanmamış; sadece istekleri yönlendirir (özel olarak nadiren gereklidir) |
remote_cluster_client | Cross-cluster search/replication’ı etkinleştirir |
Best practice: Production cluster’larında (>= 6-10 node), master-eligible node’ları data node’lardan ayrı tutun (3 adet, tek sayı, küçük instance, veri tutmayan) — split-brain’i ve GC baskısının cluster state’i etkilemesini önlemek için.
Shard Boyutlandırma — #1 Operasyonel Karar
- Önerilen shard boyutu: shard başına 10–50 GB (log/time-series için 50GB’a kadar uygun; arama ağırlıklı kullanım durumları için 10-30GB’a yakın tutun).
- Kural: shard sayısı ≈ toplam veri boyutu / hedef shard boyutu.
- Oversharding‘den (binlerce küçük shard) kaçının — her shard’ın overhead’i vardır (dosya handle’ları, bellek, cluster state boyutu).
- Undersharding‘den kaçının — shard sayısının ötesinde ölçeklenemezsiniz ve çok büyük shard’lar yavaş recover/relocate olur.
- Primary shard sayısı index oluşturulurken sabitlenir (reindex /
_split/_shrinkolmadan değiştirilemez). - Replica sayısı herhangi bir zamanda değiştirilebilir (
PUT /index/_settings).
Kaba formül:
number_of_shards = ceil(beklenen_index_boyutu_GB / 30GB)
Split Brain ve Quorum
discovery.seed_hostsvecluster.initial_master_nodesmaster discovery’yi yapılandırır.- Elasticsearch quorum-tabanlı bir oylama algoritması kullanır (7.x’ten beri
minimum_master_nodes‘u manuel ayarlamaya gerek yok). - Her zaman tek sayıda master-eligible node çalıştırın (3 veya 5).
3. Index Yönetimi
Açık Ayarlarla Index Oluşturma
PUT /products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "5s",
"analysis": {
"analyzer": {
"custom_english": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "english_stop", "english_stemmer"]
}
},
"filter": {
"english_stop": { "type": "stop", "stopwords": "_english_" },
"english_stemmer": { "type": "stemmer", "language": "english" }
}
}
},
"mappings": {
"properties": {
"name": { "type": "text", "analyzer": "custom_english" },
"sku": { "type": "keyword" },
"price": { "type": "scaled_float", "scaling_factor": 100 },
"created_at": { "type": "date" }
}
}
}
Index Template’leri (Composable, 7.8+‘dan beri)
Best practice: production index’lerinin asla sadece dynamic mapping’e güvenmemesi. Tutarlılık için index template’leri kullanın.
PUT _index_template/logs_template
{
"index_patterns": ["logs-*"],
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.lifecycle.name": "logs_policy"
},
"mappings": {
"dynamic": "strict",
"properties": {
"@timestamp": { "type": "date" },
"level": { "type": "keyword" },
"message": { "type": "text" }
}
}
},
"composed_of": ["component_mappings", "component_settings"],
"priority": 200
}
Alias’lar — Kesintisiz (Zero-Downtime) Reindexing’in Temeli
Uygulamaları asla doğrudan fiziksel bir index’e işaret etmeyin. Her zaman bir alias kullanın.
POST /_aliases
{
"actions": [
{ "add": { "index": "products_v2", "alias": "products" } },
{ "remove": { "index": "products_v1", "alias": "products" } }
]
}
Reindex pattern’i (kesintisiz):
- Yeni mapping ile
products_v2oluşturun. products_v1→products_v2arasındaPOST _reindexçalıştırın.productsalias’ını tek bir_aliasesçağrısında atomik olarak değiştirin (eskiyi kaldır, yeniyi ekle).- Doğruladıktan sonra
products_v1‘i silin.
POST _reindex
{
"source": { "index": "products_v1" },
"dest": { "index": "products_v2" }
}
Dönüşümlü (Transform) Reindex
POST _reindex
{
"source": { "index": "products_v1" },
"dest": { "index": "products_v2" },
"script": {
"source": "ctx._source.full_name = ctx._source.first_name + ' ' + ctx._source.last_name"
}
}
Index’leri Kapatma / Açma / Dondurma
POST /index/_close— okuma/yazmayı durdurur, heap/dosya handle’larını serbest bırakır, veriyi diskte tutar.POST /index/_open— tekrar açar.- Frozen tier (aranabilir snapshot’lar), deprecated
freezeAPI’nin modern yerine geçenidir — nadiren sorgulanan, maliyet-optimize edilmiş veri için kullanılır.
4. Mapping ve Veri Tipleri
Temel Alan Tipleri
| Tip | Kullanım alanı |
|---|---|
text | Tam metin arama, analiz edilmiş, tokenize edilmiş |
keyword | Tam eşleşme, sıralama, aggregation, filtreleme |
long/integer/short/byte | Tam sayılar |
double/float/half_float/scaled_float | Ondalık sayılar (para birimi için scaled_float kullanın) |
date | ISO8601 veya özel formatlar |
boolean | true/false |
object | JSON nesnesi (dahili olarak düzleştirilir — array ilişkilerini kaybeder) |
nested | Alanlar arasındaki ilişkileri koruyan nesne array’i |
geo_point | Enlem/boylam koordinatları |
geo_shape | Karmaşık geometriler |
ip | IPv4/IPv6 |
completion | Otomatik tamamlama önerici (suggester) |
dense_vector | Vektör arama / kNN / embedding’ler |
flattened | Tüm bir nesneyi açık alt-mapping olmadan tek bir alan olarak indexler (rastgele JSON için iyi) |
join | Tek bir index içinde parent/child ilişkileri |
alias | Başka bir alan ismine işaret eder |
constant_keyword | Index’teki her doküman için aynı değer |
rank_features / rank_feature | Sayısal özelliklere dayalı skorlama boost’u |
search_as_you_type | Otomatik tamamlama tarzı prefix sorguları için optimize edilmiş |
text vs keyword — En Önemli Ayrım
{
"properties": {
"title": {
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 }
}
}
}
}
title→ tam metin arama (matchsorgusu, relevance skorlaması, stemming, stopword temizleme).title.keyword→ tam eşleşme, sıralama, terms aggregation,termsorgusu.
Kural: bir string alanında sıralama veya aggregation yapmanız gerekiyorsa, mutlaka bir keyword alt-alanı olmalı veya doğrudan keyword olarak map edilmelidir.
object vs nested
// object - ilişkili alan array'leri için YANLIŞ
{ "user": [ { "first": "John", "last": "Smith" }, { "first": "Alice", "last": "Doe" } ] }
Dahili olarak user.first: [John, Alice], user.last: [Smith, Doe] şeklinde düzleşir — first: John AND last: Doe sorgusu yanlışlıkla eşleşir! Nesne sınırlarını korumak için nested kullanın:
{
"properties": {
"user": { "type": "nested" }
}
}
nested sorgusu ile sorgulama:
{
"query": {
"nested": {
"path": "user",
"query": {
"bool": {
"must": [
{ "match": { "user.first": "John" } },
{ "match": { "user.last": "Doe" } }
]
}
}
}
}
}
Trade-off: her nested nesne, gizli ayrı bir Lucene dokümanı olarak indexlenir — daha fazla nested nesne = daha fazla overhead. Derin nested yapılardan veya çok büyük nested array’lerden (parent başına binlerce nested doküman) kaçının.
Dynamic Mapping Kontrolü
{
"mappings": {
"dynamic": "strict", // "true" | "false" | "strict"
"properties": { ... }
}
}
true(varsayılan): yeni alanlar otomatik olarak mapping’e eklenir.false: yeni alanlar yok sayılır (indexlenmez, aranamaz, ama_sourceiçinde saklanır).strict: yeni alanlar index sırasında hata verir. İyi bilinen şemalara sahip production API’leri için idealdir.
Multi-fields Pattern’i (tek bir alanda arama + sıralama + aggregation + otomatik tamamlama)
{
"properties": {
"title": {
"type": "text",
"analyzer": "standard",
"fields": {
"keyword": { "type": "keyword" },
"english": { "type": "text", "analyzer": "english" },
"autocomplete": { "type": "search_as_you_type" }
}
}
}
}
Runtime Field’lar (sorgu zamanında alan tanımlama, reindex gerektirmez)
GET /products/_search
{
"runtime_mappings": {
"price_with_tax": {
"type": "double",
"script": "emit(doc['price'].value * 1.2)"
}
},
"query": { "range": { "price_with_tax": { "gte": 100 } } }
}
Deneme yapmak veya nadiren kullanılan alanlar için iyidir; sorgu zamanında CPU maliyeti vardır (indexleme zamanında disk maliyetine karşı) — yüksek QPS’li hot path’lerde kullanmayın.
5. Metin Analizi
Analyzer Anatomisi
Analyzer = Karakter Filtreleri (0..n) → Tokenizer (1) → Token Filtreleri (0..n)
- Karakter filtreleri: tokenize etmeden önce ham metni değiştirir (
html_strip,mapping,pattern_replace). - Tokenizer: metni token’lara böler (
standard,whitespace,ngram,edge_ngram,keyword,pattern). - Token filtreleri: token’ları değiştirir (
lowercase,stop,stemmer,synonym,asciifolding,shingle).
Analyzer’ları Test Etme (deploy etmeden önce her zaman yapın)
POST /_analyze
{
"analyzer": "standard",
"text": "The QUICK Brown-Foxes jumped!"
}
Otomatik Tamamlama için Özel Analyzer (edge n-gram)
PUT /articles
{
"settings": {
"analysis": {
"analyzer": {
"autocomplete_analyzer": {
"type": "custom",
"tokenizer": "autocomplete_tokenizer",
"filter": ["lowercase"]
},
"autocomplete_search_analyzer": {
"type": "custom",
"tokenizer": "lowercase"
}
},
"tokenizer": {
"autocomplete_tokenizer": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 10,
"token_chars": ["letter", "digit"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "autocomplete_analyzer",
"search_analyzer": "autocomplete_search_analyzer"
}
}
}
}
Kritik kural: sorgu zamanında (edge_ngram olmadan) farklı bir search_analyzer kullanın, aksi takdirde arama sorgusunun kendisi de n-gram’lanır ve ilgisiz eşleşmeler üretir.
Eş Anlamlılar (Synonyms)
"filter": {
"synonym_filter": {
"type": "synonym",
"synonyms": [
"laptop, notebook",
"tv, television => television"
]
}
}
Çok kelimeli eş anlamlılar için doğru phrase sorgu desteğiyle synonym_graph + multiplexer kullanın.
Dile Özgü Analiz
Elasticsearch, her dil için stemming ve stopword’leri doğru şekilde işleyen hazır dil analyzer’ları (english, turkish, french, vb.) sunar. Manuel özel stemmer oluşturmak yerine bunları tercih edin.
{ "type": "text", "analyzer": "turkish" }
Not: Türkçe metinler için
turkishanalyzer’ı kullanmak, Türkçe’ye özgü ekleri (çekim ekleri) ve büyük/küçük harf kurallarını (örn. “İ”/“i” ve “I”/“ı” ayrımı) doğru şekilde ele alır. Standartstandardanalyzer’ı Türkçe için yetersiz kalabilir.
6. CRUD ve Bulk İşlemleri
Tekil Doküman İşlemleri
PUT /products/_doc/1
{ "name": "Wireless Mouse", "price": 25.99 }
GET /products/_doc/1
POST /products/_update/1
{ "doc": { "price": 22.99 } }
DELETE /products/_doc/1
Optimistic Concurrency Control (İyimser Eşzamanlılık Kontrolü)
PUT /products/_doc/1?if_seq_no=10&if_primary_term=1
Eşzamanlı yazma senaryolarında kayıp güncellemeleri önlemek için _seq_no + _primary_term kullanın (bu amaç için eski version yaklaşımının yerini alan modern yöntem).
Bulk API — Birden Fazla Doküman için Her Zaman Bunu Kullanın
POST /_bulk
{ "index": { "_index": "products", "_id": "1" } }
{ "name": "Mouse", "price": 25.99 }
{ "update": { "_index": "products", "_id": "2" } }
{ "doc": { "price": 19.99 } }
{ "delete": { "_index": "products", "_id": "3" } }
Bulk indexleme için best practice’ler:
- Batch boyutu: bulk istek başına 5–15 MB, veya 1.000–5.000 doküman — veriniz için benchmark yapın.
- Birden fazla paralel bulk thread’i kullanın (ancak
thread_pool.write.queue_size‘a dikkat edin). - Büyük ilk bulk yüklemesi sırasında replica’ları devre dışı bırakın, sonra yeniden etkinleştirin:
PUT /index/_settings { "number_of_replicas": 0 } - Bulk yükleme sırasında
refresh_interval‘ı-1yapın, sonra geri yükleyin:PUT /index/_settings { "index": { "refresh_interval": "-1" } } - Sıra önemli değilse otomatik
_idüretimini kullanın (_id‘yi atlayın) — indexleme throughput’u için version lookup overhead’ini önler. 429(es_rejected_execution_exception) durumunda exponential backoff ile tekrar deneyin.
Update by Query / Delete by Query
POST /products/_update_by_query
{
"query": { "term": { "category": "electronics" } },
"script": { "source": "ctx._source.price *= 1.1" }
}
POST /products/_delete_by_query
{
"query": { "range": { "created_at": { "lt": "now-1y" } } }
}
Bunlar kaynak açısından yoğun işlemlerdir (aslında arka planda reindex gibi çalışır) — requests_per_second ile throttle edin ve paralellik için slices kullanmayı düşünün.
7. Query DSL Derinlemesine
Query Context vs Filter Context
- Query context: “Bu doküman ne kadar iyi eşleşiyor?” →
_scorehesaplar. - Filter context: “Bu doküman eşleşiyor mu?” → evet/hayır, cache’lenir, skorlama overhead’i yoktur.
Skorlama gerektirmeyen koşulları her zaman must yerine filter içine koyun.
GET /products/_search
{
"query": {
"bool": {
"must": [
{ "match": { "description": "wireless mouse" } }
],
"filter": [
{ "term": { "category": "electronics" } },
{ "range": { "price": { "gte": 10, "lte": 100 } } }
],
"should": [
{ "match": { "brand": "logitech" } }
],
"must_not": [
{ "term": { "discontinued": true } }
]
}
}
}
| Clause | Skorlama | Amaç |
|---|---|---|
must | evet | AND, skora katkıda bulunur |
filter | hayır (cache’lenir) | AND, skora katkı yok — hızlı |
should | evet | OR, eşleşirse skoru artırır (veya must yoksa OR gibi davranır) |
must_not | hayır | NOT, cache’lenir |
Tam Metin Sorguları
// match - standart analiz edilmiş tam metin sorgusu
{ "match": { "title": "quick brown fox" } }
// match_phrase - terimlerin tam sırası
{ "match_phrase": { "title": "quick brown fox" } }
// match_phrase_prefix - phrase + son terimde prefix (otomatik tamamlama)
{ "match_phrase_prefix": { "title": "quick bro" } }
// multi_match - boost ile birden fazla alanda arama
{
"multi_match": {
"query": "wireless mouse",
"fields": ["title^3", "description", "tags^2"],
"type": "best_fields"
}
}
// query_string / simple_query_string - kullanıcı tarafından yazılan sorgu sözdizimi (Google benzeri)
{ "simple_query_string": { "query": "wireless +mouse -wired", "fields": ["title", "description"] } }
multi_match tipleri:
| Tip | Davranış |
|---|---|
best_fields (varsayılan) | Tek en iyi eşleşen alanın skorunu kullanır |
most_fields | Eşleşen tüm alanların skorlarını birleştirir — aynı metnin farklı alanlarda farklı analiz edildiği durumlarda iyidir |
cross_fields | Birden fazla alanı tek büyük bir alan gibi ele alır (first_name/last_name arasında isim araması için iyi) |
phrase | Her alanda match_phrase çalıştırır |
bool_prefix | match_bool_prefix çalıştırır — search-as-you-type için iyi |
Term-Level Sorguları (tam değerler, analiz edilmemiş)
{ "term": { "status.keyword": "active" } }
{ "terms": { "status.keyword": ["active", "pending"] } }
{ "range": { "price": { "gte": 10, "lt": 100 } } }
{ "exists": { "field": "email" } }
{ "prefix": { "sku": "AB-" } }
{ "wildcard": { "sku": "AB-*" } }
{ "fuzzy": { "name": { "value": "quikc", "fuzziness": "AUTO" } } }
{ "ids": { "values": ["1", "2", "3"] } }
textalanında aslatermsorgusu çalıştırmayın — alan indexleme zamanında analiz edilir/küçük harfe çevrilir, bu yüzden büyük/küçük harf duyarlı tam terimler eşleşmez..keywordalt-alanlarını kullanın.
Bileşik (Compound) Sorgular
// boosting - "negative" ile eşleşen dokümanların skorunu düşür ama dışlama
{
"boosting": {
"positive": { "match": { "title": "apple" } },
"negative": { "match": { "title": "fruit" } },
"negative_boost": 0.2
}
}
// constant_score - bir filtreyi sar, sabit skor ver (skorlamayı tamamen atlamak için kullanılır)
{ "constant_score": { "filter": { "term": { "status": "active" } }, "boost": 1.2 } }
// dis_max - clause'lar arasında toplam değil maksimum skoru al ("farklı alan anlamları arasında OR" için en iyisi)
{
"dis_max": {
"queries": [
{ "match": { "title": "star wars" } },
{ "match": { "description": "star wars" } }
],
"tie_breaker": 0.3
}
}
Sıralama (Sorting)
{
"sort": [
{ "price": "asc" },
{ "_score": "desc" },
{ "created_at": { "order": "desc", "missing": "_last" } }
]
}
text alanlarında doğrudan sıralamaya izin verilmez — .keyword veya fielddata: true kullanın (fielddata’dan kaçının, bellek açısından pahalıdır).
Highlighting (Vurgulama)
{
"query": { "match": { "description": "wireless mouse" } },
"highlight": {
"fields": { "description": { "fragment_size": 150, "number_of_fragments": 3 } }
}
}
8. Aggregation’lar
Metric Aggregation’lar
{
"aggs": {
"avg_price": { "avg": { "field": "price" } },
"price_stats": { "stats": { "field": "price" } },
"unique_categories": { "cardinality": { "field": "category.keyword" } },
"percentiles_price": { "percentiles": { "field": "price", "percents": [50, 95, 99] } }
}
}
cardinality yaklaşık bir değerdir (HyperLogLog++) — hassasiyeti precision_threshold ile ayarlayın (bellek trade-off’u).
Bucket Aggregation’lar
{
"aggs": {
"by_category": {
"terms": { "field": "category.keyword", "size": 10 },
"aggs": {
"avg_price": { "avg": { "field": "price" } }
}
},
"price_ranges": {
"range": {
"field": "price",
"ranges": [
{ "to": 50 },
{ "from": 50, "to": 200 },
{ "from": 200 }
]
}
},
"sales_over_time": {
"date_histogram": {
"field": "created_at",
"calendar_interval": "month"
}
}
}
}
Pipeline Aggregation’lar (diğer aggregation’ların sonuçları üzerinde aggregation)
{
"aggs": {
"sales_per_month": {
"date_histogram": { "field": "date", "calendar_interval": "month" },
"aggs": { "total_sales": { "sum": { "field": "amount" } } }
},
"max_monthly_sales": {
"max_bucket": { "buckets_path": "sales_per_month>total_sales" }
},
"cumulative_sales": {
"cumulative_sum": { "buckets_path": "sales_per_month>total_sales" }
}
}
}
terms Aggregation Doğruluğu — Önemli Tuzak
Sharded bir index üzerinde terms aggregation’ı varsayılan olarak yaklaşıktır (her shard kendi top N’ini döner, sonra coordinator birleştirir). Yüksek kardinaliteli alanlarda doğru sonuçlar için:
{
"terms": {
"field": "category.keyword",
"size": 10,
"shard_size": 100
}
}
doc_count hatasını azaltmak (ortadan kaldırmak değil) için shard_size‘ı size‘dan çok daha büyük yapın. Yanıttaki sum_other_doc_count ve doc_count_error_upper_bound alanlarını kontrol edin.
filter/filters ve composite Aggregation’lar
// composite - tüm bucket kombinasyonları arasında sayfalama (tüm agg verisini export etmek için harika)
{
"aggs": {
"my_buckets": {
"composite": {
"size": 1000,
"sources": [
{ "category": { "terms": { "field": "category.keyword" } } },
{ "month": { "date_histogram": { "field": "date", "calendar_interval": "month" } } }
]
}
}
}
}
Tüm kombinasyonları numaralandırmanız gerektiğinde derinlemesine nested terms agg’ları yerine composite kullanın (normal terms‘ten farklı olarak sayfalama için after destekler).
Sadece Aggregation İçin search.size: 0
{ "size": 0, "aggs": { ... } }
Sadece aggregation sonuçlarını istediğinizde her zaman size: 0 ayarlayın — gereksiz hit çekme/serileştirmeyi önler.
9. Relevance (İlgililik) ve Skorlama
BM25 (ES 5.0’dan beri varsayılan benzerlik algoritması)
Skor kabaca şu fonksiyona dayanır:
- Term frequency (TF) — terimin alanda ne sıklıkla geçtiği (klasik TF-IDF’in aksine doygunlaşır/saturates).
- Inverse document frequency (IDF) — daha nadir terimler daha yüksek skor alır.
- Field length norm — aynı term frequency için daha kısa alanlar daha yüksek skor alır.
PUT /products
{
"settings": {
"index": {
"similarity": {
"custom_bm25": {
"type": "BM25",
"b": 0.75,
"k1": 1.2
}
}
}
},
"mappings": {
"properties": {
"description": { "type": "text", "similarity": "custom_bm25" }
}
}
}
function_score — İlgililiği İş Mantığıyla Birleştirme
{
"query": {
"function_score": {
"query": { "match": { "title": "laptop" } },
"functions": [
{ "filter": { "term": { "featured": true } }, "weight": 2 },
{ "field_value_factor": { "field": "sales_count", "modifier": "log1p", "factor": 0.1 } },
{ "gauss": { "created_at": { "origin": "now", "scale": "10d", "decay": 0.5 } } }
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
Yaygın kullanım: tazelik (freshness) azalması (gauss/exp/linear decay fonksiyonları), popülerlik boost’u, manuel sabitleme (pinning).
Rescoring (pahalı skorlamayı sadece en üstteki N sonuca uygulama)
{
"query": { "match": { "title": "laptop" } },
"rescore": {
"window_size": 100,
"query": {
"rescore_query": { "match_phrase": { "title": { "query": "gaming laptop", "slop": 2 } } },
"query_weight": 0.7,
"rescore_query_weight": 1.2
}
}
}
Pahalı sorguları (phrase matching, learning-to-rank, vektör yeniden sıralama) tüm sonuç kümesi yerine sadece üst pencereye uygulamak için rescoring kullanın — büyük performans kazancı.
explain API — İlgililik Hata Ayıklama
GET /products/_explain/1
{ "query": { "match": { "title": "wireless mouse" } } }
Ayrıca normal bir _search isteği içinde "explain": true, her hit için skorlama detayını gösterir — relevance ayarı yaparken gereklidir.
10. Sayfalama (Pagination) Stratejileri
| Yöntem | Kullanım alanı | Kısıtlama |
|---|---|---|
from + size | Küçük sonuç kümeleri, UI sayfalaması (1-100. sayfa) | Derin sayfalama (from > 10.000) pahalıdır/varsayılan olarak engellenir (index.max_result_window) |
search_after | Derin sayfalama, gerçek zamanlı “sonraki sayfa” | Kararlı bir sort gerektirir (genelde _shard_doc/_id tie-breaker ile); rastgele sayfaya atlama yok |
| Scroll API | Tam veri export’u / reindex benzeri batch işleme | Kullanıcıya yönelik sayfalama için değil; bir point-in-time snapshot’ı açık tutar (kaynak maliyeti); PIT tarafından yerini alıyor |
Point in Time (PIT) + search_after | Scroll’un modern yerine geçeni; sayfalanmış istekler arasında tutarlı görünüm | Biraz daha fazla kurulum (PIT aç, PIT kapat) |
PIT ile search_after (Önerilen Modern Pattern)
POST /products/_pit?keep_alive=1m
GET /_search
{
"size": 100,
"query": { "match_all": {} },
"pit": { "id": "<pit_id>", "keep_alive": "1m" },
"sort": [ { "created_at": "asc" }, { "_shard_doc": "asc" } ]
}
Sonraki istekte search_after değeri olarak son hit’in sort değerlerini kullanın.
{
"search_after": [1622512800000, 987654],
"sort": [ { "created_at": "asc" }, { "_shard_doc": "asc" } ]
}
İşiniz bitince PIT’i DELETE /_pit ile kapatın.
Best practice: from + size değerinin asla index.max_result_window‘u (varsayılan 10.000) aşmasına izin vermeyin — hata fırlatır ve sayfalama tasarımı için kırmızı bir bayraktır.
11. Veri Modelleme Pattern’leri
Denormalizasyon Elasticsearch’te Normaldir
İlişkisel veritabanlarının aksine, ES’te ölçekte index’ler arası join yoktur. İlişkili veriyi indexleme zamanında dokümanın içine denormalize edin.
{
"order_id": "1001",
"customer": { "id": "55", "name": "Jane Doe", "tier": "gold" },
"items": [
{ "sku": "A1", "name": "Widget", "qty": 2, "price": 9.99 }
]
}
Parent/Child (join alanı) — İlişkileri Modellemek Zorunda Olduğunuzda
Sadece child’lar parent’lardan çok daha sık güncellendiğinde kullanın (parent’ı reindexlemeyi önler).
PUT /forum
{
"mappings": {
"properties": {
"join_field": { "type": "join", "relations": { "question": "answer" } }
}
}
}
Trade-off: daha yavaş sorgular (has_child/has_parent), parent+child için aynı shard routing gerektirir. Okuma ağırlıklı, nadiren güncellenen ilişkiler için nested‘i, çoğu durum için denormalizasyonu tercih edin.
Nested vs Parent/Child vs Denormalize — Karar Tablosu
| Pattern | Güncelleme sıklığı | Sorgu karmaşıklığı | Performans | Ne zaman kullanılır |
|---|---|---|---|---|
| Denormalize | Her parent güncellemesinde veri çoğaltılır | Basit | En hızlı okuma | Varsayılan seçim — çoğu durum |
nested | Herhangi bir nested değişikliğinde tüm parent doküman reindexlenir | Orta (nested sorgusu) | İyi | Çoğunlukla statik, küçük-orta boy ilişkili nesne array’leri |
join (parent/child) | Child’lar bağımsız güncellenir | Karmaşık (has_child) | Daha yavaş | Child’lar parent’tan çok daha sık güncellendiğinde, büyük 1:çok ilişkiler |
Time-Series / Log Pattern’i: Zaman Dilimi Başına Index
logs-2026.08.01
logs-2026.08.02
logs-2026.08.03
Bir alias (logs-write) ve ILM rollover ile birleştirildiğinde — eski verinin kolayca silinmesini sağlar (tüm index’i düşürmek delete_by_query‘e göre çok daha hızlıdır) ve hot/warm/cold tier’ları index yaşına göre izole eder. Bu, modern data streams özelliğinin temelidir.
Data Streams (bu pattern üzerine inşa edilmiştir, 7.9+‘dan beri)
PUT _index_template/logs-template
{
"index_patterns": ["logs-myapp-*"],
"data_stream": {},
"template": {
"settings": { "index.lifecycle.name": "logs-policy" }
}
}
POST /logs-myapp-default/_doc
{ "@timestamp": "2026-08-17T10:00:00Z", "message": "hello" }
Data stream’ler, bir dizi gizli backing index’i, rollover’ı otomatik olarak yönetir ve time-series ingest işlemlerini basitleştirir — log/metrik için manuel olarak yönetilen günlük index pattern’lerine göre tercih edilir.
Alan Patlaması — Mapping Büyümesine Dikkat
Varsayılan index.mapping.total_fields.limit 1000’dir. Rastgele JSON’ın (örn. kullanıcı tarafından sağlanan metadata) dynamic mapping’i bunu patlatabilir. Bunların dinamik olarak map edilmesine izin vermek yerine rastgele/değişken JSON nesneleri için flattened tipini kullanın:
{ "metadata": { "type": "flattened" } }
12. Index Lifecycle Management (ILM)
ILM, index’leri yaş/boyut/doküman sayısına göre hot → warm → cold → frozen → delete fazları arasında otomatik olarak taşır.
PUT _ilm/policy/logs_policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": { "max_primary_shard_size": "30gb", "max_age": "1d" },
"set_priority": { "priority": 100 }
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 },
"set_priority": { "priority": 50 }
}
},
"cold": {
"min_age": "30d",
"actions": { "set_priority": { "priority": 0 }, "freeze": {} }
},
"delete": {
"min_age": "90d",
"actions": { "delete": {} }
}
}
}
}
Ana aksiyonlar:
rollover— mevcut index boyut/yaş/doküman sayısı eşiklerine ulaştığında yeni bir backing index oluşturur (bir alias veya data stream ile eşleştirilir).shrink— daha eski, daha az sorgulanan index’ler için shard sayısını azaltır (daha az shard = daha az overhead).forcemerge— segment’leri birleştirir (salt okunur warm/cold veri için idealdir — aktif olarak yazılan bir index’te asla forcemerge yapmayın).freeze/ searchable snapshot’lar — veriyi ucuz object storage’a (S3/GCS/Azure Blob) taşırken aranabilir kalmasını sağlar, cold/frozen tier’lar için.delete— index’i tamamen kaldırır.
Best practice: time-series veriyi asla delete_by_query ile manuel silmeyin — her zaman zaman-dilimi-başına-index + ILM delete fazı olarak yapılandırın (tüm bir index’i düşürmek anındadır; delete_by_query pahalıdır ve tombstone bırakır).
13. Performans Best Practice’leri
İndexleme Performansı
- Bulk API‘yi kullanın, sadece doküman sayısına göre değil boyuta göre (5–15MB) batch’leyin.
- Yazma ağırlıklı iş yükleri için
refresh_interval‘ı artırın (örn.30sveya-1); okuma ağırlıklı fazlarda geri yükleyin. - Büyük bulk yüklemeleri sırasında
number_of_replicas: 0ayarlayın, sonra ölçeklendirin. - İdempotent upsert’lere ihtiyacınız olmadıkça otomatik üretilen ID’ler kullanın (özel ID’ler yazmadan önce bir version lookup gerektirir).
- Derin nested dokümanlardan ve büyük array’lerden kaçının — indexleme maliyetini katlarlar.
- Yoğun indexleme yapan node’lar için
indices.memory.index_buffer_size‘ı (varsayılan heap’in %10’u) ayarlayın. _source‘u sadece aşırı alan kısıtlı durumlarda devre dışı bırakın (reindex/update/highlight yeteneğini ciddi şekilde sınırlar — nadiren buna değer).- Diski kurtarmak için cold/arşiv index’lerinde
index.codec: best_compressionkullanın (biraz CPU’ya karşılık).
Arama Performansı
- Skorlama gerekmeyen her yerde
filtercontext kullanın — filtreler cache’lenir ve sorgular arasında yeniden kullanılır. size‘ı sınırlayın ve sayfalamayı doğru kullanın (bkz. bölüm 10).- Hot path’lerde
scriptsorgularından /script_score‘dan kaçının — doküman başına script çalıştırmak pahalıdır. - Aggregation/sıralama için
textüzerindefielddatayerinekeywordalanları tercih edin. - Sadece gerekli alanları döndürmek için
_source filteringkullanın:{ "_source": ["title", "price"], "query": { ... } } - Kritik index’ler için restart sonrası cache’leri ısıtın (
index.warmer, veya sadece temsili sorgular çalıştırın). *ile başlayan wildcard sorgularından kaçının (*mouse) — term index’i verimli kullanamaz;ngramalanlarını düşünün.- Partition key’i bildiğinizde tüm shard’lara scatter-gather yapmayı önlemek için belirli shard’ları hedeflemek üzere routing kullanın:
GET /orders/_search?routing=customer_123 - Daha hızlı arama için salt okunur index’leri 1 segmente force merge edin (aktif olarak yazılan index’lerde asla).
search.max_buckets‘ı dikkatli artırın; çok büyük aggregation’lar bellek baskısını artırır.
Donanım ve JVM
- Heap: kullanılabilir RAM’in %50’sine ayarlayın, ~30-32GB’ı asla aşmayın (compressed oops sınırı).
- Diğer %50 RAM’i OS page cache’i için bırakın — Lucene, OS seviyesindeki dosya cache’ine büyük ölçüde bağımlıdır.
- SSD kullanın; Elasticsearch I/O’ya duyarlıdır.
- Swap’ı devre dışı bırakın (
bootstrap.memory_lock: true). - GC duraklamalarını izleyin — sık ve uzun GC’ler heap baskısını veya fielddata/query cache’in yanlış kullanımını gösterir.
Circuit Breaker’lar
Elasticsearch’ün OOM’u önlemek için yerleşik circuit breaker’ları vardır (indices.breaker.total.limit, fielddata, request). Sık sık circuit_breaking_exception alıyorsanız, sadece limitleri yükseltmek yerine sorgu pattern’lerini (büyük aggregation’lar, fielddata kullanımı) araştırın.
14. Search-as-You-Type ve Otomatik Tamamlama
Seçenek 1: search_as_you_type alan tipi (en basit)
{
"properties": {
"title": { "type": "search_as_you_type" }
}
}
{
"query": {
"multi_match": {
"query": "wirele mo",
"type": "bool_prefix",
"fields": ["title", "title._2gram", "title._3gram"]
}
}
}
Seçenek 2: completion suggester (en hızlı, bellek içi FST yapısı)
{
"properties": {
"suggest": { "type": "completion" }
}
}
{
"suggest": {
"product-suggest": {
"prefix": "wirel",
"completion": { "field": "suggest", "fuzzy": { "fuzziness": 1 }, "size": 5 }
}
}
}
En iyi kullanım: milisaniye gecikmeli dropdown tarzı otomatik tamamlama. Kısıtlama: tam bir sorgudan daha az esnek sıralama/filtreleme.
Seçenek 3: Edge n-gram özel analyzer (bkz. bölüm 5)
En iyi kullanım: relevance skorlaması/filtreleme ile birleştirilmiş tam metin tarzı prefix eşleştirme.
Öneri: saf otomatik tamamlama widget’ları için completion, diğer filtreler/skorlama ile birleştirilmesi gerektiğinde edge_ngram veya search_as_you_type kullanın.
15. Geo Sorguları
{
"properties": {
"location": { "type": "geo_point" }
}
}
// geo_distance filtresi
{
"query": {
"bool": {
"filter": {
"geo_distance": {
"distance": "10km",
"location": { "lat": 40.73, "lon": -73.99 }
}
}
}
}
}
// mesafeye göre sıralama
{
"sort": [
{
"_geo_distance": {
"location": { "lat": 40.73, "lon": -73.99 },
"order": "asc",
"unit": "km"
}
}
]
}
// geo_bounding_box - hızlı dikdörtgen filtre
{ "query": { "geo_bounding_box": { "location": { "top_left": { "lat": 41, "lon": -74.5 }, "bottom_right": { "lat": 40.5, "lon": -73.5 } } } } }
Karmaşık poligonlar/şekiller için, geo_shape sorgusu ile geo_shape alan tipini kullanın (intersects, within, contains, disjoint destekler).
16. Güvenlik
Temel Yapı Taşları (X-Pack Security, 7.1’den beri temel özellikler için ücretsiz dahildir)
- TLS, transport ve HTTP katmanları için — production için zorunludur.
- Kimlik doğrulama (Authentication): native realm, LDAP, SAML, OIDC, Kerberos, API key’ler.
- Role-Based Access Control (RBAC): index/cluster/alan/doküman seviyesinde yetkilerle rol tanımlama.
POST /_security/role/read_only_products
{
"indices": [
{
"names": ["products"],
"privileges": ["read"],
"field_security": { "grant": ["name", "price", "category"] },
"query": { "term": { "public": true } }
}
]
}
Bu, tek bir rolde alan seviyesi güvenliki (sadece belirli alanları göster) ve doküman seviyesi güvenliki (satır seviyesi filtreleme) birleştirir.
API Key’ler (servis-servis kimlik doğrulaması için basic auth’a göre tercih edilir)
POST /_security/api_key
{
"name": "my-app-key",
"role_descriptors": {
"app_role": { "indices": [ { "names": ["products"], "privileges": ["read"] } ] }
},
"expiration": "30d"
}
Best Practice’ler
- Elasticsearch’ü asla doğrudan halka açık internete maruz bırakmayın.
- Superuser yerine, her uygulama için minimal yetkilere sahip API key’leri kullanın.
- Kimlik bilgilerini rotate edin ve API key’lerde expiration ayarlayın.
- Uyumluluk açısından hassas cluster’lar için audit logging’i etkinleştirin.
- Dynamic mapping istismarı yoluyla alan patlaması saldırılarını önlemek için ingest pipeline’ları/index template’leri kullanın.
17. İzleme ve Sorun Giderme
Temel Cluster API’leri
GET /_cluster/health?level=indices
GET /_cluster/state
GET /_cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m
GET /_cat/indices?v&s=store.size:desc
GET /_cat/shards?v&h=index,shard,prirep,state,docs,store,node
GET /_nodes/stats
GET /_nodes/hot_threads
GET /_cat/thread_pool/write?v
GET /_cat/pending_tasks?v
Cluster Health Renkleri
| Durum | Anlamı |
|---|---|
| Green | Tüm primary + replica shard’lar allocate edilmiş |
| Yellow | Tüm primary’ler allocate edilmiş, bazı replica’lar değil (tek node’lu dev cluster’larda yaygın) |
| Red | Bazı primary shard’lar allocate edilmemiş — etkilenen index’lerde veri kaybı riski / sorgular başarısız oluyor |
Slow Log
PUT /products/_settings
{
"index.search.slowlog.threshold.query.warn": "2s",
"index.search.slowlog.threshold.fetch.warn": "1s",
"index.indexing.slowlog.threshold.index.warn": "2s"
}
Tam profiling overhead’i olmadan production’da yavaş sorguları/indexleme işlemlerini tespit etmek için kullanın.
Profile API (derin sorgu performans analizi)
GET /products/_search
{
"profile": true,
"query": { "match": { "title": "laptop" } }
}
Clause başına zamanlama detayını gösterir (Lucene seviyesinde) — az kullanın, overhead ekler, production hot path’leri için değil.
Yaygın Hatalar ve Çözümleri
| Hata | Nedeni | Çözüm |
|---|---|---|
circuit_breaking_exception | Sorgu/agg çok fazla bellek kullanıyor | Agg boyutunu/kardinalitesini azaltın, filter ekleyin, heap artırın, fielddata’yı kontrol edin |
es_rejected_execution_exception | Thread pool kuyruğu dolu (bulk/search) | Backoff + tekrar dene, bulk boyutunu/eşzamanlılığını azaltın, node’ları ölçeklendirin |
mapper_parsing_exception | Indexleme sırasında tip uyuşmazlığı | Kaynak veriyi veya mapping’i düzeltin; ignore_malformed‘ı düşünün |
version_conflict_engine_exception | Eşzamanlı güncelleme yarışı (race) | retry_on_conflict ile tekrar deneyin, veya optimistic concurrency’yi doğru kullanın |
search_phase_execution_exception | Alttaki shard hataları | _cluster/health, node loglarını, allocate edilmemiş shard’ları kontrol edin |
| Cluster yellow/red’de takılı kalıyor | Unassigned shard’lar | GET _cluster/allocation/explain |
GET /_cluster/allocation/explain
Bu, shard’ların neden allocate olmadığını (disk watermark, node filtreleme, eksik node, vb.) teşhis etmenin #1 aracıdır.
18. Yaygın Anti-Pattern’ler
| Anti-Pattern | Neden kötü | Bunun yerine yapın |
|---|---|---|
| Binlerce index, her biri küçük tek shard’lı | Cluster state şişmesi, shard başına overhead | Data stream’ler / ILM rollover kullanın, konsolide edin |
text alanında term sorgusu kullanmak | Sessizce beklenen şekilde asla eşleşmez | .keyword alt-alanını kullanın |
Derin from/size sayfalama | Pahalı, coordinating node’da bellek yoğun | search_after + PIT |
| Öngörülemeyen girdi ile production’da dynamic mapping | Alan patlaması, mapping çakışmaları, index bozulma riski | Açık mapping’ler + dynamic: strict, veya flattened tipi |
Kullanıcıya yönelik sayfalama için scroll kullanmak | Kaynak sızıntısı, stateful, canlı veriyi yansıtmaz | search_after / PIT |
| Devasa sınırsız array’ler / nested nesneler saklamak | Doküman başına ciddi overhead | Yeniden yapılandırın, array boyutlarını sınırlayın, veya ayrı index kullanın |
Başında * olan wildcard sorgusu | Tam index taramasına benzer maliyet | ngram alanı kullanın veya yeniden yapılandırın |
Bulk yükleme sırasında refresh_interval‘ı görmezden gelmek | Gereksiz segment oluşturma, daha yavaş indexleme | Yükleme sırasında -1 ayarlayın |
| ES’i sistem-of-record / birincil DB olarak kullanmak | Gerçek ACID transaction/join yok; yanlış yapılandırmada veri kaybı riski | Bir source-of-truth DB tutun; ES’i arama/analitik katmanı olarak kullanın |
| Disk watermark’larını izlememek | Cluster beklenmedik şekilde read-only’e geçer | %85/%90/%95 watermark’larda alert kurun |
Her şeyin must içinde olduğu tek dev bir bool sorgusu | Cache yeniden kullanımı yok, gereksiz skorlama | Skorlama gerekmeyen yerlerde filter‘a ayırın |
_all alanını veya tüm alanlarda aşırı geniş multi_match kullanmak | Yavaş, düşük relevance | Uygun boost’larla aranabilir alanları açıkça tanımlayın |
19. Client Kod Örnekleri
Python (elasticsearch-py, 8.x client)
from elasticsearch import Elasticsearch
es = Elasticsearch(
"https://localhost:9200",
api_key="base64_api_key",
)
# Doküman indexleme
es.index(index="products", id="1", document={"name": "Mouse", "price": 25.99})
# Arama
resp = es.search(
index="products",
query={"bool": {"must": [{"match": {"name": "mouse"}}], "filter": [{"range": {"price": {"lte": 50}}}]}},
size=10,
)
for hit in resp["hits"]["hits"]:
print(hit["_source"])
# Bulk
from elasticsearch.helpers import bulk
actions = [
{"_index": "products", "_id": str(i), "_source": {"name": f"Item {i}", "price": i}}
for i in range(1000)
]
bulk(es, actions)
Node.js (@elastic/elasticsearch)
const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'https://localhost:9200', auth: { apiKey: 'base64_api_key' } });
await client.index({
index: 'products',
id: '1',
document: { name: 'Mouse', price: 25.99 },
});
const result = await client.search({
index: 'products',
query: {
bool: {
must: [{ match: { name: 'mouse' } }],
filter: [{ range: { price: { lte: 50 } } }],
},
},
});
console.log(result.hits.hits);
Java (Java API Client, 8.x)
ElasticsearchClient client = new ElasticsearchClient(transport);
IndexResponse response = client.index(i -> i
.index("products")
.id("1")
.document(new Product("Mouse", 25.99))
);
SearchResponse<Product> search = client.search(s -> s
.index("products")
.query(q -> q.match(m -> m.field("name").query("mouse"))),
Product.class
);
20. Hızlı Referans (Cheat Sheet)
# Cluster
GET /_cluster/health
GET /_cat/indices?v
GET /_cat/nodes?v
GET /_cluster/allocation/explain
# Index yönetimi
PUT /my_index
DELETE /my_index
POST /my_index/_close
POST /my_index/_open
GET /my_index/_mapping
PUT /my_index/_settings
# Alias'lar
POST /_aliases
GET /_alias/my_alias
# CRUD
PUT /my_index/_doc/1
GET /my_index/_doc/1
POST /my_index/_update/1
DELETE /my_index/_doc/1
POST /_bulk
# Arama
GET /my_index/_search
GET /my_index/_search?q=title:laptop
POST /my_index/_search { query, aggs, sort, size, from, _source }
# Reindex
POST /_reindex
POST /my_index/_update_by_query
POST /my_index/_delete_by_query
# ILM
GET /_ilm/policy
PUT /_ilm/policy/my_policy
POST /my_index/_ilm/retry
# Analyze
POST /_analyze { "analyzer": "standard", "text": "..." }
Daha Fazla Okuma
- Resmi dokümantasyon: https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
- Elasticsearch: The Definitive Guide (eski ama temel kavramlar için hâlâ ilgili)
- Versiyona özgü özellik detayları için Elastic blog’u
Bu rehber, Elasticsearch 8.x ile tutarlı pattern ve API’leri yansıtır. Çalıştırdığınız versiyona göre tam sözdizimini her zaman doğrulayın — bazı seçenekler (örn. _type, eski scroll varsayılanları) 7.x öncesi versiyonlardan önemli ölçüde farklıdır.