Highlighting ve Suggesters
Giriş — Sarı Fosforlu Kalem
Üniversitede ders çalışırken önemli yerleri sarı fosforlu kalemle işaretlemiş olabilirsiniz. Bir bakışta neyin önemli olduğunu görürsünüz. Google'da arama yaptığınızda da arama teriminiz sonuçlarda kalın olarak gösterilir — bu highlighting'dir. Ayrıca "elasticseach" yazdığınızda Google "elasticsearch mi demek istediniz?" diye sorar — bu da suggestion'dır.
Bu iki özellik arama deneyimini dramatik şekilde iyileştirir. Kullanıcı hem sonuçların neden döndüğünü anlar hem de yazım hatası yapsa bile doğru sonuca ulaşır. Bu derste Elasticsearch'ün highlighting ve suggester özelliklerini derinlemesine inceleyeceğiz.
1. Highlighting — Eşleşen Metni Vurgulama
1.1 Temel Kullanım
PUT articles
{
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "turkish" },
"content": { "type": "text", "analyzer": "turkish" },
"author": { "type": "keyword" },
"tags": { "type": "keyword" }
}
}
}
POST articles/_bulk
{"index":{"_id":"1"}}
{"title":"Elasticsearch ile Full-Text Arama","content":"Elasticsearch modern web uygulamalarında arama işlevselliği için vazgeçilmez bir araçtır. Full-text search özelliği sayesinde milyonlarca doküman arasında milisaniyeler içinde arama yapabilirsiniz. Elasticsearch'ün inverted index yapısı bu hızın temel sebebidir.","author":"Ahmet Yılmaz","tags":["elasticsearch","arama"]}
{"index":{"_id":"2"}}
{"title":"Spring Boot ve Elasticsearch Entegrasyonu","content":"Spring Data Elasticsearch ile Java uygulamalarınızda Elasticsearch kullanabilirsiniz. Repository pattern sayesinde CRUD işlemleri çok kolaydır. Spring Boot auto-configuration desteği ile minimum konfigürasyon gerekir.","author":"Mehmet Kaya","tags":["spring-boot","elasticsearch","java"]}
{"index":{"_id":"3"}}
{"title":"Kibana ile Veri Görselleştirme","content":"Kibana, Elasticsearch verilerini görselleştirmek için kullanılan güçlü bir araçtır. Dashboard oluşturarak verilerinizi gerçek zamanlı izleyebilirsiniz. Elasticsearch cluster sağlığını Kibana üzerinden takip edebilirsiniz.","author":"Ayşe Demir","tags":["kibana","elasticsearch","görselleştirme"]}
GET articles/_search
{
"query": {
"match": {
"content": "elasticsearch arama"
}
},
"highlight": {
"fields": {
"content": {}
}
}
}Yanıt:
{
"hits": {
"hits": [
{
"_source": { "title": "Elasticsearch ile Full-Text Arama", ... },
"highlight": {
"content": [
"<em>Elasticsearch</em> modern web uygulamalarında <em>arama</em> işlevselliği için vazgeçilmez bir araçtır.",
"Full-text search özelliği sayesinde milyonlarca doküman arasında milisaniyeler içinde <em>arama</em> yapabilirsiniz.",
"<em>Elasticsearch</em>'ün inverted index yapısı bu hızın temel sebebidir."
]
}
}
]
}
}Eşleşen terimler <em> etiketleriyle sarıldı. Frontend'de bu etiketleri CSS ile sarı arka plan yapabilirsiniz.
1.2 Highlight Etiketlerini Özelleştirme
GET articles/_search
{
"query": {
"match": { "content": "elasticsearch" }
},
"highlight": {
"pre_tags": ["<mark>"],
"post_tags": ["</mark>"],
"fields": {
"content": {}
}
}
}Artık <mark>Elasticsearch</mark> şeklinde döner.
Farklı field'lar için farklı etiketler:
GET articles/_search
{
"query": {
"multi_match": {
"query": "elasticsearch",
"fields": ["title", "content"]
}
},
"highlight": {
"fields": {
"title": {
"pre_tags": ["<strong>"],
"post_tags": ["</strong>"]
},
"content": {
"pre_tags": ["<em>"],
"post_tags": ["</em>"]
}
}
}
}1.3 Fragment Boyutu ve Sayısı
Varsayılan olarak 100 karakterlik fragment'lar döner. Bunu ayarlayabilirsiniz:
GET articles/_search
{
"query": {
"match": { "content": "elasticsearch" }
},
"highlight": {
"fields": {
"content": {
"fragment_size": 200,
"number_of_fragments": 3,
"no_match_size": 150
}
}
}
}| Parametre | Varsayılan | Açıklama |
|---|---|---|
fragment_size | 100 | Her fragment'ın karakter uzunluğu |
number_of_fragments | 5 | Döndürülecek fragment sayısı |
no_match_size | 0 | Eşleşme yoksa kaç karakter döndürülür |
order | none | "score" ile en alakalı fragment önce |
1.4 Tüm Alanı Döndürme (Fragment Yok)
Kısa alanlar (başlık gibi) için fragment'lama istemeyebilirsiniz:
GET articles/_search
{
"query": {
"match": { "title": "elasticsearch" }
},
"highlight": {
"fields": {
"title": {
"number_of_fragments": 0
}
}
}
}number_of_fragments: 0 tüm alanı tek parça olarak döndürür.
2. Highlighter Tipleri
Elasticsearch üç farklı highlighter tipi sunar:
2.1 unified (Varsayılan — Önerilen)
GET articles/_search
{
"query": {
"match": { "content": "elasticsearch arama" }
},
"highlight": {
"type": "unified",
"fields": {
"content": {}
}
}
}BM25 ile fragment scoring yapar
Phrase ve multi-term highlight desteği
En çok önerilen tip
2.2 plain
GET articles/_search
{
"query": {
"match": { "content": "elasticsearch" }
},
"highlight": {
"type": "plain",
"fields": {
"content": {}
}
}
}Standard Lucene highlighter
Basit senaryolarda iyi çalışır
Büyük dokümanlar için yavaş olabilir
2.3 fvh (Fast Vector Highlighter)
PUT fvh_articles
{
"mappings": {
"properties": {
"content": {
"type": "text",
"term_vector": "with_positions_offsets"
}
}
}
}
GET fvh_articles/_search
{
"query": {
"match": { "content": "elasticsearch" }
},
"highlight": {
"type": "fvh",
"fields": {
"content": {}
}
}
}term_vector: with_positions_offsetsgerektirirBüyük dokümanlar için en hızlı
Daha fazla disk alanı kullanır (term vector saklanır)
Highlighter Karşılaştırması
| Özellik | unified | plain | fvh |
|---|---|---|---|
| Hız (küçük dok.) | Hızlı | Hızlı | Hızlı |
| Hız (büyük dok.) | İyi | Yavaş | Çok hızlı |
| Disk kullanımı | Normal | Normal | Yüksek |
| Phrase highlight | ✅ | ✅ | ✅ |
| Önerilen | ✅ | Basit durumlar | Büyük dokümanlar |
3. Gelişmiş Highlight Özellikleri
3.1 Birden Fazla Field'da Highlight
GET articles/_search
{
"query": {
"multi_match": {
"query": "elasticsearch java",
"fields": ["title^2", "content"]
}
},
"highlight": {
"pre_tags": ["<mark>"],
"post_tags": ["</mark>"],
"fields": {
"title": {
"number_of_fragments": 0
},
"content": {
"fragment_size": 150,
"number_of_fragments": 2
}
}
}
}3.2 require_field_match
Varsayılan olarak highlight sadece sorguyla eşleşen field'larda çalışır. Bunu değiştirebilirsiniz:
GET articles/_search
{
"query": {
"match": {
"title": "elasticsearch"
}
},
"highlight": {
"require_field_match": false,
"fields": {
"title": {},
"content": {}
}
}
}require_field_match: false ile sorgu title'da yapılsa bile content'te de highlight uygulanır.
3.3 highlight_query — Farklı Sorgu ile Highlight
GET articles/_search
{
"query": {
"bool": {
"must": { "match": { "content": "elasticsearch" } },
"filter": { "term": { "tags": "java" } }
}
},
"highlight": {
"fields": {
"content": {
"highlight_query": {
"bool": {
"must": { "match": { "content": "elasticsearch" } },
"should": { "match": { "content": "java spring" } }
}
}
}
}
}
}Arama sorgusu ve highlight sorgusu farklı olabilir. Filtrelenen terimleri de highlight etmek için yararlıdır.
3.4 matched_fields — Çoklu Field Birleştirme
PUT matched_articles
{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "turkish",
"fields": {
"plain": {
"type": "text",
"analyzer": "standard"
}
}
}
}
}
}
GET matched_articles/_search
{
"query": {
"multi_match": {
"query": "elasticsearch geliştiriciler",
"fields": ["content", "content.plain"],
"type": "most_fields"
}
},
"highlight": {
"type": "fvh",
"fields": {
"content": {
"matched_fields": ["content", "content.plain"]
}
}
}
}4. Suggesters — Öneri Sistemi
Elasticsearch dört tip suggester sunar:
4.1 Term Suggester — Yazım Düzeltme
En basit suggester. Yanlış yazılmış kelimelere doğru alternatifleri önerir:
GET articles/_search
{
"suggest": {
"spell_check": {
"text": "elasticsearh arma",
"term": {
"field": "content",
"suggest_mode": "always",
"min_word_length": 3,
"max_edits": 2
}
}
}
}Yanıt:
{
"suggest": {
"spell_check": [
{
"text": "elasticsearh",
"options": [
{ "text": "elasticsearch", "score": 0.9166666, "freq": 5 }
]
},
{
"text": "arma",
"options": [
{ "text": "arama", "score": 0.75, "freq": 3 }
]
}
]
}
}`suggest_mode` değerleri:
| Mod | Davranış |
|---|---|
missing | Sadece index'te olmayan terimler için öneri |
popular | Daha sık geçen alternatif varsa öner |
always | Her zaman öneri sun |
4.2 Phrase Suggester — Cümle Düzeltme
Term suggester'ın gelişmiş hali — kelime çiftlerini ve cümle yapısını da dikkate alır:
GET articles/_search
{
"suggest": {
"phrase_suggestion": {
"text": "elastcsearch arma motoru",
"phrase": {
"field": "content",
"size": 3,
"gram_size": 2,
"direct_generator": [
{
"field": "content",
"suggest_mode": "always",
"min_word_length": 3
}
],
"highlight": {
"pre_tag": "<em>",
"post_tag": "</em>"
}
}
}
}
}Yanıt:
{
"suggest": {
"phrase_suggestion": [
{
"text": "elastcsearch arma motoru",
"options": [
{
"text": "elasticsearch arama motoru",
"highlighted": "<em>elasticsearch</em> <em>arama</em> motoru",
"score": 0.085
}
]
}
]
}
}Phrase suggester, kelimeler arası bağlamı anlayarak daha doğru öneriler sunar.
4.3 Completion Suggester — Autocomplete
Completion suggester, autocomplete için özel olarak tasarlanmıştır. FST (Finite State Transducer) veri yapısını kullanır ve son derece hızlıdır:
PUT autocomplete_articles
{
"mappings": {
"properties": {
"title": {
"type": "text"
},
"title_suggest": {
"type": "completion",
"analyzer": "standard",
"search_analyzer": "standard"
}
}
}
}
POST autocomplete_articles/_bulk
{"index":{"_id":"1"}}
{"title":"Elasticsearch Temelleri","title_suggest":{"input":["Elasticsearch Temelleri","ES Temelleri","Elastic Temelleri"],"weight":10}}
{"index":{"_id":"2"}}
{"title":"Elasticsearch ile Full-Text Arama","title_suggest":{"input":["Elasticsearch ile Full-Text Arama","ES Full-Text","Elastic Arama"],"weight":8}}
{"index":{"_id":"3"}}
{"title":"Spring Boot Elasticsearch Entegrasyonu","title_suggest":{"input":["Spring Boot Elasticsearch","Spring Data ES","Spring Elastic"],"weight":6}}
{"index":{"_id":"4"}}
{"title":"Kibana Dashboard Oluşturma","title_suggest":{"input":["Kibana Dashboard","Kibana Görselleştirme"],"weight":5}}Autocomplete sorgusu:
GET autocomplete_articles/_search
{
"suggest": {
"article_suggest": {
"prefix": "ela",
"completion": {
"field": "title_suggest",
"size": 5,
"skip_duplicates": true
}
}
}
}Yanıt: "Elasticsearch Temelleri", "Elasticsearch ile Full-Text Arama", vb. — weight'e göre sıralı.
Fuzzy Completion — Yazım Toleransı
GET autocomplete_articles/_search
{
"suggest": {
"article_suggest": {
"prefix": "elastiksearch",
"completion": {
"field": "title_suggest",
"size": 5,
"fuzzy": {
"fuzziness": "AUTO"
}
}
}
}
}"elastiksearch" yazım hatası olsa bile "Elasticsearch" önerilir.
Context Suggester — Bağlamsal Öneri
Kategori veya lokasyona göre filtrelenmiş öneriler:
PUT context_articles
{
"mappings": {
"properties": {
"title_suggest": {
"type": "completion",
"contexts": [
{
"name": "category",
"type": "category"
}
]
}
}
}
}
POST context_articles/_doc/1
{
"title_suggest": {
"input": "Elasticsearch Kurulumu",
"contexts": {
"category": ["backend", "devops"]
}
}
}
POST context_articles/_doc/2
{
"title_suggest": {
"input": "React Kurulumu",
"contexts": {
"category": ["frontend"]
}
}
}
// Sadece backend önerileri
GET context_articles/_search
{
"suggest": {
"filtered_suggest": {
"prefix": "kur",
"completion": {
"field": "title_suggest",
"size": 5,
"contexts": {
"category": ["backend"]
}
}
}
}
}4.4 Suggester Karşılaştırması
| Özellik | Term | Phrase | Completion |
|---|---|---|---|
| Amaç | Yazım düzeltme | Cümle düzeltme | Autocomplete |
| Hız | Normal | Normal | Çok hızlı |
| Veri yapısı | Inverted index | Inverted index | FST (in-memory) |
| Context desteği | ❌ | ❌ | ✅ |
| Fuzzy desteği | ✅ | ✅ | ✅ |
| Özel field gerekli | ❌ | ❌ | ✅ (completion type) |
5. Highlight + Suggestion Birlikte Kullanımı
Gerçek bir arama deneyimi için ikisini birleştirin:
GET articles/_search
{
"query": {
"match": {
"content": "elasticsearch arama"
}
},
"highlight": {
"pre_tags": ["<mark>"],
"post_tags": ["</mark>"],
"fields": {
"title": { "number_of_fragments": 0 },
"content": { "fragment_size": 150, "number_of_fragments": 2 }
}
},
"suggest": {
"spell_check": {
"text": "elasticsearch arama",
"term": {
"field": "content",
"suggest_mode": "popular"
}
}
}
}6. "Did You Mean?" Implementasyonu
Google tarzı "Bunu mu demek istediniz?" özelliği:
PUT search_index
{
"settings": {
"analysis": {
"analyzer": {
"trigram": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "shingle_filter"]
}
},
"filter": {
"shingle_filter": {
"type": "shingle",
"min_shingle_size": 2,
"max_shingle_size": 3
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "turkish",
"fields": {
"trigram": {
"type": "text",
"analyzer": "trigram"
}
}
}
}
}
}
// "Did you mean?" sorgusu
GET search_index/_search
{
"suggest": {
"did_you_mean": {
"text": "elastiksörc arma motru",
"phrase": {
"field": "title.trigram",
"size": 1,
"gram_size": 3,
"direct_generator": [
{
"field": "title.trigram",
"suggest_mode": "always"
}
],
"collate": {
"query": {
"source": {
"match": {
"{{field_name}}": "{{suggestion}}"
}
}
},
"params": {
"field_name": "title"
},
"prune": true
}
}
}
}
}collate özelliği, önerinin gerçekten sonuç döndürüp döndürmeyeceğini kontrol eder — boş sonuç döndürecek önerileri eler.
7. Java ile Highlighting ve Suggesters
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.*;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.List;
import java.util.Map;
public class HighlightSuggesterJava {
// Highlighting
public static void searchWithHighlight(ElasticsearchClient client) throws Exception {
SearchResponse<ObjectNode> response = client.search(s -> s
.index("articles")
.query(q -> q
.match(m -> m
.field("content")
.query("elasticsearch arama")
)
)
.highlight(h -> h
.preTags("<mark>")
.postTags("</mark>")
.fields("title", f -> f.numberOfFragments(0))
.fields("content", f -> f
.fragmentSize(150)
.numberOfFragments(2)
)
),
ObjectNode.class
);
for (Hit<ObjectNode> hit : response.hits().hits()) {
System.out.println("Title: " + hit.source().get("title").asText());
Map<String, List<String>> highlights = hit.highlight();
if (highlights.containsKey("content")) {
for (String fragment : highlights.get("content")) {
System.out.println(" >> " + fragment);
}
}
}
}
// Completion Suggester
public static void autocomplete(ElasticsearchClient client, String prefix) throws Exception {
SearchResponse<ObjectNode> response = client.search(s -> s
.index("autocomplete_articles")
.suggest(su -> su
.suggesters("article_suggest", sg -> sg
.prefix(prefix)
.completion(c -> c
.field("title_suggest")
.size(5)
.skipDuplicates(true)
.fuzzy(f -> f.fuzziness("AUTO"))
)
)
),
ObjectNode.class
);
var suggestions = response.suggest().get("article_suggest");
if (suggestions != null) {
for (var suggestion : suggestions) {
for (var option : suggestion.completion().options()) {
System.out.printf("Öneri: %s (score: %.2f)%n",
option.text(), option.score());
}
}
}
}
// Term Suggester — Yazım Düzeltme
public static void spellCheck(ElasticsearchClient client, String text) throws Exception {
SearchResponse<ObjectNode> response = client.search(s -> s
.index("articles")
.suggest(su -> su
.text(text)
.suggesters("spell_check", sg -> sg
.term(t -> t
.field("content")
.suggestMode(SuggestMode.Always)
)
)
),
ObjectNode.class
);
var suggestions = response.suggest().get("spell_check");
if (suggestions != null) {
for (var suggestion : suggestions) {
if (!suggestion.term().options().isEmpty()) {
System.out.printf("'%s' → '%s'%n",
suggestion.term().text(),
suggestion.term().options().get(0).text());
}
}
}
}
}8. Best Practices
✅ Yapın
| Uygulama | Neden |
|---|---|
| Highlight'ta fragment_size'ı UI'a göre ayarlayın | Mobil = 100, masaüstü = 200 |
Completion suggester için weight kullanın | Popüler önerileri üste çıkarır |
skip_duplicates: true kullanın | Tekrar eden önerileri engeller |
| Fuzzy autocomplete ekleyin | Yazım hatalarını tolere eder |
unified highlighter kullanın | En iyi genel performans |
❌ Yapmayın
| Uygulama | Neden |
|---|---|
| Çok büyük fragment_size kullanmayın | Ağ trafiği artar |
| plain highlighter'ı büyük dokümanlarla kullanmayın | Yavaş |
| Completion field'ı çok fazla input ile doldurmayın | Bellek kullanımı artar |
| Suggestion sonuçlarını doğrulamadan göstermeyin | Anlamsız öneriler UI'a sızabilir |
9. Yaygın Hatalar
Hata 1: keyword Field'da Highlight
// ❌ keyword field highlight edilemez (analyze edilmediği için)
"highlight": {
"fields": {
"category": {} // keyword type
}
}
// ✅ text field veya text sub-field kullanın
"highlight": {
"fields": {
"content": {} // text type
}
}Hata 2: Completion Field'ı Sorguda Kullanmak
// ❌ completion field normal query'de kullanılamaz
GET my_index/_search
{
"query": {
"match": {
"title_suggest": "elasticsearch" // completion type!
}
}
}
// ✅ suggest API kullanın
GET my_index/_search
{
"suggest": {
"my_suggest": {
"prefix": "elasticsearch",
"completion": { "field": "title_suggest" }
}
}
}Hata 3: Highlight ile _source: false
// ❌ _source kapalıyken highlight çalışmaz (unified/plain)
GET my_index/_search
{
"_source": false,
"highlight": {
"fields": { "content": {} }
}
}
// ✅ _source açık bırakın veya source filtering kullanın
GET my_index/_search
{
"_source": ["title"],
"highlight": {
"fields": { "content": {} }
}
}10. Performans Notları
| İşlem | Maliyet | Optimizasyon |
|---|---|---|
| Highlight (küçük dok.) | Düşük | — |
| Highlight (büyük dok.) | Yüksek | fvh kullanın |
| Term suggest | Düşük | — |
| Phrase suggest | Orta | gram_size ayarlayın |
| Completion suggest | Çok düşük | FST in-memory |
Completion suggester'ın hızı olağanüstüdür çünkü FST yapısı tamamen bellekte tutulur. Milyonlarca öneri bile milisaniyeler içinde döner.
Özet
Highlighting arama terimlerinin sonuçlarda nerede eşleştiğini gösterir —
<em>etiketleri özelleştirilebilirÜç highlighter tipi vardır:
unified(önerilen),plain(basit),fvh(büyük dokümanlar)fragment_size ve number_of_fragments ile highlight çıktısı kontrol edilir
Term Suggester kelime bazlı yazım düzeltme yapar — "elasticsearh" → "elasticsearch"
Phrase Suggester cümle bağlamını dikkate alarak düzeltme önerir
Completion Suggester autocomplete için tasarlanmıştır — FST yapısıyla son derece hızlı,
completionfield type gerektirirContext Suggester kategori veya lokasyona göre filtrelenmiş öneriler sunar
Highlight ve suggest birlikte kullanılarak zengin bir arama deneyimi oluşturulabilir
AI Asistan
Sorularını yanıtlamaya hazır