DevVeri.com

Boğulacaksan büyük veride boğul!

ElasticSearch ve Native Script Kullanımı

elasticsearchElasticSearch gün geçtikçe popülerliğini arttırıyor. Özellikle Apache Solr projesine çok ciddi bir rakip olarak ortaya çıktı ve barındırdığı özellikler sayesinde Solr projesinin de gelişmesini sağladığını söyleyebiliriz. Solr 4.x sürümü ile beraber gelen Cloud özelliklerini ElasticSearch olmasaydı herhalde daha uzun süre kullanamayabilirdik.

ElasticSearch birçok yönden Solr’dan farklı. Sıfır konfigürasyon ile kullanmaya başlıyabiliyorsunuz. Ayrıca yeni sunucular ekledikçe otomatik olarak küme oluşturuyorlar, bu sayede peformans ve ölçeklenebilirlik kolayca sağlanıyor.

Ancak biraz daha ciddi birşeyler yapmak istediğiniz zaman ElasticSearch’ün sıkıntılarını yaşamaya başlıyorsunuz. Öncelikle arkasındaki community gerçekten Apache ile kıyaslanamayacak kadar küçük. Bunu fazlasıyla yetersiz dökümantasyonu içerisinde debelenirken çok ciddi hissediyorsunuz. Özellikle Java API ve dökümantasyonu çok yetersiz.

Bir başka problemli konu River konusu. Solr’daki DataImportHandler mantığının ElasticSearch karşılığı olan River malesef her seferinde indeksi baştan oluşturacakmış gibi konfigüre edilebiliyor. Sunucu her açıldığında River otomatik çalışarak tüm veriyi(!) indekslemeye çalışıyor. Eğer milyonlarca satırdan oluşan bir tabloyu vs indekslemek isterseniz sizin için pek kullanışlı değil. Belki ince ayarı vardır ancak ben bulamadım. Hem JDBC hem Mongo ile benzer problemler yaşayınca, indeksi kendi yazdığım kod ile oluşturup/güncellemeye karar vermiştim.

Bunun gibi sebeplerle ve tabiki şahsi fikrim, Solr kesinlikle açık ara çok daha kurumsal bir proje niteliğinde. Karmaşık konfigürasyonları Solr ile yapmak hem çok rahat hem de çok fazla özellik barındırıyor. Ancak ElasticSearch gelişmeye çok açık ve farklı kullanım alanları olabilecek bir araç. Özellikle log uygulamalarında NoSQL veritabanı gibi kullanıldığında gerçekten faydalı olabiliyor.

Eğer ElasticSearch kullanıyorsanız ve dökümanlarınızı belirli bir mantığa göre kendiniz sıralamak isterseniz o zaman ElasticSearch size scripting desteği sunuyor. Tabiki JavaScript burada varsayılan dil. Yani dökümanları sizin yazdığınız bir JS ile elde edilecek skora göre dizebiliyorsunuz.

Bazı durumlarda ise yapmak istediğiniz işlemi JS ile yazamıyor olabilirsiniz. Bu durumda Native dil desteği sayesinde Java kodu yazarak istediğiniz şekilde sıralama yaptırabilirsiniz. (Bu özelliğe Native Script denmesi bana soracak olursanız biraz saçma olmuş ancak gelin beraber nasıl yapıldığını inceleyelim)

NativeScript özelliğini kullanabilmek için, NativeScriptFactory sınıfından bir adet factory, bir adet de AbstractDoubleSearchScript benzeri bir sınıftan türeyen script sınıfını yazmamız gerekiyor. Burada farklı tipte sonuç dönmek için farklı sınıflar mevcut, ama bizim hesaplayacağımız skor double cinsinden olacağı için bunu tercih ettim. ElasticSearch üzerinde arama yaparken bu script’i kullanmasını ilettiğimizde, her döküman için tek tek bu script çalıştırılacak ve dökümanlar oluşan bu skora göre sıralanacak. Burada dikkat edilmesi gereken şey, her döküman için çalıştırılacağı için script içerisinde kompleks bir kodun çalışmasının (veritabanına gidip gelmek) pek mümkün olmadığı.

Örnek olması açısından, vereceğimiz bir parametre ile başlayan dökümanların üstte gelmesini sağlamak istediğimizi düşünelim. Bunun için TestScriptFactory sınıfını şu şekilde yazabiliriz:

import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.script.ExecutableScript;
import org.elasticsearch.script.NativeScriptFactory;
import java.util.Map;

public class TestScriptFactory implements NativeScriptFactory {

    @Override 
    public ExecutableScript newScript (@Nullable Map<String,Object> params) {
        return new TestScript(params);
    }

}

Ardından script sınıfına da şöyle bir implementasyon yapabiliriz:

import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.fielddata.ScriptDocValues;
import org.elasticsearch.script.AbstractDoubleSearchScript;

import java.util.Map;

public class TestScript extends AbstractDoubleSearchScript {

    public static final String PREFIX = "prefix";
    public static final String NAME = "name";

    private String memberPrefix;

    public TestScript(@Nullable Map<String,Object> params) {
        memberPrefix = params == null ? null : XContentMapValues.nodeStringValue(params.get(PREFIX), null);
    }

    @Override
    public double runAsDouble() {
        String currentMember = getFieldAsString(NAME);
        if (currentMember != null && currentMember.startsWith(memberPrefix)) {
            return 1;
        }
        return 0.1;
    }

    private String getFieldAsString(String fieldName) {
        ScriptDocValues.Strings fieldData = (ScriptDocValues.Strings) doc().get(fieldName);
        return fieldData.getValue();
    }

}

Burada prefix parametresi ile bize gelen değeri alıp daha sonra skorunu hesaplayacağımız dökümanın name field’ı ile karşılaştırarak, prefix mevcutsa yüksek puan (1.0), değil ise düşük puan (0.1) dönüyoruz. Eğer prefix’in bulunmadığı durumlarda sıfır (0) dönseydik sonuç setinde bu dökümanlar yer almayacaktı.

Yazdığımız bu kodu jar olarak paketleyerek ElasticSearch’ün kurulu bulunduğu dizinin altındaki lib klasörüne atıyoruz. Bu sayede ElasticSearch’ün classpath’ine eklemiş oluyoruz. Unutmadan, küme olarak çalıştırıyorsanız tüm düğümlerin lib klasöründe bu jar’ın bulunması gerekir.

Ardından config dizini içerisindeki elasticsearch.yml dosyasına aşağıdaki tanımı yapıyoruz:

script.native:
  testscript.type: com.devveri.search.script.TestScriptFactory

ElasticSearch açıldıktan sonra, script’imizi şu istek ile test edebiliriz:

curl -XGET "http://localhost:9200/test/doc/_search" -d '{
  "query" :{
     "custom_score": {
       "query" : { "match_all": {}},
       "script" : "testscript",
       "params" :{
          "prefix": "member2"
       },
       "lang": "native"
     }
  }
}'

Örnek kodları GitHub üzerindeki elasticsearch-native-script-example projesinde bulabilirsiniz.

, ,

One thought on “ElasticSearch ve Native Script Kullanımı

  • Özellikle kullanıcı/custom kriterlere göre rank belirlenmesi gereken durumlarda işe yarar bir özellik. “Benim bütün aramalarımda sadece şu kritere uygun sonuçları göster/ benim bütün aramalarımı şuna göre öncelikle” denilesi arama teknolojilerine ön ayak olmuş, günümüz sıkıntılarını kaldırmış..

    Gerçekten çok faydalı bir özelliğe değinmişsiniz, teşekkürler.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

This site uses Akismet to reduce spam. Learn how your comment data is processed.