Elasticsearch ve Tam Metin Arama

Elasticsearch ve OpenSearch arama motoru mimarisi nedir? Ters indeks (Inverted Index), bulanık arama (Fuzzy Search), otomatik tamamlama ve log analitiği.

Yozel

12 dakika

Elasticsearch ve Tam Metin Arama
İçindekiler (14)

Bir kullanıcının arama çubuğuna bir kelime yazıp Enter tuşuna bastığı an ile sonuçların ekranda belirdiği an arasındaki birkaç milisaniye, dijital ürünlerin dönüşüm oranını belirleyen en kritik eşiktir. E-ticaret pazar yerlerinde, kurumsal bilgi bankalarında veya milyarlarca satırlık sunucu loglarını analiz eden gözlemlenebilirlik (observability) platformlarında kullanıcılar yalnızca tam eşleşen kelimeleri aramazlar; yazım hatası yaparlar, ek almış Türkçe kelimeler yazarlar, eş anlamlı ifadeler kullanırlar ve aynı anda marka, fiyat aralığı, renk gibi on farklı dinamik filtreyi (facets) seçerler. Geleneksel ilişkisel veritabanları (PostgreSQL, MySQL, MSSQL) bu tür karmaşık metin sorgularında hızla darboğaza girerken, Apache Lucene çekirdeği üzerine inşa edilen Elasticsearch ve OpenSearch dağıtık arama motorları milisaniyeler içinde en alakalı sonuçları sıralar. Modern uygulama ve yazılım mimarilerinde tam metin arama (Full-Text Search) katmanını doğru kurgulamak, ölçeklenebilirliğin temel şartlarından biridir.

Bu kapsamlı mühendislik rehberinde; ilişkisel veritabanlarındaki `LIKE '%kelime%'` sorgularının neden milyonlarca satırda çöktüğünü, Elasticsearch'ün kalbindeki Ters İndeks (Inverted Index) veri yapısını, Türkçe morfolojik analizörleri (Tokenization & Stemming), BM25 alaka düzeyi (relevance) skorlama algoritmasını, Levenshtein mesafesine dayalı Bulanık Arama (Fuzzy Search) mekanizmasını ve hibrit vektör aramasını tüm teknik detaylarıyla inceleyeceğiz.

1. Neden SQL `LIKE '%...%'` Sorguları Büyük Ölçekte Çöker?

İlişkisel veritabanlarında indeksler varsayılan olarak B-Tree (Dengeli Ağaç) veri yapısı üzerinde çalışır. Bir B-Tree indeksi, metinleri soldan sağa doğru alfabetik sırayla dizer. Eğer `WHERE title LIKE 'kablosuz%'` gibi önek (prefix) araması yaparsanız B-Tree indeksi çalışır; ancak kullanıcı `"kulaklık"` yazdığında ürün başlığının ortasında geçen bu kelimeyi bulmak için `WHERE title LIKE '%kulaklık%'` veya `ILIKE '%kulaklık%'` sorgusu çalıştırdığınız anda B-Tree indeksi tamamen işlevsiz kalır.

Veritabanı motoru, tabloda 5 milyon ürün varsa 5 milyon satırın tamamını diskten veya RAM'den tek tek okuyarak (Full Table Scan - Tam Tablo Taraması) karakter karşılaştırması yapmak zorunda kalır. Daha da kötüsü:

  • Morfolojik Körlük (Ek Sorunu): Kullanıcı `"kulaklıklar"` yazdığında, başlığında `"Kablosuz Kulaklık"` geçen ürün eşleşmez.
  • Yazım Hatası (Typo) İmkansızlığı: Kullanıcı yanlışlıkla `"kablusuz kulaklik"` yazdığında SQL sorgusu 0 sonuç döndürür ve potansiyel satış anında kaybedilir.
  • Alaka Düzeyi (Relevance) Sıralaması Yoktur: Aranan kelimenin ürünün ana başlığında geçmesi ile 500 kelimelik ürün açıklamasının en altında bir kez geçmesi SQL için aynı değerdedir; sonuçları "en alakalıdan en az alakalıya" göre puanlayamaz.

İşte bu nedenlerle yüksek ölçekli e-ticaret ve mikroservis mimarilerinde ana kayıt sistemi (System of Record) olarak ilişkisel veritabanı korunurken, arama ve filtreleme yükü Change Data Capture (CDC / Debezium) veya olay kuyrukları (Kafka / RabbitMQ) aracılığıyla Elasticsearch / OpenSearch kümesine aktarılır.

Ters İndeks Inverted Index Veri Yapısı ve BM25 Skorlama Mimarisi
Elasticsearch ve Apache Lucene, belgeleri satır bazlı taramak yerine kelimelerden belge kimliklerine (Document IDs) işaret eden Ters İndeks (Inverted Index) yapısı ve BM25 algoritmasıyla milisaniyeler içinde arama yapar.

2. Apache Lucene ve Ters İndeks (Inverted Index) Mimarisi

Elasticsearch ve açık kaynaklı çatalı (fork) olan AWS destekli OpenSearch, aslında dağıtık birer RESTful orkestrasyon katmanıdır; her bir parçanın (Shard) altında gerçek indeksleme ve arama işini yapan motor Apache Lucene kütüphanesidir. Lucene'in bu kadar hızlı olmasının sırrı, kalın teknik kitapların en arkasında bulunan "Dizin (Index)" sayfasına benzeyen Ters İndeks (Inverted Index) veri yapısıdır.

Standart bir veritabanı "Belge ID -> İçerdiği Kelimeler" şeklinde kayıt tutarken, Ters İndeks bu tabloyu tersine çevirerek "Kelime (Term) -> Geçtiği Belge ID'leri (Posting List), Frekansı ve Konumu" şeklinde saklar. Üstelik terim sözlüğü (Term Dictionary), Finite State Transducers (FST) adı verilen sıkıştırılmış bir otomat yapısıyla doğrudan RAM'de tutulduğu için milyonlarca belge arasında bir kelimenin hangi belgelerde geçtiğini bulmak `O(1)` (sabit zaman) karmaşıklığına yakındır.

Metin Analiz Boru Hattı (Analyzer Pipeline): Türkçe Kök Bulma (Stemming)

Bir belge Ters İndeks'e yazılmadan (index-time) ve kullanıcı arama yaptığında (search-time), metin üç aşamalı bir Analyzer (Analizör) boru hattından geçer:

  1. Character Filters (Karakter Filtreleri): HTML etiketlerini (`<p>`, `<b>`) temizler veya `&` işaretini `"ve"` kelimesine dönüştürür.
  2. Tokenizer (Kelimeleştirici): Cümleyi boşluklardan ve noktalama işaretlerinden bölerek tekil kelimelere (token) ayırır.
  3. Token Filters (Kelime Filtreleri): Tüm harfleri küçültür (`lowercase`), Türkçe'ye özgü `İ->i` / `I->ı` dönüşümünü yapar, `"ve, ile, için, bir"` gibi anlamsız bağlaçları (`stop words`) çıkarır ve en önemlisi Stemmer (Kök Bulucu) ile kelimenin çekim eklerini atarak kökünü bulur (örneğin `"gözlükçülerden"` -> `"gözlük"`).
// Elasticsearch: Türkçe Morfolojik Analizör, Eş Anlamlılar (Synonyms) ve Otomatik Tamamlama İndeks Tanımı
PUT /urunler_katalog_v1
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "analysis": {
      "filter": {
        "turkish_stop": { "type": "stop", "stopwords": "_turkish_" },
        "turkish_stemmer": { "type": "stemmer", "language": "turkish" },
        "eticaret_synonyms": {
          "type": "synonym",
          "synonyms": [
            "laptop, dizüstü bilgisayar, notebook",
            "cep telefonu, akıllı telefon, smartphone",
            "powerbank, taşınabilir şarj cihazı"
          ]
        }
      },
      "analyzer": {
        "tr_custom_analyzer": {
          "tokenizer": "standard",
          "filter": [
            "apostrophe",
            "turkish_lowercase",
            "eticaret_synonyms",
            "turkish_stop",
            "turkish_stemmer"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "title": { "type": "text", "analyzer": "tr_custom_analyzer" },
      "brand": { "type": "keyword" },
      "category": { "type": "keyword" },
      "price": { "type": "scaled_float", "scaling_factor": 100 },
      "in_stock": { "type": "boolean" }
    }
  }
}

Bu yapılandırma sayesinde satıcı ürünü `"Dizüstü Bilgisayar"` başlığıyla eklemiş olsa bile, müşteri arama çubuğuna `"notebooklar"` yazdığında `eticaret_synonyms` ve `turkish_stemmer` filtreleri devreye girerek ürünü %100 isabetle yakalar.

3. BM25 (Best Matching 25) Alaka Düzeyi Skorlama Algoritması

Arama motorunun 5.000 eşleşen ürünü bulması işin sadece yarısıdır; asıl mühendislik başarısı kullanıcının satın alma ihtimali en yüksek olan ilk 10 ürünü en üste dizmektir. Elasticsearch 5.x sürümünden itibaren klasik TF-IDF yerine Okapi BM25 olasılıksal sıralama algoritmasını kullanır. BM25 bir belgenin puanını (`_score`) hesaplarken üç temel faktörü birleştirir:

  • Term Frequency (TF - Terim Sıklığı Doygunluğu): Aranan kelime bir belgede ne kadar çok geçiyorsa puan artar; ancak TF-IDF'ten farklı olarak BM25'te logaritmik doygunluk (saturation) vardır. Kelimenin 5 kez geçmesi 1 kez geçmesinden çok daha değerlidir ama 50 kez geçmesi (SEO spam) puanı 10 kat artırmaz, belirli bir asimptotta sabitlenir.
  • Inverse Document Frequency (IDF - Ters Belge Sıklığı): Aranan kelimenin tüm katalogda ne kadar nadir olduğunu ölçer. Kullanıcı `"Apple iPhone 16 Pro kılıf"` aradığında, `"kılıf"` kelimesi 50.000 üründe geçerken `"16"` ve `"iPhone"` çok daha az üründe geçer; dolayısıyla nadir kelimeler skora çok daha yüksek ağırlık verir.
  • Field Length Normalization (Alan Uzunluğu Normalizasyonu): Aranan kelimenin 4 kelimelik kısa bir `title` (başlık) alanında geçmesi, 800 kelimelik uzun bir `description` (açıklama) alanında geçmesinden çok daha yüksek puan alır. Ayrıca `title^3` gibi Alan Güçlendirme (Field Boosting) ile başlık eşleşmelerinin katsayısı 3 katına çıkarılabilir.
Bulanık Arama Fuzzy Search Otomatik Tamamlama ve Faceted Filtreleme
Levenshtein mesafesiyle çalışan Bulanık Arama (Fuzzy Search), Otomatik Tamamlama (Edge N-Gram) ve Çok Boyutlu Kırılım (Faceted Aggregations) modern e-ticaret arama deneyiminin üç temel direğidir.

4. Bulanık Arama (Fuzzy Search), Otomatik Tamamlama ve Faceted Aggregations

Mobil ekranlarda hızlı yazarken yapılan harf hatalarını tolere etmek için Elasticsearch, Levenshtein Düzenleme Mesafesi (Edit Distance) algoritmasını kullanan `fuzziness: "AUTO"` özelliğini sunar. Levenshtein mesafesi, bir kelimeyi başka bir kelimeye dönüştürmek için gereken minimum tek karakterlik işlem (silme, ekleme, değiştirme veya komşu iki harfin yerini değiştirme - transposition) sayısıdır. Örneğin kullanıcı `"blutut kulaklk"` yazdığında, sistem 1 veya 2 harf mesafesindeki `"bluetooth kulaklık"` terimini otomatik olarak eşleştirir.

// Elasticsearch: Çoklu Alan Güçlendirme (Multi-Match Boost), Bulanık Arama ve Dinamik Filtre Kırılımları (Aggregations)
POST /urunler_katalog_v1/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "kablusuz kulaklik",
            "fields": ["title^3", "description"],
            "fuzziness": "AUTO",
            "prefix_length": 1
          }
        }
      ],
      "filter": [
        { "term": { "in_stock": true } },
        { "range": { "price": { "lte": 5000 } } }
      ]
    }
  },
  "aggs": {
    "marka_kirilimi": {
      "terms": { "field": "brand", "size": 15 }
    },
    "fiyat_araliklari": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 1000 },
          { "from": 1000, "to": 3000 },
          { "from": 3000 }
        ]
      }
    }
  }
}

Yukarıdaki sorguda dikkat edilmesi gereken çok kritik bir performans detayı vardır: `in_stock: true` ve `price <= 5000` koşulları `must` yerine `filter` bağlamına (Filter Context) yazılmıştır! `filter` bloğundaki kriterler için BM25 alaka düzeyi skoru hesaplanmaz; sonuçlar bellekte Bitset (Bit Dizisi) olarak önbelleklenir (Node Query Cache) ve sonraki sorgularda mikrosaniyeler içinde uygulanır. Ayrıca `aggs` (Aggregations) bloğu, tek bir ağ isteğinde hem arama sonuçlarını getirir hem de sol menüdeki "Sony (42), JBL (38), Sennheiser (19)" gibi dinamik filtre sayılarını (Faceted Search) anında hesaplar. Bu hız, e-ticaret site hızı ve filtreleme etkileşim süresi (INP) üzerinde doğrudan gelir artırıcı etki yaratır.

5. Yazarken Arama (Search-as-You-Type / Otomatik Tamamlama) ve ELK Log Analitiği

Modern arama deneyiminin en kritik bileşenlerinden biri, kullanıcı henüz kelimenin ilk üç harfini (`"kab..."`) yazarken milisaniyeler içinde `"kablosuz kulaklık"`, `"kablo düzenleyici"` ve `"kablosuz mouse"` önerilerini getiren Otomatik Tamamlama (Autocomplete / Search-as-You-Type) sistemidir. Elasticsearch bu ihtiyacı karşılamak için üç farklı teknik sunar:

  • Edge N-Gram Tokenizer (Önek Parçalayıcı): İndeksleme aşamasında `"kulaklık"` kelimesini `["ku", "kul", "kula", "kulak", "kulakl", "kulaklı", "kulaklık"]` şeklinde önek parçalarına ayırarak Ters İndeks'e kaydeder. İndeks boyutunu biraz büyütse de kelimenin ortasında geçen terimleri ve çok kelimeli ürün başlıklarını yakalamada en esnek yöntemdir.
  • Completion Suggester (FST Tabanlı Tamamlayıcı): Tamamen RAM'de tutulan Finite State Transducer (FST) grafiği üzerinde çalışır. Arama hızı 1 milisaniyenin altındadır; saniyede on binlerce otomatik tamamlama sorgusu alan yüksek trafikli arama çubukları için en ideal veri yapısıdır.
  • `search_as_you_type` Özel Alan Tipi: Elasticsearch'ün tek bir alan tanımıyla arka planda otomatik olarak `_2gram`, `_3gram` (Shingle) ve `_index_prefix` alt alanlarını oluşturduğu modern yerleşik veri tipidir.

Uygulama Aramasının Ötesi: ELK Yığını ve Gözlemlenebilirlik (Observability)

Elasticsearch yalnızca müşterilerin ürün aradığı bir motor değildir; aynı zamanda ELK / Elastic Stack (Elasticsearch, Logstash/FluentBit, Kibana) mimarisiyle modern DevOps ve siber güvenlik (SIEM) dünyasının kalbidir. Yüzlerce Kubernetes podundan akan milyonlarca satırlık JSON uygulama logu, APM (Application Performance Monitoring) izleri ve sistem metrikleri Data Streams (Veri Akışları) ve ILM (Index Lifecycle Management - Hot, Warm, Cold, Frozen katmanları) aracılığıyla Elasticsearch'te indekslenir. Bir mikroserviste `500 Internal Server Error` sıçraması yaşandığında, mühendisler Kibana üzerinden milyarlarca log satırını 2 saniyede filtreleyerek kök nedeni (root cause) tespit edebilir.

6. Arama Motoru Teknolojileri Karşılaştırma Tablosu

Projenizin ölçeğine, bütçesine ve mühendislik ekibinizin operasyonel kapasitesine göre en popüler arama çözümlerinin karşılaştırması aşağıdadır:

Kriter / Özellik Elasticsearch / OpenSearch Meilisearch / Typesense PostgreSQL (`tsvector` + `pg_trgm`)
Temel Mimari Java / Apache Lucene (Dağıtık Küme) Rust (Meilisearch) / C++ (Typesense) İlişkisel Veritabanı İçi GIN/GiST İndeks
Ölçeklenebilirlik Sınırı Milyarlarca belge ve Petabaytlarca log On milyonlarca belge (RAM odaklı) Yüz binlerce - birkaç milyon satır
Kurulum ve Bakım Yükü Yüksek (JVM Heap, Shard ve Küme yönetimi) Çok Düşük (Sıfır ayar ile anında çalışır) Sıfır Ek Altyapı (Mevcut DB içinde)
Otomatik Tamamlama ve Typo Çok Güçlü (Özelleştirilebilir N-Gram/Fuzzy) Kutudan Çıktığı Gibi Mükemmel Orta (`pg_trgm` trigram ile sınırlı)
Log Analitiği (ELK) ve SIEM Endüstri Standardı (Kibana / Logstash) Uygun Değil (Uygulama araması odaklı) Uygun Değil
Vektör ve Hibrit Arama (RAG) Yerleşik HNSW kNN + BM25 RRF Destekliyor (Embedding entegrasyonlu) `pgvector` eklentisi ile güçlü

6. Sıkça Sorulan Sorular (SSS)

1. Elasticsearch birincil veritabanı (Primary Database) olarak kullanılabilir mi?

Hayır, kesinlikle kullanılmamalıdır. Elasticsearch ACID транzaksiyonları (rollback), çoklu tablo ilişkisel bütünlüğü (foreign keys) veya anlık tutarlılık (immediate consistency) için değil, Yakın Gerçek Zamanlı (Near Real-Time - varsayılan 1 saniye `refresh_interval`) arama ve analitik için tasarlanmıştır. Birincil veriniz her zaman PostgreSQL, MySQL veya MongoDB gibi bir veritabanında durmalı, Elasticsearch ikincil arama indeksi olarak beslenmelidir.

2. Elasticsearch ile OpenSearch arasındaki fark nedir? Hangisini seçmeliyim?

2021 yılında Elastic şirketi Elasticsearch 7.10 sürümünden sonra lisansını değiştirdiğinde, AWS (Amazon Web Services) son açık kaynaklı Apache 2.0 sürümünü çatallayarak OpenSearch projesini başlattı (Elastic 2024 sonunda AGPL açık kaynak lisansını tekrar eklemiştir). Mimari, sorgu dili (Query DSL) ve Lucene altyapısı %95 oranında aynıdır. AWS bulutu üzerinde yönetilen servis kullanıyorsanız OpenSearch, Elastic Cloud veya kendi sunucunuzda en yeni vektör/AI özelliklerini kullanıyorsanız Elasticsearch tercih edebilirsiniz.

3. İndeks şeması (Mapping) değiştiğinde mevcut verileri kesintisiz nasıl taşırız?

Elasticsearch'te mevcut bir alanın tipi (mapping) doğrudan değiştirilemez. Sıfır kesintili (zero-downtime) geçiş için Index Alias (Takma Ad) deseni kullanılır: Uygulamanız doğrudan `urunler_v1` yerine `urunler_aktif` takma adına sorgu atar. Yeni şemayla `urunler_v2` indeksini oluşturur, `_reindex` API'si ile verileri kopyalar ve atomik bir komutla `urunler_aktif` takma adını `urunler_v1`'den `urunler_v2`'ye çevirirsiniz.

4. Çok fazla parça (Over-sharding) oluşturmak neden performansı düşürür?

Yeni başlayanların en sık yaptığı hata, 2 GB'lık küçük bir indeks için 10 veya 20 Shard (parça) açmaktır. Her bir Shard arka planda ayrı bir Apache Lucene motorudur ve dosya tanımlayıcıları ile JVM Heap belleği tüketir. Altın kural olarak her bir Shard boyutunun 10 GB ile 50 GB arasında olması hedeflenmeli; birkaç milyon satırlık ürün kataloglarında 1 ila 3 Primary Shard yeterli görülmelidir.

5. Anahtar kelime araması (BM25) ile Vektör Araması (Semantic Search) nasıl birleştirilir?

Üretken yapay zeka ve derin öğrenme modelleriyle birlikte metinler artık çok boyutlu sayısal vektörlere (Embeddings) dönüştürülüyor. Ancak vektör araması kavramsal yakınlığı (`"kışlık mont"` -> `"soğuk hava ceketi"`) harika yakalarken, tam model kodu veya barkod (`"WH-1000XM5"`) aramalarında BM25 çok daha başarılıdır. Elasticsearch'te her iki sorguyu aynı anda çalıştırıp sonuçları RRF (Reciprocal Rank Fusion) algoritmasıyla birleştiren Hibrit Arama (Hybrid Search) günümüzün en gelişmiş arama mimarisidir.

Paylaş

Yozel

Codex haberleri en az iki bağımsız kaynakla doğrulanır; düzeltmeler yazının altında tarih ve saatle belirtilir. Yayın ilkelerimiz