Text Analysis Pipeline — Tokenizer, Filter, Char Filter
Giriş — Bir Mektubun Yolculuğu
Bir postaneye mektup gönderdiğinizi düşünün. Mektup önce zarftan çıkarılır ve üzerindeki lekeler temizlenir (character filter). Sonra kelime kelime ayrılıp etiketlenir (tokenizer). En sonunda da her kelime standart bir formata sokulur — büyük harfler küçültülür, gereksiz ekler atılır (token filter). İşte Elasticsearch'ün metinleri arama için hazırlarken yaptığı şey tam olarak budur.
Elasticsearch'te bir text alanına veri yazdığınızda, o metin doğrudan saklanmaz. Önce bir analysis pipeline'dan geçer. Bu pipeline'ın çıktısı, inverted index'e yazılan token'lardır. Aynı şekilde siz bir arama yaptığınızda, arama metniniz de (varsayılan olarak) aynı pipeline'dan geçer. İşte bu yüzden "Elasticsearch" yazdığınızda "elasticsearch" yazılmış bir dokümanı bulabilirsiniz — ikisi de aynı token'a dönüşür.
Bu ders, text analysis pipeline'ın üç katmanını derinlemesine inceleyecek. Bu kavramı anlamadan custom analyzer yazmak, mapping tasarlamak ya da arama kalitesini artırmak imkansızdır.
1. Text Analysis Nedir?
Text analysis, ham metni aranabilir token'lara dönüştürme sürecidir. Elasticsearch'te bu süreç her text tipindeki alana uygulanır.
Pipeline'ın Üç Katmanı
Ham Metin
│
▼
┌─────────────────────┐
│ Character Filters │ ← Karakter seviyesinde dönüşüm
│ (0 veya daha fazla) │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Tokenizer │ ← Metni token'lara böler
│ (tam olarak 1 adet) │
└─────────┬───────────┘
│
▼
┌─────────────────────┐
│ Token Filters │ ← Token seviyesinde dönüşüm
│ (0 veya daha fazla) │
└─────────────────────┘
│
▼
Token'lar → Inverted IndexKritik kural: Bir analyzer tam olarak 1 tokenizer içermek zorundadır. Character filter ve token filter ise opsiyoneldir — 0, 1 veya birden fazla olabilir.
Neden Önemli?
Eğer analysis sürecini anlamazsanız, şu sorunlarla karşılaşırsınız:
"iPhone" araması "iphone" sonuçlarını getirmez
Türkçe "çalışıyor" araması "çalış" kökünü bulamaz
HTML içerikli alanlar
<b>gibi etiketleri aranabilir token olarak saklarE-posta adresleri yanlış yerlerde bölünür
2. Character Filters — Karakter Düzeyinde Temizlik
Character filter'lar, tokenizer'a gitmeden önce ham metin üzerinde karakter düzeyinde dönüşüm yapar. HTML etiketlerini temizlemek, özel karakterleri dönüştürmek veya karakter eşlemeleri yapmak için kullanılır.
2.1 Built-in Character Filters
Elasticsearch üç adet built-in character filter sunar:
a) html_strip — HTML Etiketlerini Temizler
Web scraping verileri ya da CMS içerikleri genellikle HTML etiketleri içerir. Bu filter onları temizler:
POST _analyze
{
"char_filter": ["html_strip"],
"tokenizer": "standard",
"text": "<p>Elasticsearch <b>çok</b> hızlı bir <em>arama motoru</em>dur.</p>"
}Çıktı token'ları: ["elasticsearch", "çok", "hızlı", "bir", "arama", "motoru", "dur"]
HTML entity'leri de çözümlenir:
POST _analyze
{
"char_filter": ["html_strip"],
"tokenizer": "keyword",
"text": "Fiyat: 100 & 200 €"
}Çıktı: "Fiyat: 100 & 200 €"
b) mapping — Karakter Eşleme
Belirli karakterleri başka karakterlere dönüştürür. Özellikle özel semboller veya kısaltmalar için kullanışlıdır:
PUT my_mapping_index
{
"settings": {
"analysis": {
"char_filter": {
"emoticon_filter": {
"type": "mapping",
"mappings": [
":) => _mutlu_",
":( => _uzgun_",
"<3 => _kalp_",
"& => ve"
]
}
},
"analyzer": {
"emoticon_analyzer": {
"type": "custom",
"char_filter": ["emoticon_filter"],
"tokenizer": "standard",
"filter": ["lowercase"]
}
}
}
}
}Test edelim:
POST my_mapping_index/_analyze
{
"analyzer": "emoticon_analyzer",
"text": "Bugün hava güzel :) ve güneşli <3"
}Çıktı token'ları: ["bugün", "hava", "güzel", "_mutlu_", "ve", "güneşli", "_kalp_"]
Dikkat edin: :) artık _mutlu_ token'ına dönüştü ve aranabilir hale geldi!
c) pattern_replace — Regex ile Değiştirme
Regular expression kullanarak karakter dönüşümü yapar:
PUT my_pattern_index
{
"settings": {
"analysis": {
"char_filter": {
"phone_normalizer": {
"type": "pattern_replace",
"pattern": "[\\-\\(\\)\\s]",
"replacement": ""
}
},
"analyzer": {
"phone_analyzer": {
"type": "custom",
"char_filter": ["phone_normalizer"],
"tokenizer": "keyword",
"filter": []
}
}
}
}
}Test:
POST my_pattern_index/_analyze
{
"analyzer": "phone_analyzer",
"text": "+90 (532) 123-45-67"
}Çıktı: ["+905321234567"]
Telefon numarası boşluk, tire ve parantezden arındı — artık hangi formatta yazılırsa yazılsın aynı token'a dönüşecek.
2.2 Birden Fazla Character Filter Zinciri
Character filter'lar sıralı çalışır — biri diğerinin çıktısını alır:
PUT multi_char_index
{
"settings": {
"analysis": {
"char_filter": {
"html_cleaner": {
"type": "html_strip"
},
"special_chars": {
"type": "mapping",
"mappings": [
"& => ve",
"@ => at"
]
}
},
"analyzer": {
"clean_analyzer": {
"type": "custom",
"char_filter": ["html_cleaner", "special_chars"],
"tokenizer": "standard",
"filter": ["lowercase"]
}
}
}
}
}Sıra önemlidir: Önce HTML temizlenir, sonra & → ve dönüşümü yapılır. Sırayı tersine çevirirseniz, & entity'si henüz çözümlenmediği için mapping çalışmaz.
3. Tokenizer — Metni Token'lara Bölme
Tokenizer, analysis pipeline'ın kalbidir. Character filter'lardan geçmiş metni alır ve onu parçalara (token'lara) böler. Her analyzer tam olarak bir tokenizer içerir.
3.1 Word-Oriented Tokenizer'lar
a) standard Tokenizer
En yaygın kullanılan tokenizer. Unicode Text Segmentation algoritmasını kullanır, çoğu dilde iyi çalışır:
POST _analyze
{
"tokenizer": "standard",
"text": "Elasticsearch'ün 8.x sürümü çok güçlü!"
}Çıktı: ["Elasticsearch'ün", "8.x", "sürümü", "çok", "güçlü"]
Noktalama işaretlerini kaldırır ama apostrof ve nokta gibi kelime içi karakterleri korur.
b) letter Tokenizer
Sadece harflere göre böler — harf olmayan her karakter ayırıcıdır:
POST _analyze
{
"tokenizer": "letter",
"text": "user@email.com IP:192.168.1.1"
}Çıktı: ["user", "email", "com", "IP", "x"] — rakamlar bile ayırıcı olarak işlenir (beklenti dışı olabilir).
c) whitespace Tokenizer
Yalnızca boşluk karakterlerine göre böler — noktalama, özel karakter dahil her şeyi korur:
POST _analyze
{
"tokenizer": "whitespace",
"text": "Fiyat: $99.99 (KDV dahil)"
}Çıktı: ["Fiyat:", "$99.99", "(KDV", "dahil)"]
Parantez ve iki nokta token'ın parçası olarak kalır. Bu genellikle istenmeyen bir durumdur, ama log analizi gibi bazı senaryolarda faydalıdır.
d) uax_url_email Tokenizer
URL ve e-posta adreslerini bölmeden tek token olarak tanır:
POST _analyze
{
"tokenizer": "uax_url_email",
"text": "Bize info@example.com adresinden veya https://example.com/contact sayfasından ulaşın."
}Çıktı: ["Bize", "info@example.com", "adresinden", "veya", "https://example.com/contact", "sayfasından", "ulaşın"]
3.2 Partial Word Tokenizer'lar
a) ngram Tokenizer
Metni n-gram'lara böler. Autocomplete veya fuzzy matching için kullanılır:
POST _analyze
{
"tokenizer": {
"type": "ngram",
"min_gram": 3,
"max_gram": 5,
"token_chars": ["letter", "digit"]
},
"text": "Arama"
}Çıktı: ["Ara", "Aram", "Arama", "ram", "rama", "ama"]
⚠️ Dikkat: N-gram tokenizer index boyutunu ciddi şekilde artırır. min_gram ile max_gram arasındaki fark büyüdükçe token sayısı patlar.
b) edge_ngram Tokenizer
Sadece kelimenin başından itibaren n-gram oluşturur. Autocomplete (search-as-you-type) için idealdir:
POST _analyze
{
"tokenizer": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 8,
"token_chars": ["letter", "digit"]
},
"text": "Elasticsearch"
}Çıktı: ["El", "Ela", "Elas", "Elast", "Elasti", "Elastic", "Elastics"]
Kullanıcı "Ela" yazdığında bile "Elasticsearch" bulunur.
3.3 Structured Text Tokenizer'lar
a) keyword Tokenizer
Metni hiç bölmez — tüm girdiyi tek token olarak döndürür:
POST _analyze
{
"tokenizer": "keyword",
"text": "New York City"
}Çıktı: ["New York City"]
Normalde tam eşleşme için keyword field type kullanılır, ama bazı durumlarda keyword tokenizer + lowercase filter kombinasyonu tercih edilir.
b) pattern Tokenizer
Regex pattern'a göre böler:
POST _analyze
{
"tokenizer": {
"type": "pattern",
"pattern": "[,;|]"
},
"text": "elma,armut;portakal|muz"
}Çıktı: ["elma", "armut", "portakal", "muz"]
c) path_hierarchy Tokenizer
Dosya yolu yapılarını hiyerarşik olarak token'lara böler:
POST _analyze
{
"tokenizer": "path_hierarchy",
"text": "/var/log/elasticsearch/cluster.log"
}Çıktı: ["/var", "/var/log", "/var/log/elasticsearch", "/var/log/elasticsearch/cluster.log"]
Bu sayede /var/log araması, alt dizinlerdeki tüm dosyaları bulur.
4. Token Filters — Token Düzeyinde Dönüşüm
Token filter'lar, tokenizer'dan çıkan token'ları alır ve üzerlerinde çeşitli dönüşümler yapar: küçük harfe çevirme, köke indirgeme (stemming), eşanlamlı ekleme, durdurma kelimeleri çıkarma vb.
4.1 Temel Token Filters
a) lowercase
Tüm harfleri küçük harfe çevirir:
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase"],
"text": "Elasticsearch GÜÇLÜ bir Arama Motoru"
}Çıktı: ["elasticsearch", "güçlü", "bir", "arama", "motoru"]
b) uppercase
Tüm harfleri büyük harfe çevirir (nadiren kullanılır):
POST _analyze
{
"tokenizer": "standard",
"filter": ["uppercase"],
"text": "hello world"
}Çıktı: ["HELLO", "WORLD"]
c) stop — Durdurma Kelimeleri
"ve", "bir", "bu" gibi anlamsız kelimeleri kaldırır:
POST _analyze
{
"tokenizer": "standard",
"filter": [
"lowercase",
{
"type": "stop",
"stopwords": ["ve", "bir", "bu", "ile", "için", "de", "da"]
}
],
"text": "Bu bir Elasticsearch ve Kibana için güzel bir kaynak"
}Çıktı: ["elasticsearch", "kibana", "güzel", "kaynak"]
Durdurma kelimeleri çıkarılınca geriye sadece anlamlı terimler kalır.
d) stemmer — Kök Bulma
Kelimeleri köklerine indirger:
POST _analyze
{
"tokenizer": "standard",
"filter": [
"lowercase",
{
"type": "stemmer",
"language": "turkish"
}
],
"text": "çalışanlar çalışıyorlardı çalışmak"
}Çıktı: ["çalış", "çalış", "çalış"] — Hepsi aynı köke indirgendi.
4.2 İleri Token Filters
a) synonym — Eşanlamlılar
Eşanlamlı kelimeleri birbirine bağlar:
PUT synonym_index
{
"settings": {
"analysis": {
"filter": {
"my_synonyms": {
"type": "synonym",
"synonyms": [
"hızlı, çabuk, süratli",
"büyük, iri, kocaman",
"ES => Elasticsearch"
]
}
},
"analyzer": {
"synonym_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "my_synonyms"]
}
}
}
}
}Test:
POST synonym_index/_analyze
{
"analyzer": "synonym_analyzer",
"text": "ES çok hızlı"
}"hızlı" araması artık "çabuk" veya "süratli" içeren dokümanları da bulur.
b) trim — Boşluk Temizleme
Token'ların başındaki ve sonundaki boşlukları temizler:
POST _analyze
{
"tokenizer": "keyword",
"filter": ["trim"],
"text": " Elasticsearch "
}Çıktı: ["Elasticsearch"]
c) unique — Tekrar Eden Token'ları Kaldırma
Aynı pozisyondaki tekrar eden token'ları kaldırır:
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase", "unique"],
"text": "hızlı hızlı çok hızlı"
}Çıktı: ["hızlı", "çok"]
d) truncate — Token Uzunluğu Sınırlama
Token'ları belirli bir uzunlukta keser:
POST _analyze
{
"tokenizer": "standard",
"filter": [
{
"type": "truncate",
"length": 5
}
],
"text": "Elasticsearch Analizi"
}Çıktı: ["Elast", "Anali"]
e) length — Uzunluğa Göre Filtreleme
Belirli uzunluk aralığında olmayan token'ları tamamen kaldırır:
POST _analyze
{
"tokenizer": "standard",
"filter": [
{
"type": "length",
"min": 3,
"max": 10
}
],
"text": "Bu bir ES arama motoru uygulamasıdır"
}Çıktı: ["bir", "arama", "motoru"] — 3'ten kısa ve 10'dan uzun token'lar çıkarıldı.
4.3 Token Filter Sıralaması
Token filter'lar sıralı çalışır ve sıralama sonucu etkiler:
// ❌ Yanlış sıralama — önce stemmer, sonra stop
"filter": ["stemmer", "stop"]
// ✅ Doğru sıralama — önce stop, sonra stemmer
"filter": ["lowercase", "stop", "stemmer"]Neden? Eğer stemmer önce çalışırsa, "running" kelimesini "run" yapabilir ama "the" kelimesi hâlâ orada kalır ve stop filter onu yakalayamayabilir (çünkü stemmer "the" gibi kısa kelimeleri değiştirmez — ama daha karmaşık dillerde fark yaratabilir).
Genel doğru sıralama:
lowercase— Önce hepsini küçük harfe çevirstop— Durdurma kelimelerini çıkarstemmer— Kalan kelimeleri köke indirgesynonym— Eşanlamlıları ekle (genelde en sonda)
5. _analyze API — Pipeline'ı Test Etme
Elasticsearch, analysis sürecini test etmek için güçlü bir _analyze API sunar. Bu API'yi anlamak, debug sürecinin temelidir.
5.1 Basit Kullanım
Tokenizer ile:
POST _analyze
{
"tokenizer": "standard",
"text": "Merhaba Dünya!"
}Analyzer ile:
POST _analyze
{
"analyzer": "standard",
"text": "Merhaba Dünya!"
}5.2 Index-Specific Analyzer Test
Bir index'e tanımlı custom analyzer'ı test etmek:
POST my_index/_analyze
{
"analyzer": "my_custom_analyzer",
"text": "Test metni"
}Veya belirli bir field'ın analyzer'ını test etmek:
POST my_index/_analyze
{
"field": "title",
"text": "Bu metni title field'ının analyzer'ı ile analiz et"
}5.3 Detaylı Çıktı
explain: true parametresi ile her adımı detaylı görürsünüz:
POST _analyze
{
"tokenizer": "standard",
"filter": ["lowercase", "stop"],
"text": "The Quick Brown Fox",
"explain": true
}Bu çıktı, her token'ın hangi filter'dan nasıl geçtiğini adım adım gösterir. Debug için çok değerlidir.
6. Index-Time vs Search-Time Analysis
Analysis iki farklı anda gerçekleşir ve bu ayrımı anlamak kritik öneme sahiptir.
Index-Time Analysis
Bir doküman index'lenirken, text alanlarındaki değerler analyzer'dan geçirilir ve inverted index'e token'lar olarak yazılır:
Orijinal: "Elasticsearch çok hızlı bir arama motoru"
Index-time analysis sonucu (inverted index):
- elasticsearch → doc1
- çok → doc1
- hızlı → doc1
- bir → doc1
- arama → doc1
- motoru → doc1Search-Time Analysis
Bir arama yapıldığında, sorgu metni de aynı (veya farklı) analyzer'dan geçer:
Sorgu: "Elasticsearch Arama"
Search-time analysis sonucu: ["elasticsearch", "arama"]Inverted index'te bu token'lar aranır ve doc1 bulunur.
Farklı Analyzer Kullanımı
Bazen index-time ve search-time'da farklı analyzer kullanmak gerekir:
PUT my_index
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "my_index_analyzer",
"search_analyzer": "my_search_analyzer"
}
}
}
}Tipik kullanım senaryosu — Autocomplete:
Index-time: Edge ngram analyzer kullanılır → "Elastic" →
["El", "Ela", "Elas", "Elast", "Elasti", "Elastic"]Search-time: Standard analyzer kullanılır → "Ela" →
["ela"]"ela"token'ı, index'teki"Ela"token'ıyla eşleşir ✓
Eğer search-time'da da edge ngram kullansaydınız, "Elasticsearch" araması "El", "Ela" vb. token'lara bölünür ve istenmeyen sonuçlar gelirdi.
7. Java ile Analysis API
Bölüm 8'de Java client detaylı işlenecek, ama analysis API'nin Java karşılığını görelim:
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;
// Analyzer test etme
AnalyzeRequest request = AnalyzeRequest.of(a -> a
.analyzer("standard")
.text("Elasticsearch çok hızlı bir arama motoru")
);
AnalyzeResponse response = client.indices().analyze(request);
for (AnalyzeToken token : response.tokens()) {
System.out.printf("Token: %-20s | Pozisyon: %d | Offset: %d-%d%n",
token.token(),
token.position(),
token.startOffset(),
token.endOffset()
);
}Custom analyzer ile index oluşturma:
import co.elastic.clients.elasticsearch.indices.*;
import co.elastic.clients.elasticsearch.indices.IndexSettingsAnalysis;
// Custom analyzer tanımlı index oluşturma
client.indices().create(c -> c
.index("products_tr")
.settings(s -> s
.analysis(a -> a
.charFilter("html_cleaner", cf -> cf
.definition(d -> d
.htmlStrip(h -> h)
)
)
.analyzer("turkish_analyzer", an -> an
.custom(cu -> cu
.tokenizer("standard")
.charFilter("html_cleaner")
.filter("lowercase", "turkish_stop", "turkish_stemmer")
)
)
.filter("turkish_stop", f -> f
.definition(d -> d
.stop(st -> st.stopwords("_turkish_"))
)
)
.filter("turkish_stemmer", f -> f
.definition(d -> d
.stemmer(st -> st.language("turkish"))
)
)
)
)
.mappings(m -> m
.properties("description", p -> p
.text(t -> t.analyzer("turkish_analyzer"))
)
)
);8. Gerçek Dünya Örneği: E-Ticaret Ürün Arama
Bir e-ticaret sitesinin ürün açıklamalarını doğru şekilde index'lemek için tam bir pipeline oluşturalım:
PUT ecommerce_products
{
"settings": {
"analysis": {
"char_filter": {
"html_cleaner": {
"type": "html_strip"
},
"special_symbols": {
"type": "mapping",
"mappings": [
"& => ve",
"% => yüzde",
"₺ => TL",
"+ => artı"
]
}
},
"tokenizer": {
"product_tokenizer": {
"type": "standard",
"max_token_length": 50
}
},
"filter": {
"turkish_stop": {
"type": "stop",
"stopwords": "_turkish_"
},
"turkish_stemmer": {
"type": "stemmer",
"language": "turkish"
},
"product_length": {
"type": "length",
"min": 2,
"max": 30
}
},
"analyzer": {
"product_analyzer": {
"type": "custom",
"char_filter": ["html_cleaner", "special_symbols"],
"tokenizer": "product_tokenizer",
"filter": [
"lowercase",
"turkish_stop",
"turkish_stemmer",
"product_length"
]
}
}
}
},
"mappings": {
"properties": {
"product_name": {
"type": "text",
"analyzer": "product_analyzer"
},
"description": {
"type": "text",
"analyzer": "product_analyzer"
},
"category": {
"type": "keyword"
},
"price": {
"type": "float"
}
}
}
}Pipeline'ı test edelim:
POST ecommerce_products/_analyze
{
"analyzer": "product_analyzer",
"text": "<p>Samsung Galaxy S24 Ultra <b>akıllı telefon</b> & kılıfı — 256GB depolama</p>"
}Adım adım:
html_cleaner: HTML etiketleri temizlenir →
"Samsung Galaxy S24 Ultra akıllı telefon & kılıfı — 256GB depolama"special_symbols:
&→vedönüşür →"Samsung Galaxy S24 Ultra akıllı telefon ve kılıfı — 256GB depolama"product_tokenizer: Kelimelere bölünür
lowercase: Küçük harfe çevrilir
turkish_stop: Durdurma kelimeleri çıkarılır (
ve)turkish_stemmer: Köklerine indirgenir (
kılıfı→kılıf,akıllı→akıl)product_length: 2'den kısa token'lar çıkarılır
Veri ekleyip test edelim:
POST ecommerce_products/_doc/1
{
"product_name": "Samsung Galaxy S24 Ultra",
"description": "<p>Samsung Galaxy S24 Ultra <b>akıllı telefon</b> & kılıfı — 256GB depolama</p>",
"category": "Elektronik",
"price": 54999.99
}
POST ecommerce_products/_doc/2
{
"product_name": "Apple iPhone 15 Pro Max",
"description": "<p>Apple iPhone 15 Pro Max <em>akıllı telefon</em> — 512GB depolama alanı</p>",
"category": "Elektronik",
"price": 64999.99
}
GET ecommerce_products/_search
{
"query": {
"match": {
"description": "akıllı telefonlar"
}
}
}"akıllı telefonlar" araması → stemmer sayesinde "akıl" ve "telefon" köklerini bulur → her iki dokümanı da döndürür.
9. Best Practices
✅ Yapın
| Uygulama | Neden |
|---|---|
_analyze API ile her zaman test edin | Beklenmedik token'ları önceden yakalarsınız |
| Filter sıralamasına dikkat edin | lowercase → stop → stemmer sırası idealdir |
HTML içerik varsa html_strip kullanın | Etiketler aranabilir token olmamalı |
| Index-time ve search-time analyzer'ı ayrı düşünün | Autocomplete gibi senaryolarda farklı analyzer gerekir |
| Token filter'ları minimum tutun | Her eklenen filter index boyutunu ve performansı etkiler |
❌ Yapmayın
| Uygulama | Neden |
|---|---|
| Aynı field'a hem ngram hem stemmer uygulamayın | Token patlaması yaratır, index büyür |
max_gram değerini çok yüksek tutmayın | N-gram ile 20+ gram = performans felaketi |
| Stop words listesini aşırı genişletmeyin | Bazı "durdurma" kelimeleri anlamlı sorgu parçası olabilir |
| Synonym'leri sadece index-time'da kullanmayın | Yeni eşanlamlı ekleyince reindex gerekir; synonym_graph + search-time düşünün |
10. Yaygın Hatalar ve Çözümleri
Hata 1: Analyzer Tanımlamayı Unutmak
// ❌ Varsayılan standard analyzer kullanılır
PUT my_index
{
"mappings": {
"properties": {
"content": { "type": "text" }
}
}
}Türkçe içerik varsa, standard analyzer yetersiz kalır — stemming ve stop words uygulanmaz.
// ✅ Türkçe analyzer belirtin
PUT my_index
{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "turkish"
}
}
}
}Hata 2: Keyword Field'a Analyzer Uygulamak
// ❌ keyword tipi zaten analiz edilmez, analyzer anlamsız
"email": {
"type": "keyword",
"analyzer": "standard"
}// ✅ keyword tipi olduğu gibi saklanır — normalizer kullanabilirsiniz
"email": {
"type": "keyword",
"normalizer": "lowercase_normalizer"
}Hata 3: Character Filter ve Token Filter Karıştırmak
Character filter karakter seviyesinde, tokenizer'dan önce çalışır. Token filter token seviyesinde, tokenizer'dan sonra çalışır. HTML temizleme → character filter. Küçük harfe çevirme → token filter.
Hata 4: Token Sayısı Limitini Bilmemek
Elasticsearch varsayılan olarak bir field'da maksimum 10.000 token kabul eder. Çok uzun metinler kesilir:
// Limiti artırma (dikkatli kullanın)
PUT my_index/_settings
{
"index.analyze.max_token_count": 20000
}💡 İpucu: Limiti artırmak yerine, metni daha kısa parçalara bölmeyi düşünün.
11. Performans Notları
Analiz Maliyeti
Her text field'ı index-time'da analiz edilir. Bu şu anlama gelir:
Daha fazla character filter = daha fazla CPU (ama genelde ihmal edilebilir)
Daha karmaşık tokenizer (ngram vs standard) = daha fazla token = daha büyük index
Daha fazla token filter = her token için daha fazla işlem
Bellek Kullanımı
Synonym ve stop words listeleri bellekte tutulur. Binlerce eşanlamlı tanımlıyorsanız, bellek kullanımını izleyin.
Token Sayısı ve Index Boyutu
| Tokenizer | "Elasticsearch" için token sayısı |
|---|---|
| standard | 1 |
| ngram (2-4) | 11 |
| ngram (2-8) | 32 |
| edge_ngram (2-8) | 7 |
N-gram kullanıyorsanız, index boyutunun 5-10x artmasına hazır olun.
Özet
Text analysis pipeline üç katmandan oluşur: Character Filter → Tokenizer → Token Filter
Character filter'lar karakter seviyesinde çalışır (HTML temizleme, karakter eşleme, regex)
Tokenizer metni token'lara böler — her analyzer'da tam olarak 1 adet olmalıdır
Token filter'lar token seviyesinde dönüşüm yapar (lowercase, stemmer, synonym, stop)
Filter sırası kritiktir — lowercase → stop → stemmer ideal sıralamadır
`_analyze` API pipeline'ı test etmek için vazgeçilmez araçtır — her zaman test edin
Index-time vs search-time analysis farklıdır — autocomplete gibi senaryolarda farklı analyzer kullanılır
AI Asistan
Sorularını yanıtlamaya hazır