Elasticsearch ile Full-Text Search: Gelişmiş Metin Arama Rehberi

Veri miktarı arttıkça klasik SQL LIKE '%arama_terimi%' sorguları bir noktadan sonra sistemi dizüstü bırakır. Milyonlarca kayıt arasında kelime aramak, indeks kullanamayan bu yöntemle tam anlamıyla tablo taramasına dönüşür ve production ortamında ciddi performans sorunlarına yol açar. İşte tam bu noktada Elasticsearch devreye giriyor: dağıtık, ölçeklenebilir ve inanılmaz derecede hızlı bir full-text search motoru.

Bu yazıda Elasticsearch’ü sıfırdan kurarak gerçek dünya senaryolarında nasıl kullanacağınızı, index yönetiminden gelişmiş sorgu yapılarına kadar adım adım inceleyeceğiz.

Elasticsearch Nedir ve Ne Zaman Kullanmalısınız

Elasticsearch, Apache Lucene üzerine inşa edilmiş, REST API aracılığıyla yönetilen açık kaynaklı bir arama ve analitik motorudur. JSON belgeler üzerinde çalışır, yatay ölçeklenebilir ve gerçek zamanlıya yakın (near-real-time) arama sunar.

Ne zaman Elasticsearch tercih etmelisiniz:

  • E-ticaret sitelerinde ürün arama
  • Log yönetimi ve analizi (ELK Stack)
  • Büyük içerik platformlarında makale/döküman arama
  • Otomatik tamamlama (autocomplete) özellikleri
  • Coğrafi konum tabanlı arama
  • Metrik ve zaman serisi verisi analizi

Ne zaman Elasticsearch ihtiyacınız olmayabilir:

  • Birkaç bin kayıt içeren küçük tablolar için PostgreSQL’in full-text search özelliği yeterli
  • Basit CRUD işlemleri ağırlıklı sistemlerde ek karmaşıklık yaratır
  • Tek sunucuda düşük kaynak ortamlarında Elasticsearch’ün JVM ayak izi ağır gelebilir

Kurulum: Ubuntu/Debian Ortamında

Gereksinimler

  • Java 11 veya üzeri (Elasticsearch 7.x ile birlikte gelir)
  • En az 4 GB RAM (production için 8 GB+ önerilir)
  • 64-bit işletim sistemi

APT ile Kurulum

# GPG anahtarını ekle
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg

# Apt transport https paketini kur
sudo apt-get install apt-transport-https

# Repository ekle
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list

# Güncelle ve kur
sudo apt-get update && sudo apt-get install elasticsearch

# Servisi etkinleştir ve başlat
sudo systemctl daemon-reload
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch

Kurulum tamamlandıktan sonra elastic kullanıcısının şifresi terminalde gösterilir. Bu şifreyi mutlaka kaydedin.

# Kurulumun başarılı olduğunu doğrula
curl -X GET "https://localhost:9200" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ

Temel Konfigürasyon

# /etc/elasticsearch/elasticsearch.yml

cluster.name: uretim-cluster
node.name: node-1
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch

# Ağ ayarları - sadece iç ağdan erişim için
network.host: 0.0.0.0
http.port: 9200

# Discovery ayarları - tek node için
discovery.type: single-node

# Bellek ayarları
bootstrap.memory_lock: true

# Güvenlik (8.x'te varsayılan olarak açık)
xpack.security.enabled: true
xpack.security.http.ssl.enabled: true

JVM heap boyutunu da ayarlayalım. Genel kural olarak toplam RAM’in yarısını verin, 31 GB’ı geçmeyin:

# /etc/elasticsearch/jvm.options.d/heap.options dosyası oluştur
sudo bash -c 'cat > /etc/elasticsearch/jvm.options.d/heap.options << EOF
-Xms4g
-Xmx4g
EOF'

sudo systemctl restart elasticsearch

Index Yönetimi

Elasticsearch’te veriler index’lerde tutulur. Index, ilişkisel veritabanlarındaki tabloya benzer ama çok daha esnektir.

Index Oluşturma ve Mapping Tanımlama

Mapping, Elasticsearch’e belgenizdeki alanların veri tiplerini ve nasıl analiz edileceğini söyler. Otomatik mapping (dynamic mapping) kullanabilirsiniz ama production ortamında explicit mapping her zaman daha güvenlidir.

# Ürün arama için bir index oluştur
curl -X PUT "https://localhost:9200/urunler" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "settings": {
      "number_of_shards": 2,
      "number_of_replicas": 1,
      "analysis": {
        "analyzer": {
          "turkce_analyzer": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": ["lowercase", "asciifolding"]
          }
        }
      }
    },
    "mappings": {
      "properties": {
        "ad": {
          "type": "text",
          "analyzer": "turkce_analyzer",
          "fields": {
            "keyword": {
              "type": "keyword"
            }
          }
        },
        "aciklama": {
          "type": "text",
          "analyzer": "turkce_analyzer"
        },
        "fiyat": {
          "type": "float"
        },
        "kategori": {
          "type": "keyword"
        },
        "stok": {
          "type": "integer"
        },
        "olusturma_tarihi": {
          "type": "date"
        },
        "etiketler": {
          "type": "keyword"
        }
      }
    }
  }'

text: Full-text arama için kullanılır, analiz sürecinden geçer keyword: Tam eşleşme, sıralama ve aggregation için kullanılır float/integer: Nümerik alanlar için date: Tarih alanları için, format belirtilebilir

Belge Ekleme ve Güncelleme

# Tek belge ekle
curl -X POST "https://localhost:9200/urunler/_doc" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "ad": "Kablosuz Bluetooth Kulaklık",
    "aciklama": "Gürültü engelleme özellikli premium ses kalitesi sunan over-ear kulaklık",
    "fiyat": 1299.99,
    "kategori": "elektronik",
    "stok": 45,
    "olusturma_tarihi": "2024-01-15",
    "etiketler": ["bluetooth", "kablosuz", "ses", "müzik"]
  }'

# Bulk API ile çoklu belge ekle (production için tercih edilen yöntem)
curl -X POST "https://localhost:9200/urunler/_bulk" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '
{"index": {}}
{"ad": "Mekanik Klavye", "aciklama": "RGB aydınlatmalı cherry switch mekanik oyuncu klavyesi", "fiyat": 899.00, "kategori": "bilgisayar", "stok": 23, "olusturma_tarihi": "2024-01-20", "etiketler": ["klavye", "rgb", "oyuncu", "mekanik"]}
{"index": {}}
{"ad": "4K Monitör", "aciklama": "27 inç IPS panel 4K çözünürlüklü profesyonel monitör", "fiyat": 8500.00, "kategori": "bilgisayar", "stok": 12, "olusturma_tarihi": "2024-01-22", "etiketler": ["monitör", "4k", "ips", "profesyonel"]}
{"index": {}}
{"ad": "Kablosuz Mouse", "aciklama": "Ergonomik tasarım 2.4GHz kablosuz ofis faresi", "fiyat": 349.99, "kategori": "bilgisayar", "stok": 67, "olusturma_tarihi": "2024-01-18", "etiketler": ["mouse", "kablosuz", "ergonomik"]}
'

Temel Arama Sorguları

Match Query: Full-Text Aramanın Temeli

# "kablosuz" içeren ürünleri bul
curl -X GET "https://localhost:9200/urunler/_search" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "query": {
      "match": {
        "aciklama": {
          "query": "kablosuz bluetooth kulaklık",
          "operator": "or",
          "fuzziness": "AUTO"
        }
      }
    },
    "highlight": {
      "fields": {
        "aciklama": {}
      }
    }
  }'

operator: "or": Kelimelerden herhangi biri eşleşsin (varsayılan) operator: "and": Tüm kelimeler eşleşmeli fuzziness: "AUTO": Yazım hatalarını tolere et (örneğin “kulaklk” yerine “kulaklık”)

Multi-Match Query: Birden Fazla Alanda Arama

# Hem ad hem aciklama alanında ara, ad alanına daha fazla ağırlık ver
curl -X GET "https://localhost:9200/urunler/_search" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "query": {
      "multi_match": {
        "query": "profesyonel monitör",
        "fields": ["ad^3", "aciklama^1", "etiketler^2"],
        "type": "best_fields",
        "fuzziness": "AUTO"
      }
    }
  }'

^3 notasyonu o alanın ağırlığını belirtir. Ad alanında bulunan eşleşmeler aciklama alanına göre 3 kat daha fazla skora katkıda bulunur.

Bool Query: Karmaşık Filtreler

Gerçek dünyada genellikle birden fazla koşulu birleştirmeniz gerekir. Bool query bunun için biçilmiş kaftandır:

# 500-2000 TL arası, elektronik kategorisinde, stokta olan ve "kablosuz" içeren ürünler
curl -X GET "https://localhost:9200/urunler/_search" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "query": {
      "bool": {
        "must": [
          {
            "multi_match": {
              "query": "kablosuz",
              "fields": ["ad", "aciklama", "etiketler"]
            }
          }
        ],
        "filter": [
          {
            "term": {
              "kategori": "elektronik"
            }
          },
          {
            "range": {
              "fiyat": {
                "gte": 500,
                "lte": 2000
              }
            }
          },
          {
            "range": {
              "stok": {
                "gt": 0
              }
            }
          }
        ],
        "should": [
          {
            "term": {
              "etiketler": "bluetooth"
            }
          }
        ],
        "minimum_should_match": 0
      }
    },
    "sort": [
      {"_score": {"order": "desc"}},
      {"fiyat": {"order": "asc"}}
    ],
    "from": 0,
    "size": 10
  }'

must: Mutlaka eşleşmeli, skora katkıda bulunur filter: Eşleşmeli ama skora katkıda bulunmaz (önbelleklenir, daha hızlı) should: Eşleşirse skoru artırır ama zorunlu değil must_not: Kesinlikle eşleşmemeli

Aggregation: Veri Analizi

Aggregation, Elasticsearch’ü sadece bir arama motorundan çıkarıp analitik bir araca dönüştüren özelliktir. E-ticaret sitelerindeki “Kategoriye göre filtrele” ve fiyat aralığı filtreleri bunlarla yapılır.

# Kategoriye göre ürün sayısı ve ortalama fiyat
curl -X GET "https://localhost:9200/urunler/_search" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "size": 0,
    "aggs": {
      "kategoriler": {
        "terms": {
          "field": "kategori",
          "size": 10
        },
        "aggs": {
          "ortalama_fiyat": {
            "avg": {
              "field": "fiyat"
            }
          },
          "fiyat_aralik": {
            "range": {
              "field": "fiyat",
              "ranges": [
                {"to": 500},
                {"from": 500, "to": 1500},
                {"from": 1500, "to": 5000},
                {"from": 5000}
              ]
            }
          }
        }
      }
    }
  }'

"size": 0 sorgu sonuçlarını döndürmez, sadece aggregation sonuçlarını verir. Bu önemli bir performans optimizasyonudur.

Gerçek Dünya Senaryosu: Log Analizi

Bir web uygulamasının nginx loglarını Elasticsearch’e göndererek analiz edelim.

Log Verisi için Index Oluştur

curl -X PUT "https://localhost:9200/nginx-logs-2024.01" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 0
    },
    "mappings": {
      "properties": {
        "@timestamp": {"type": "date"},
        "remote_addr": {"type": "ip"},
        "request_method": {"type": "keyword"},
        "request_uri": {"type": "keyword"},
        "status": {"type": "short"},
        "body_bytes_sent": {"type": "integer"},
        "http_referer": {"type": "keyword"},
        "http_user_agent": {"type": "text", "fields": {"keyword": {"type": "keyword"}}},
        "request_time": {"type": "float"},
        "message": {"type": "text"}
      }
    }
  }'

Hata Analizi Sorgusu

# Son 1 saatteki 5xx hatalarını listele, hangi endpoint en çok hata veriyor
curl -X GET "https://localhost:9200/nginx-logs-2024.01/_search" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "size": 0,
    "query": {
      "bool": {
        "filter": [
          {
            "range": {
              "@timestamp": {
                "gte": "now-1h",
                "lt": "now"
              }
            }
          },
          {
            "range": {
              "status": {
                "gte": 500,
                "lt": 600
              }
            }
          }
        ]
      }
    },
    "aggs": {
      "endpoint_hatalari": {
        "terms": {
          "field": "request_uri",
          "size": 10,
          "order": {"_count": "desc"}
        },
        "aggs": {
          "ortalama_yanit_suresi": {
            "avg": {"field": "request_time"}
          }
        }
      },
      "zaman_grafigi": {
        "date_histogram": {
          "field": "@timestamp",
          "calendar_interval": "5m"
        }
      }
    }
  }'

Index Alias ve Template Kullanımı

Production ortamında index yönetimini kolaylaştırmak için alias kullanmak şarttır. Özellikle günlük veya aylık log indexleri oluştururken bu yaklaşım hayat kurtarır.

# Index template oluştur - yeni aylik log indexleri otomatik bu ayarları alsın
curl -X PUT "https://localhost:9200/_index_template/nginx-logs-template" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "index_patterns": ["nginx-logs-*"],
    "template": {
      "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 1,
        "index.lifecycle.name": "logs-policy",
        "index.lifecycle.rollover_alias": "nginx-logs"
      }
    },
    "priority": 100
  }'

# Alias ekle - uygulama nginx-logs alias'ını kullansın
# Fiziksel index adını değiştirseniz bile uygulama etkilenmez
curl -X POST "https://localhost:9200/_aliases" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "actions": [
      {
        "add": {
          "index": "nginx-logs-2024.01",
          "alias": "nginx-logs",
          "is_write_index": true
        }
      }
    ]
  }'

Performans ve İzleme

Cluster Sağlığını Kontrol Et

# Cluster durumu - green, yellow veya red döner
curl -X GET "https://localhost:9200/_cluster/health?pretty" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ

# Node istatistikleri
curl -X GET "https://localhost:9200/_nodes/stats?pretty" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ

# Yavaş sorguları logla - 2 saniyeden uzun sorgular için
curl -X PUT "https://localhost:9200/urunler/_settings" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "index.search.slowlog.threshold.query.warn": "2s",
    "index.search.slowlog.threshold.query.info": "1s",
    "index.search.slowlog.threshold.fetch.warn": "1s",
    "index.search.slowlog.level": "info"
  }'

Önemli Performans İpuçları

Shard boyutu: Bir shard 10-50 GB arasında tutulmalı. Çok fazla küçük shard cluster’ı yorar.

Filter önce, must sonra: Bool query’de mümkün olduğunca filter kullanın. Filter sonuçları önbelleğe alınır.

_source sınırlandırma: Büyük belgelerde sadece ihtiyacınız olan alanları çekin:

"_source": ["ad", "fiyat", "kategori"]

Bulk API kullanımı: Tek tek belge eklemek yerine bulk API ile toplu ekleme yapın. Bu 5-10 kat performans farkı yaratır.

Mapping’i erken kilitleyin: dynamic: "strict" ile yanlışlıkla alan eklenmesini önleyin:

"mappings": {
  "dynamic": "strict",
  "properties": { ... }
}

Index Lifecycle Management (ILM)

Log verisi gibi büyüyen indexler için ILM politikası oluşturmak disk yönetimini otomatikleştirir:

# 30 günlük log saklama politikası
curl -X PUT "https://localhost:9200/_ilm/policy/logs-policy" 
  --cacert /etc/elasticsearch/certs/http_ca.crt 
  -u elastic:SIFRENIZ 
  -H 'Content-Type: application/json' 
  -d '{
    "policy": {
      "phases": {
        "hot": {
          "actions": {
            "rollover": {
              "max_size": "50gb",
              "max_age": "7d"
            }
          }
        },
        "warm": {
          "min_age": "7d",
          "actions": {
            "forcemerge": {
              "max_num_segments": 1
            },
            "shrink": {
              "number_of_shards": 1
            }
          }
        },
        "delete": {
          "min_age": "30d",
          "actions": {
            "delete": {}
          }
        }
      }
    }
  }'

Güvenlik Notları

  • Elasticsearch’ü asla doğrudan internete açmayın. Nginx veya HAProxy arkasına alın.
  • Güçlü şifreler kullanın ve Kibana ile Elasticsearch arasındaki trafiği TLS ile şifreleyin.
  • Role tabanlı erişim kontrolü (RBAC) ile kullanıcılara yalnızca ihtiyaçları olan izinleri verin.
  • Firewall’da 9200 ve 9300 portlarını sadece yetkili IP adreslerine açın.
  • Snapshot politikası oluşturun, verilerinizi düzenli olarak yedekleyin.

Sonuç

Elasticsearch öğrenme eğrisi biraz dik olsa da bir kez içselleştirdiğinizde milyonlarca kayıt üzerinde milisaniyeler içinde arama yapmak, karmaşık aggregation’lar çalıştırmak ve log analizini otomatize etmek gerçekten keyifli hale geliyor.

Başlarken dikkat etmeniz gereken birkaç kritik nokta var: mapping’i önceden iyi tasarlayın çünkü sonradan değiştirmek index’i yeniden oluşturmayı gerektirir, shard sayısını gereksiz yere artırmayın ve mutlaka ILM politikası kurarak disk dolmasını önleyin.

Bir sonraki adım olarak Kibana’yı kurarak verilerinizi görselleştirmeye, Logstash veya Beats ailesini kullanarak log toplama pipeline’ı kurmaya bakabilirsiniz. ELK Stack olarak bu üçünü birleştirdiğinizde güçlü bir gözlemlenebilirlik (observability) platformuna sahip olursunuz.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir