Elasticsearch: Gizli Bir Hazine — Search Template

Elasticsearch’ün hem geliştiricilere hem de son kullanıcılara sunduğu güzelliklere — ki güzellik hafif kalır — dokunmaya devam etmekte fayda var diye düşündüm ve bir başka güzelliği elim döndüğünce (dilim döndüğünce deyimi kadar etkili olmadı, kabul) anlatmak için yola koyuldum.

Search Template Nedir?

Aslına bakarsanız Search Template çok yeni bir özellik değil Elasticsearch dünyasında. Hatta 1.1.0 versiyonundan beri varlığını sürdürüyor. Ancak bu zamana dek ne internet dünyasındaki örneklerde, ne dinlediğim sunum ve konuşmalarda kendisinden çok bahsedildiğine şahit olmadım. Belki de tanınırlık olarak istenilen düzeyde değildir diye düşünerek popülaritesine +1 demek adına kendisine tanıtmak isterim.

Elasticsearch, ilgilenenlerin yakınen bileceği üzere belirli kurallar çerçevesinde oluşturulmuş JSON tabanlı ifadeler üzerinden iletişim (request — response) gerçekleştirilen bir platform. En basitinden bir Elasticsearch sorgusu şu şekilde oluşuyor:

"query": {
"match": {
"title": "aradığım ifade"
}
}

Son cümleyi tekrarlayalım: En basitinden bir Elasticsearch sorgusu. Peki daha kapsamlı sorgular? Herhangi bir e-ticaret sitesindeki filtre ekranını gözümüzün önüne getirelim:

A, B, C markalarına ait, son 14 gün içerisinde eklenmiş, 100–500 ₺ fiyat aralığında, en az 500 GB kapasiteli, 5400 RPM dönüş hızına sahip, kullanıcılar tarafından en az 4 yıldız almış bir HDD arıyorum.

Elasticsearch mapping’inize bağlı olarak değişkenlik göstermekle birlikte kompleks bir sorgu olduğunu söylemek yanlış olmayacaktır. Uygulama üzerinden böyle bir sorgu üretmek ve Elasticsarch API’sine göndermek istesem nasıl bir yol izlerdim?

String ifadeleri birleştirerek JSON oluştururum

Bir metin editörü üzerinden JSON sorgumu oluştururum. Sonra da sorgudaki satırları tek tek StringBuilder ya da benzeri bir nesne ile birbirine ekler, bu şekilde API’ye gönderirim.

Öncesinde sorguyu Elasticsearch’te çalıştırdığınızdan ve aldığınız sonuçtan %100 eminseniz kabul etmeliyim ki kötü bir yöntem değil. Murphy Kanunları’nın da söylediği gibi;

If it’s stupid but it’s work, it’s not stupid

Ama şu konuda da sanırım hem fikiriz ki, bakımı çok zor. Sorguda bir değişiklik yapmaya çalıştığınızda sizi bekleyen bir reverse-engineering var. Bir kod dosyası içerisinde yüzlerce metinsel ifade, hangisinin hangisiyle bağlantısı olduğunu bulmak imkansız! Temiz kod prensiplerini es geçtiğimizi de söylememek haksızlık olacaktır.

Bu yöntemden vaz mı geçtiniz, pekala o halde ikinci yöntem.

Bir client library kullanırım.

Bakın bu daha evrensel olarak kabul görmüş bir yöntem. Üstelik geliştirmelerinizi .NET ortamında gerçekleştiriyorsanız yine Elastic tarafından geliştirilen bir kütüphane sizi karşılıyor: NEST. Detaylara bu linkten erişmeniz mümkün.

NEST, Elasticsearch’ün sağlamış olduğu tüm fonksiyonalitenin kod tarafında karşılığını veren bir kütüphane. Oldukça da başarılı. Biz de Hürriyet mutfağında bu kütüphanenin müdavimlerindeniz. Ancak bu kütüphaneyi kullanırken bir sorgu oluşturmaya çalıştığınızda birbirine zincirlenmiş fonksiyonlar kullanmanız gerekiyor ve bu da kodun okunurluğunu elbette zorlaştırıyor. Aşağıdaki örnek Elastic’in github sayfasından alıntıdır:

Kaynak: https://github.com/elastic/elasticsearch-net-example/tree/5.x-codecomplete

Yazının asıl başlığıyla ilgili paragrafa geçmeden önce bu iki yöntemin bence en büyük handikapı şu ki; sorgunuzda bir değişiklik yapmanız gerektiğinde (örneğin yukarıdaki kod bloğunda yer alan Max Boost değerini 10'dan 100'e yükseltmek istiyorsunuz) ana uygulamanızda bir değişiklik yapma gereği duymanız. Bu da Seperation of Concerns prensibiyle tam bir çelişki durumuna işaret ediyor.

Search Template kullanırım

Böylece Elasticsearch sorgularımı uygulama katmanından ayırır, uygulamama sadece istediğim parametreyi ilgili sorguya gönderme sorumluluğunu yüklerim. Ayrıca bir kütüphane bağımlılığını da ortadan kaldırırım çünkü buradan sonra yapacağım tek aksiyon, parametreleri bir HTTP client nesnesi ile Elasticsearch API’sine göndermek olacak.

Search Template yapısı SQL Server ile çalışan kişilerin aşina olacağı Stored Procedure yapısına benzer nitelikte. Sorguların genel yapısını isterseniz Elasticsearch veritabanı içerisinde, isterseniz Elasticsearch’ün kurulu olduğu sunucudaki “/config/scripts” klasörü içerisinde tutabilirsiniz.

Template olarak tasarlamayı düşündüğünüz sorguları Mustache template dilinin özellikleri ile oluşturmalısınız. Başta verdiğimiz örnek Elasticsearch sorgunu template seviyesine çektiğimizde şöyle bir çıktı oluşuyor:

Çift süslü parantez içerisindeki değer (query_string) parametre olarak işaretlediğimiz değer. Bu template’ı render etmek ve sorguyu çalıştırmak için ilgili parametreyi çağrımıza dahil etmeliyiz.

Küçük bir örnekle konuyu tamamlayalım. Bunun için Docker’da bir Elasticsearch instance’ı oluşturalım:

docker pull docker.elastic.co/elasticsearch/elasticsearch:5.4.3
docker run --name elastic -d -p 9200:9200 -p 9300:9300 -e "http.host=0.0.0.0" -e "transport.host=127.0.0.1" -e "xpack.security.enabled=false" docker.elastic.co/elasticsearch/elasticsearch:5.4.3

Sonrasında bir index oluşturalım ve index’e bazı kayıtlar girelim. Ben yazının çok uzun olmaması için bu komutları buraya eklemedim ancak dilerseniz örnek projenin yer aldığı Github repo’ma yazının sonundaki linkten ulaşabilirsiniz. Kayıt girme işlemi de tamamlandıktan sonra bir template oluşturalım:

Bundan sonra geriye kalan tek şey, ilgili template’e parametre gönderip HTTP sorgusunu yapacak uygulamayı geliştirmek. C# ile bu işlemi şu şekilde örnekleyebiliriz:

17. satırda template’in id’sini gönderiyoruz. Eğer template’i file olarak tutsaydık bu parametrenin adı “file” olacaktı. 18. satırda ise template’in beklediği parametreleri “params” isimli parametre ile tanımlıyoruz.

29–50. satırlar arasındaki fonksiyon Elasticsearch API’sine HTTP isteği göndermekle sorumlu. Hiç bir şekilde bu fonksiyona dokunmadan istediğiniz parametreler ile istediğiniz template’i çağırabilirsiniz. Yaptığı iş isteği göndermek ve cevaptan gelen JSON’ı parse edip belirttiğiniz nesneye çevirip geri döndürmek.

Bu yapının bir çok detayı mevcut (dinamik template oluşturma, template objelerini formatlama, vs…). Yapıyla ilgili son güncellemeleri ve resmi detayları ilgili adresten takip edebilirsiniz. Örneğin tamamının yer aldığı Github repository’sine ise aşağıdan ulaşabilirsiniz:

Bir sonraki yazıya kadar istekleriniz hep temiz ve sorunsuz, aldığınız cevaplar hep 2xx ailesinden olsun :)