Analyzer Test ve Debug Rehberi
Giriş — Kara Kutuyu Açmak
Bir custom analyzer yazdınız. Güzel görünüyor. Index'e veri eklediniz, arama yaptınız ve... sonuç boş geldi. Neden? "evlerin" araması "ev" içeren dokümanı neden bulamadı? Stemmer çalışmıyor mu? Yoksa stop filter "bir" yerine "bir" kelimesini mi yedi?
Bu an, Elasticsearch'teki en sinir bozucu andır. Hata mapping'de mi, analyzer'da mı, sorguda mı — hiçbir fikriniz yok.
İşte _analyze API tam bu an için var. Bu API, Elasticsearch'ün kara kutusunu açar ve analiz sürecinin her adımını görünür kılar: hangi character filter ne dönüştürdü, tokenizer metni nasıl böldü, her token filter ne yaptı — hepsini adım adım görebilirsiniz.
Bu ders, _analyze API'nin her parametresini, debug tekniklerini, search-time vs index-time farklarını pratikte nasıl test edeceğinizi ve analyzer geliştirme workflow'unu öğretecek.
1. _analyze API Derinlemesine
1.1 Temel Kullanım Formları
_analyze API'nin üç temel kullanım şekli vardır:
Form 1: Ad-hoc — Bileşenleri elle belirt
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase", "stop"],
"char_filter": ["html_strip"],
"text": "<p>The Quick Brown Fox</p>"
}Bu form, hızlı test için idealdir. Index oluşturmadan direkt test edersiniz.
Form 2: Built-in analyzer adıyla
POST _analyze
{
"analyzer": "turkish",
"text": "İstanbul'daki evlerin fiyatları"
}Form 3: Index'e tanımlı analyzer ile
POST my_index/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "Test metni"
}Form 4: Field adıyla — o field'ın analyzer'ını kullan
POST my_index/_analyze
{
"field": "title",
"text": "Bu metnin title field'ının analyzer'ı ile analiz et"
}1.2 Çıktıyı Anlamak
POST _analyze
{
"analyzer": "standard",
"text": "Elasticsearch güçlü bir arama motoru"
}Çıktı:
{
"tokens": [
{
"token": "elasticsearch",
"start_offset": 0,
"end_offset": 13,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "güçlü",
"start_offset": 14,
"end_offset": 19,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "bir",
"start_offset": 20,
"end_offset": 23,
"type": "<ALPHANUM>",
"position": 2
}
]
}Her token şu bilgileri taşır:
| Alan | Açıklama |
|---|---|
token | Analiz sonrası oluşan token (inverted index'e yazılan değer) |
start_offset | Orijinal metindeki başlangıç pozisyonu (karakter) |
end_offset | Orijinal metindeki bitiş pozisyonu |
type | Token tipi (<ALPHANUM>, <NUM>, <HANGUL>, <KATAKANA> vb.) |
position | Token'ın sıra pozisyonu (phrase query ve proximity search'te kritik) |
1.3 explain: true — Her Adımı Görmek
Bu parametreyi kullandığınızda, her token'ın hangi adımdan geçtiğini ve nasıl dönüştüğünü görürsünüz:
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase", {"type": "stop", "stopwords": ["bir", "ve"]}],
"text": "Güçlü bir Arama ve Analiz",
"explain": true
}Çıktı (kısaltılmış):
{
"detail": {
"custom_analyzer": true,
"tokenizer": {
"name": "standard",
"tokens": [
{ "token": "Güçlü", "position": 0, "start_offset": 0, "end_offset": 5 },
{ "token": "bir", "position": 1, "start_offset": 6, "end_offset": 9 },
{ "token": "Arama", "position": 2, "start_offset": 10, "end_offset": 15 },
{ "token": "ve", "position": 3, "start_offset": 16, "end_offset": 18 },
{ "token": "Analiz", "position": 4, "start_offset": 19, "end_offset": 25 }
]
},
"tokenfilters": [
{
"name": "lowercase",
"tokens": [
{ "token": "güçlü", "position": 0 },
{ "token": "bir", "position": 1 },
{ "token": "arama", "position": 2 },
{ "token": "ve", "position": 3 },
{ "token": "analiz", "position": 4 }
]
},
{
"name": "__anonymous_stop",
"tokens": [
{ "token": "güçlü", "position": 0 },
{ "token": "arama", "position": 2 },
{ "token": "analiz", "position": 4 }
]
}
]
}
}Her adımda ne olduğu net görünüyor:
Tokenizer: 5 token üretildi
lowercase: Tüm harfler küçüldü
stop: "bir" ve "ve" silindi, 3 token kaldı
Position değerlerine dikkat edin: "arama" hâlâ position 2'de, "analiz" position 4'te. Stop filter pozisyonları değiştirmez — bu phrase query'ler için önemlidir.
1.4 Birden Fazla Metin Test Etme
text alanına array geçerek birden fazla metni tek seferde test edebilirsiniz:
POST _analyze
{
"analyzer": "standard",
"text": [
"Birinci metin",
"İkinci metin",
"Üçüncü metin"
]
}Her metin ayrı ayrı analiz edilir, token'lar aynı array'de birleşir. Pozisyon numaraları her metinde sıfırdan başlar.
2. Analyzer Karşılaştırma Testi
Farklı analyzer'ların aynı metin üzerindeki etkisini karşılaştırmak, doğru analyzer'ı seçmenin en güvenilir yoludur.
2.1 Built-in Analyzer Karşılaştırması
// Standard analyzer
POST _analyze
{
"analyzer": "standard",
"text": "İstanbul'daki 3 katlı evlerin fiyatları arttı mı?"
}
// Sonuç: ["istanbul'daki", "3", "katlı", "evlerin", "fiyatları", "arttı", "mı"]
// Turkish analyzer
POST _analyze
{
"analyzer": "turkish",
"text": "İstanbul'daki 3 katlı evlerin fiyatları arttı mı?"
}
// Sonuç: ["istanbul", "3", "kat", "ev", "fiyat", "art"]
// Whitespace analyzer
POST _analyze
{
"analyzer": "whitespace",
"text": "İstanbul'daki 3 katlı evlerin fiyatları arttı mı?"
}
// Sonuç: ["İstanbul'daki", "3", "katlı", "evlerin", "fiyatları", "arttı", "mı?"]
// Simple analyzer
POST _analyze
{
"analyzer": "simple",
"text": "İstanbul'daki 3 katlı evlerin fiyatları arttı mı?"
}
// Sonuç: ["istanbul", "daki", "katlı", "evlerin", "fiyatları", "arttı", "mı"]Karşılaştırma tablosu:
| Analyzer | Token sayısı | Apostrof | Stemming | Stop | Lowercase |
|---|---|---|---|---|---|
| standard | 7 | Kelime içi | ✗ | ✗ | ✓ |
| turkish | 6 | Atılır | ✓ | ✓ | ✓ (TR) |
| whitespace | 7 | Kelime içi | ✗ | ✗ | ✗ |
| simple | 7 | Böler | ✗ | ✗ | ✓ |
2.2 Side-by-Side Karşılaştırma Script'i
Production'da birden fazla analyzer'ı karşılaştırmak için bir test index'i oluşturun:
PUT analyzer_lab
{
"settings": {
"analysis": {
"filter": {
"tr_lower": { "type": "lowercase", "language": "turkish" },
"tr_stop": { "type": "stop", "stopwords": "_turkish_" },
"tr_stem": { "type": "stemmer", "language": "turkish" },
"apost": { "type": "apostrophe" }
},
"analyzer": {
"v1_basic": {
"type": "custom",
"tokenizer": "standard",
"filter": ["tr_lower"]
},
"v2_with_stop": {
"type": "custom",
"tokenizer": "standard",
"filter": ["tr_lower", "tr_stop"]
},
"v3_with_stem": {
"type": "custom",
"tokenizer": "standard",
"filter": ["apost", "tr_lower", "tr_stop", "tr_stem"]
},
"v4_aggressive": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"apost", "tr_lower", "tr_stop", "tr_stem",
{"type": "length", "min": 3}
]
}
}
}
}
}
// Her analyzer'ı aynı metinle test et
POST analyzer_lab/_analyze
{ "analyzer": "v1_basic", "text": "Türkiye'deki büyük şehirlerin bir listesi" }
// ["türkiye'deki", "büyük", "şehirlerin", "bir", "listesi"]
POST analyzer_lab/_analyze
{ "analyzer": "v2_with_stop", "text": "Türkiye'deki büyük şehirlerin bir listesi" }
// ["türkiye'deki", "büyük", "şehirlerin", "listesi"]
POST analyzer_lab/_analyze
{ "analyzer": "v3_with_stem", "text": "Türkiye'deki büyük şehirlerin bir listesi" }
// ["türkiye", "büyük", "şehir", "list"]
POST analyzer_lab/_analyze
{ "analyzer": "v4_aggressive", "text": "Türkiye'deki büyük şehirlerin bir listesi" }
// ["türkiye", "büyük", "şehir", "list"]Her adımda nelerin değiştiğini net görebilirsiniz. Bu yaklaşımla production'a almadan önce ideal kombinasyonu bulursunuz.
3. Index-time vs Search-time Analyzer Farkları
Bu konu, Elasticsearch'teki en kafa karıştırıcı konulardan biridir. Yanlış anlaşılması, "arama çalışmıyor" sorunlarının en yaygın nedenidir.
3.1 Temel Kavram
Index-time analyzer: Doküman index'lenirken text field'larını analiz eder. Çıkan token'lar inverted index'e yazılır.
Search-time analyzer: Arama sorgusundaki metni analiz eder. Çıkan token'lar inverted index'te aranır.
Index-time:
"Evlerin fiyatları arttı"
→ turkish analyzer → ["ev", "fiyat", "art"]
→ inverted index'e yazılır
Search-time:
"evlerin fiyatı"
→ turkish analyzer → ["ev", "fiyat"]
→ inverted index'te bu token'lar aranır
→ EŞLEŞME! ✓3.2 Farklı Analyzer Kullanma
Bazı senaryolarda farklı analyzer kullanmak gerekir:
PUT different_analyzers
{
"settings": {
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 10
}
},
"analyzer": {
"autocomplete_index": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "autocomplete_filter"]
},
"autocomplete_search": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "autocomplete_index",
"search_analyzer": "autocomplete_search"
}
}
}
}3.3 Hangi Analyzer Kullanılıyor — Nasıl Anlaşılır?
Mapping'den doğrulama:
GET my_index/_mappingField'ın analyzer bilgisi çıktıda görünür. Eğer search_analyzer belirtilmemişse, analyzer hem index hem search'te kullanılır. Hiçbiri belirtilmemişse standard kullanılır.
3.4 Index-time vs Search-time Test
Aynı field'ın her iki analyzer'ını da _analyze ile test edin:
// Index-time analyzer testi
POST different_analyzers/_analyze
{
"field": "title",
"text": "Elasticsearch"
}
// Sonuç: ["el", "ela", "elas", "elast", "elasti", "elastic", "elastics", "elasticse", "elasticsearch"]
// Search-time analyzer testi — normalizer parametresi ile
POST _analyze
{
"analyzer": "autocomplete_search",
"text": "Ela"
}
// Sonuç: ["ela"]⚠️ Dikkat: POST my_index/_analyze + field parametresi her zaman index-time analyzer'ı kullanır. Search-time analyzer'ı test etmek için analyzer adını doğrudan verin.
4. Explain API ile Scoring Debug
_analyze API metin analizini debug eder. _explain API ise bir dokümanın neden belli bir skor aldığını debug eder. İkisi birlikte güçlü bir debug toolkit oluşturur.
4.1 Explain API Kullanımı
GET my_index/_explain/1
{
"query": {
"match": {
"title": "elasticsearch güçlü"
}
}
}Çıktı (kısaltılmış):
{
"_index": "my_index",
"_id": "1",
"matched": true,
"explanation": {
"value": 1.8754,
"description": "sum of:",
"details": [
{
"value": 1.2345,
"description": "weight(title:elasticsearch in 0) [PerFieldSimilarity]",
"details": [
{
"value": 1.2345,
"description": "score(freq=1.0), computed as boost * idf * tf",
"details": [
{ "value": 2.2, "description": "boost" },
{ "value": 0.6931, "description": "idf, computed as log(1 + (N - n + 0.5) / (n + 0.5))" },
{ "value": 0.8103, "description": "tf, computed as freq / (freq + k1 * (1 - b + b * dl / avgdl))" }
]
}
]
}
]
}
}Bu çıktıdan anlayabileceğiniz bilgiler:
matched: true/false — Doküman sorguyla eşleşti mi?
value — BM25 skoru
idf — Terimin nadir olup olmadığı (nadir = yüksek skor)
tf — Terimin dokümanda kaç kez geçtiği
boost — Uygulanan boost değeri
dl/avgdl — Doküman uzunluğu / ortalama doküman uzunluğu
4.2 Profile API — Sorgu Performans Analizi
GET my_index/_search
{
"profile": true,
"query": {
"bool": {
"must": [
{ "match": { "title": "elasticsearch" } }
],
"filter": [
{ "term": { "category": "Teknoloji" } }
]
}
}
}Profile çıktısı her query bileşeninin süresini gösterir:
{
"profile": {
"shards": [
{
"searches": [
{
"query": [
{
"type": "BooleanQuery",
"description": "+title:elasticsearch #category:Teknoloji",
"time_in_nanos": 285430,
"children": [
{
"type": "TermQuery",
"description": "title:elasticsearch",
"time_in_nanos": 142356
},
{
"type": "TermQuery",
"description": "#category:Teknoloji",
"time_in_nanos": 52841
}
]
}
]
}
]
}
]
}
}Bu çıktı, hangi sorgu bileşeninin yavaş olduğunu gösterir. Optimizasyon yapacağınız yeri bulursunuz.
5. Tokenization Debug Teknikleri
5.1 Problem: Arama Sonuç Getirmiyor
En yaygın debug senaryosu budur. Sistematik yaklaşım:
Adım 1: Dokümanın index-time token'larını kontrol edin:
POST my_index/_analyze
{
"field": "title",
"text": "İstanbul'daki güzel evler"
}
// Sonuç: ["istanbul", "güzel", "ev"]Adım 2: Sorgunun search-time token'larını kontrol edin:
POST _analyze
{
"analyzer": "my_search_analyzer",
"text": "İstanbul ev"
}
// Sonuç: ["istanbul", "ev"] — Eşleşmeli!Adım 3: Eğer token'lar eşleşiyor ama sonuç gelmiyorsa, _explain kullanın:
GET my_index/_explain/1
{
"query": { "match": { "title": "İstanbul ev" } }
}5.2 Problem: Beklenmeyen Sonuçlar Geliyor
Alakasız dokümanlar geliyorsa, o dokümanın neden eşleştiğini anlamak için:
// Dokümanın token'larını görmek
POST my_index/_analyze
{
"field": "title",
"text": "Problematik dokümanın title değeri buraya"
}5.3 Token Position Analizi
Phrase query ve span query'lerde pozisyon önemlidir:
POST _analyze
{
"analyzer": "standard",
"text": "quick brown fox",
"explain": true
}match_phrase sorgusu, token'ların ardışık pozisyonlarda olmasını gerektirir. Stop filter bir token'ı silerse, pozisyon boşluğu oluşur ve phrase query etkilenebilir.
// Stop filter pozisyon koruması
POST _analyze
{
"tokenizer": "standard",
"filter": [
"lowercase",
{ "type": "stop", "stopwords": ["the"] }
],
"text": "the quick brown fox",
"explain": true
}
// "quick" pozisyon 1'de kalır (0 değil!) — "the"nin yeri korunurBu yüzden match_phrase sorgusu slop: 0 ile yapılıyorsa, stop words pozisyon boşluğu yaratabilir.
6. Custom Analyzer Geliştirme Workflow
Sıfırdan bir analyzer geliştirirken sistematik bir yaklaşım izleyin.
6.1 Adım 1: Gereksinimleri Tanımla
Sorulacak sorular:
Hangi dilde/dillerde arama yapılacak?
Autocomplete gerekli mi?
Diacritics (özel karakter) toleransı gerekli mi?
Synonym desteği gerekli mi?
HTML içerik var mı?
Exact match de gerekli mi (keyword field)?
6.2 Adım 2: Test Corpus Hazırla
En az 10-15 gerçek metin örneği toplayın. Bunlar edge case'leri de kapsamalı:
// Test corpus
[
"İstanbul'daki 3+1 daireler",
"ÇALIŞANLARIN hakları",
"Samsung Galaxy S24 Ultra 256GB",
"<p>HTML <b>içerikli</b> metin</p>",
"Atatürk'ün Gençliğe Hitabesi",
"100₺ ve üzeri ürünler",
"user@email.com adresine gönderin",
"COVID-19 aşı kampanyası",
"e-ticaret ve e-devlet",
"Fenerbahçe-Galatasaray derbi maçı"
]6.3 Adım 3: İteratif Geliştirme
// İterasyon 1: Sadece tokenizer + lowercase
POST _analyze
{
"tokenizer": "standard",
"filter": [{"type": "lowercase", "language": "turkish"}],
"text": "İstanbul'daki 3+1 daireler"
}
// Sonuç: ["istanbul'daki", "3", "1", "daireler"]
// Problem: apostrof, stemming yok
// İterasyon 2: Apostrof + stop ekle
POST _analyze
{
"tokenizer": "standard",
"filter": [
{"type": "apostrophe"},
{"type": "lowercase", "language": "turkish"},
{"type": "stop", "stopwords": "_turkish_"}
],
"text": "İstanbul'daki 3+1 daireler"
}
// Sonuç: ["istanbul", "3", "1", "daireler"]
// İyileşme: apostrof temiz! Stemming hâlâ yok
// İterasyon 3: Stemmer ekle
POST _analyze
{
"tokenizer": "standard",
"filter": [
{"type": "apostrophe"},
{"type": "lowercase", "language": "turkish"},
{"type": "stop", "stopwords": "_turkish_"},
{"type": "stemmer", "language": "turkish"}
],
"text": "İstanbul'daki 3+1 daireler"
}
// Sonuç: ["istanbul", "3", "1", "dair"]
// İyileşme: stemming çalışıyor!Her iterasyonda bir bileşen ekleyip test edin. Tüm corpus'u her iterasyonda geçirin.
6.4 Adım 4: Edge Case ve Finalize
Edge case'leri (e-posta, tireli kelimeler, URL'ler) test edin. uax_url_email tokenizer e-postaları bölmeden korur. Tireli kelimeler (e-ticaret) standard tokenizer'da bölünür — char filter mapping ile düzeltilebilir. Sonuçlardan memnunsanız, analyzer'ı index settings'e yazın.
7. Normalizer Test — Keyword Field'lar İçin
keyword field'lar analiz edilmez ama normalizer ile basit dönüşümler yapılabilir:
PUT normalizer_test
{
"settings": {
"analysis": {
"normalizer": {
"turkish_normalizer": {
"type": "custom",
"filter": [{"type": "lowercase", "language": "turkish"}]
}
}
}
},
"mappings": {
"properties": {
"category": {
"type": "keyword",
"normalizer": "turkish_normalizer"
}
}
}
}Normalizer testi:
POST normalizer_test/_analyze
{
"normalizer": "turkish_normalizer",
"text": "ELEKTRONİK"
}
// Sonuç: "elektronik" — keyword olarak saklanır ama küçük harfe çevrilmiş⚠️ Dikkat: Normalizer'a tokenizer ekleyemezsiniz. Sadece char_filter ve token_filter kullanabilirsiniz. Metin bölünmez — tek token olarak kalır.
8. Analyzer Benchmark — Performans Karşılaştırma
8.1 Basit Benchmark Yaklaşımı
Analyzer performansını ölçmek için büyük bir metin seti üzerinde _analyze çağrıları yapın:
// Basit performans testi — Kibana Console'da
// 1000 dokümanı indexleyip süreyi ölçmek daha gerçekçidir
// Analyzer 1: Standard
POST _analyze
{
"analyzer": "standard",
"text": "Bu çok uzun bir metin olsun ki tokenizer ve filter performansını ölçebilelim. Elasticsearch güçlü bir arama motorudur ve binlerce dokümanı saniyeler içinde tarar."
}
// Analyzer 2: Turkish (daha ağır)
POST _analyze
{
"analyzer": "turkish",
"text": "Bu çok uzun bir metin olsun ki tokenizer ve filter performansını ölçebilelim. Elasticsearch güçlü bir arama motorudur ve binlerce dokümanı saniyeler içinde tarar."
}8.2 Token Sayısı Karşılaştırması
Token sayısı doğrudan index boyutunu etkiler. Bir benchmark index'i oluşturup test edin:
// Token sayıları — "Elasticsearch" kelimesi için: POST benchmark_index/_analyze { "analyzer": "standard_only", "text": "Elasticsearch" } // 1 token: ["elasticsearch"]
POST benchmark_index/_analyze { "analyzer": "with_ngram", "text": "Elasticsearch" } // ~30 token: ["el","ela","elas","las","last",...] — çok fazla!
POST benchmark_index/_analyze { "analyzer": "with_edge_ngram", "text": "Elasticsearch" } // 7 token: ["el","ela","elas","elast","elasti","elastic","elastics"]
| Analyzer | "Elasticsearch" token sayısı | Index boyutu etkisi |
|----------|------------------------------|---------------------|
| standard | 1 | 1x |
| edge_ngram (2-8) | 7 | ~7x |
| ngram (2-4) | ~30 | ~30x |
Gerçekçi benchmark için 10.000+ dokümanı farklı analyzer'larla indexleyip `GET my_index/_stats/store` ile `size_in_bytes` karşılaştırın. Indexing hızı farkı genelde %10-30 arasıdır.
---
## 9. Multi-field Analyzer Testi
Bir field'ın birden fazla analyzer ile nasıl çalıştığını test etmek:
```json
PUT multi_analyzer_test
{
"settings": {
"analysis": {
"filter": {
"tr_lower": { "type": "lowercase", "language": "turkish" },
"tr_stem": { "type": "stemmer", "language": "turkish" },
"tr_stop": { "type": "stop", "stopwords": "_turkish_" },
"edge_filter": { "type": "edge_ngram", "min_gram": 2, "max_gram": 10 }
},
"char_filter": {
"diacritics": {
"type": "mapping",
"mappings": [
"ş => s", "ç => c", "ğ => g",
"ü => u", "ö => o", "ı => i"
]
}
},
"analyzer": {
"tr_full": {
"type": "custom",
"tokenizer": "standard",
"filter": ["tr_lower", "tr_stop", "tr_stem"]
},
"tr_folded": {
"type": "custom",
"char_filter": ["diacritics"],
"tokenizer": "standard",
"filter": ["lowercase", "tr_stop", "tr_stem"]
},
"tr_autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": ["tr_lower", "edge_filter"]
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "tr_full",
"fields": {
"folded": { "type": "text", "analyzer": "tr_folded" },
"autocomplete": {
"type": "text",
"analyzer": "tr_autocomplete",
"search_analyzer": "standard"
},
"raw": { "type": "keyword" }
}
}
}
}
}
// Her sub-field'ın token'larını görmek
POST multi_analyzer_test/_analyze
{ "field": "title", "text": "Şehirdeki evlerin güzelliği" }
// tr_full: ["şehir", "ev", "güzel"]
POST multi_analyzer_test/_analyze
{ "field": "title.folded", "text": "Şehirdeki evlerin güzelliği" }
// tr_folded: ["sehir", "ev", "guzel"]
POST multi_analyzer_test/_analyze
{ "field": "title.autocomplete", "text": "Şehirdeki" }
// tr_autocomplete: ["şe", "şeh", "şehi", "şehir", "şehird", ...]10. Yaygın Debug Senaryoları
Senaryo 1: match_phrase Çalışmıyor
// Doküman: "güçlü bir arama motoru"
// Sorgu: match_phrase "güçlü arama"
// Sonuç: BOŞ
// Neden? match_phrase ardışık pozisyon ister
POST my_index/_analyze
{
"field": "description",
"text": "güçlü bir arama motoru",
"explain": true
}
// güçlü: pos 0, bir: pos 1 (stop filter silse bile), arama: pos 2
// "güçlü arama" → pos 0,1 bekler ama gerçekte pos 0,2
// Çözüm: slop parametresi ekleyin
GET my_index/_search
{
"query": {
"match_phrase": {
"description": {
"query": "güçlü arama",
"slop": 1
}
}
}
}Senaryo 2: Synonym Çalışmıyor
// "araba" ararken "otomobil" bulunamıyor
// Debug: synonym filter sırası kontrol
POST my_index/_analyze
{
"analyzer": "my_search_analyzer",
"text": "araba",
"explain": true
}
// explain çıktısında synonym filter'ın token'ı genişletip genişletmediğini kontrol edin
// Eğer stemmer synonym'den ÖNCE geliyorsa, "araba" → "arab" oluyor
// ve "arab" synonym listesinde yok!Senaryo 3: Case Sensitivity Sorunu
// "IŞIK" araması "ışık" bulamıyor
POST my_index/_analyze
{
"field": "title",
"text": "IŞIK"
}
// Eğer standard lowercase kullanıyorsanız: ["isik"] — yanlış!
// Turkish lowercase gerekli: ["ışık"] — doğru!11. Java ile Analyzer Testi
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.indices.AnalyzeRequest;
import co.elastic.clients.elasticsearch.indices.AnalyzeResponse;
import co.elastic.clients.elasticsearch.indices.analyze.AnalyzeToken;
// Basit analyzer testi
AnalyzeResponse response = client.indices().analyze(a -> a
.analyzer("turkish")
.text("İstanbul'daki evlerin fiyatları arttı mı?")
);
System.out.println("=== Token Listesi ===");
for (AnalyzeToken token : response.tokens()) {
System.out.printf(" [%d] %-15s (offset: %d-%d, type: %s)%n",
token.position(),
token.token(),
token.startOffset(),
token.endOffset(),
token.type()
);
}
// Index-specific analyzer testi
AnalyzeResponse indexResponse = client.indices().analyze(a -> a
.index("my_index")
.field("title")
.text("Test metni")
);
// Explain ile detaylı analiz
AnalyzeResponse explainResponse = client.indices().analyze(a -> a
.analyzer("standard")
.text("Quick Brown Fox")
.explain(true)
);
// explainResponse.detail() ile adım adım bilgi alabilirsinizKarşılaştırma helper metodu:
public static void compareAnalyzers(
ElasticsearchClient client,
String indexName,
String[] analyzerNames,
String text
) throws Exception {
System.out.println("Metin: \"" + text + "\"");
System.out.println("─".repeat(60));
for (String analyzer : analyzerNames) {
AnalyzeResponse resp = client.indices().analyze(a -> a
.index(indexName)
.analyzer(analyzer)
.text(text)
);
String tokens = resp.tokens().stream()
.map(AnalyzeToken::token)
.reduce((a1, b) -> a1 + ", " + b)
.orElse("(boş)");
System.out.printf(" %-25s → [%s] (%d token)%n",
analyzer, tokens, resp.tokens().size());
}
}
// Kullanım
compareAnalyzers(client, "analyzer_lab",
new String[]{"v1_basic", "v2_with_stop", "v3_with_stem"},
"Türkiye'deki büyük şehirlerin bir listesi"
);12. Best Practices
✅ Yapın
| Uygulama | Neden |
|---|---|
Her analyzer değişikliğinden sonra _analyze ile test edin | Beklentinizi doğrularsınız |
explain: true kullanın | Her adımda ne olduğunu görürsünüz |
| Test corpus'u hazırlayın (10+ örnek) | Edge case'leri önceden yakalarsınız |
| Index-time VE search-time'ı ayrı test edin | Eşleşme sorunlarını önlersiniz |
| Analyzer lab index'i oluşturun | Hızlı deney yapabilirsiniz |
❌ Yapmayın
| Uygulama | Neden |
|---|---|
| Analyzer'ı test etmeden production'a almayın | Debug çok zor olur |
| Tek bir metinle test edip bırakmayın | O metin çalışır, edge case patlar |
| Token sayısını görmezden gelmeyin | 30 token/kelime = index boyutu felaketi |
| field parametresini search-time testi sanmayın | Her zaman index-time analyzer'ı döndürür |
Özet
`_analyze` API Elasticsearch'ün en önemli debug aracıdır — her zaman kullanın
`explain: true` parametresi her token'ın hangi adımdan geçtiğini gösterir
Index-time vs search-time analyzer'ı ayrı test edin —
fieldparametresi her zaman index-time döndürür`_explain` API bir dokümanın neden belli bir skor aldığını açıklar (TF, IDF, boost)
Profile API sorgu performansını bileşen bazında ölçer — yavaş kısmı bulursunuz
Analyzer lab index'i oluşturup iteratif geliştirme yapın — tek seferde doğru yazmaya çalışmayın
Token sayısı doğrudan index boyutunu etkiler — ngram vs edge_ngram farkı 4x olabilir
AI Asistan
Sorularını yanıtlamaya hazır