geri

ElasticSearch facets ile kolay veri gruplama (aggregation)

17/11/2012
ElasticSearch, geliştiricilerinin deyimiyle Apache Lucene üzerine kurulmuş açık kaynak(Apache 2), dağıtık ve RESTful bir arama motorudur.

Bu yazıda ElasticSearch facets kullanarak verinin nasıl kolayca gruplanabileceğini özetlemeye çalıştım. Aynı işi daha önce MongoDB ile yapmaya çalıştığımda oldukça güçlük çekmiştim. Ancak bu deneyimi MongoDB Aggregation Framework yayınlanmadan önce yaşamıştım. Şimdi durum MongoDB için çok daha iyi olabilir. Bu konuda bilginiz var ise yazının altına yorum yazarsanız memnun olurum.

ElasticSearch konusunda biraz fikriniz olması amacıyla facets için vereceğim ilk örnek kendi dokümantasyonundan olacak. Eğer bu konuda zaten bilgi sahibi iseniz bir sonraki aşamada anlatmaya çalıştığım kullanıcı senaryosuna geçebilirsiniz.

Temel veri gruplama

Örneğimizde bir başlığa ve bir ya da daha fazla sayıda etikete sahip makalelerimiz olacak. Yazacağımız sorgu verdiğimiz koşullara uyan makaleler için en çok kullanılan etiketleri döndürecek. İşe önce birkaç makale yaratarak başlayalım. ElasticSearch ile tamamen RESTful bir arayüz üzerinden iletişim kuruyorsunuz. Bunun için oldukça işlevsel olan curl aracını kullanacağız.

curl -X DELETE "http://localhost:9200/articles"
curl -X POST "http://localhost:9200/articles/article" -d '{"title" : "One",   "tags" : ["foo"]}'
curl -X POST "http://localhost:9200/articles/article" -d '{"title" : "Two",   "tags" : ["foo", "bar"]}'
curl -X POST "http://localhost:9200/articles/article" -d '{"title" : "Three", "tags" : ["foo", "bar", "baz"]}'

Yazacağımız sorgu ile başlığı T ile başlayan makaleleri sorgulayalım ve bu makaleler için en çok kullanılan etiketleri döndürelim. Sorguda da görüleceği üzere istediğimiz facet'in adını tags olarak koyduk ve tags alanına göre gruplama yapılacağını belirttik.

curl -X POST "http://localhost:9200/articles/_search?pretty=true" -d '
  {
    "query" : { "query_string" : {"query" : "T*"} },
    "facets" : {
      "tags" : { "terms" : {"field" : "tags"} }
    }
  }
'

ElasticSearch, yaptığımız sorgu için T ile başlayan makalelerin yanı sıra tags adını verdiğimiz facet sonucunu döndürüyor. foo 2 kez, bar 2 kez ve baz 1 kez kullanılmış. Tüm makalelerde foo etiketi aslında 3 kez kullanılmış olduğu halde yazdığımız "başlığı T ile başlayan makaleler için" kısıtı sebebiyle foo etiketinin 2 kez kullanıldığı sonucunu elde ettik.

"facets" : {
  "tags" : {
    "_type" : "terms",
    "missing" : 0,
    "total": 5,
    "other": 0,
    "terms" : [ {
      "term" : "foo",
      "count" : 2
    }, {
      "term" : "bar",
      "count" : 2
    }, {
      "term" : "baz",
      "count" : 1
    } ]
  }
}

Kullanıcı senaryosu

Kullanıcı senaryom tüm HTTP isteklerinin kayıt altına alındığı bir durum olacak. Tüm kayıtlar yüksek performans gereksinimi dolayısıyla ElasticSearch kümesine yazılıyor olsunlar. Her kayıtta istek URL'i(url), istek zamanı(tarih) ve yanıtın ne kadar sürede tamamlandığı(sure) bilgileri yer alacak. Daha sonra bu bilgileri kullanarak herhangi bir istek için yanıt süreleri hakkında bilgi sahibi olmaya çalışacağız. Böylece isteklerle ilgili bir tür profiling yapmış olacağız.

Buradaki gruplama yukarıda verdiğim örnekten biraz daha farklı olacak. Çünkü bu kez bir tarih alanı için belirteceğimiz aralıklarda gruplama yapacağız. Bu işlem için ElasticSearch, Date Histogram Facet isimli harika bir facet sağlıyor. Böylece isteğimize göre saatlik, günlük gibi değişken aralıklarda gruplama yapabileceğiz. Yine işe üç adet kayıt ekleyerek başlayalım. İlk istek 2012-11-17T12:15:30 tarihinde, ikinci istek 2012-11-17T13:17:20 tarihinde ve üçüncü istek 2012-11-17T13:29:10 tarihinde yapılmış olsun.

curl -X DELETE "http://localhost:9200/istekler"
curl -X POST "http://localhost:9200/istekler/istek/1" -d '{"url" : "/index.html", "tarih" : "2012-11-17T12:15:30Z", "sure": 100 }'
curl -X POST "http://localhost:9200/istekler/istek/2" -d '{"url" : "/index.html", "tarih" : "2012-11-17T13:17:20Z", "sure": 150 }'
curl -X POST "http://localhost:9200/istekler/istek/3" -d '{"url" : "/index.html", "tarih" : "2012-11-17T13:29:10Z", "sure": 200 }'

Şimdi de tarih alanına göre tüm /index.html isteklerini saatlik bazda gruplayalım.

curl -X POST "http://localhost:9200/istekler/_search?pretty=true" -d '
{
    "query" : { 
    	"field" : {
    		"url" : "/index.html"
    	} 
    },
    "facets" : {
        "istekler" : {
            "date_histogram" : {
                "field" : "tarih",
                "interval" : "hour"
            }
        }
    }
}'

Yanıtı yine JSON formatında alıyoruz. 17 Kasım 2012 günü Saat 12:00 için 1 kayıt, saat 13:00 için 2 kayıt bulduğunu görebiliyoruz (Zaman bilgisini timestamp olarak aldık. Buradan timestamp bilgisini okunur bir tarihe çevirebilirsiniz). Bu arada took alanı işlem süresini belirtiyor. Sorgu sonucunu yalnızca 2 milisaniyede elde ettiğimize dikkatinizi çekmek isterim.

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0,
    "hits" : [ {
      "_index" : "istekler",
      "_type" : "istek",
      "_id" : "1",
      "_score" : 1.0, "_source" : {"url" : "/index.html", "tarih" : "2012-11-17T12:15:30Z", "sure": 100 }
    }, {
      "_index" : "istekler",
      "_type" : "istek",
      "_id" : "2",
      "_score" : 1.0, "_source" : {"url" : "/index.html", "tarih" : "2012-11-17T13:17:20Z", "sure": 150 }
    }, {
      "_index" : "istekler",
      "_type" : "istek",
      "_id" : "3",
      "_score" : 1.0, "_source" : {"url" : "/index.html", "tarih" : "2012-11-17T13:29:10Z", "sure": 200 }
    } ]
  },
  "facets" : {
    "istekler" : {
      "_type" : "date_histogram",
      "entries" : [ {
        "time" : 1353153600000,
        "count" : 1
      }, {
        "time" : 1353157200000,
        "count" : 2
      } ]
    }
  }
}

Saatlik bazda kaç kez istek yapıldığı bilgisini elde ettik ama bizim asıl gereksinim duyduğumuz bilgi saatlik bazda yanıt sürelerinin ne şekilde olduğu idi. Bunun için gruplamayı tarih alanına göre yaparken bilgilerin sure alanına göre dönüşünü sağlamamız gerekmekte. Bu doğrultuda key_field ve value_field alanlarını kullanacağız.

curl -X POST "http://localhost:9200/istekler/_search?pretty=true" -d '
{
    "query" : { 
    	"field" : {
    		"url" : "/index.html"
    	} 
    },
    "facets" : {
        "istekler" : {
            "date_histogram" : {
                "key_field" : "tarih",
                "value_field" : "sure",
                "interval" : "hour"
            }
        }
    }
}'

Yukarıdaki sorgunun yanıtı olarak aşağıdaki harika bilgileri elde ediyoruz.

{
  "took" : 19,
  "timed_out" : false,
  "_shards" : {
    ...
  },
  "hits" : {
    ...
  },
  "facets" : {
    "istekler" : {
      "_type" : "date_histogram",
      "entries" : [ {
        "time" : 1353153600000,
        "count" : 1,
        "min" : 100.0,
        "max" : 100.0,
        "total" : 100.0,
        "total_count" : 1,
        "mean" : 100.0
      }, {
        "time" : 1353157200000,
        "count" : 2,
        "min" : 150.0,
        "max" : 200.0,
        "total" : 350.0,
        "total_count" : 2,
        "mean" : 175.0
      } ]
    }
  }
}

Gelen yanıta göre saat 12:00-12:59 aralığında 1 istek yapılmış ve yanıt süresi ortalaması 100 ms olmuş. Saat 13:00-13:59 aralığında 2 istek yapılmış ve istek süresi ortalaması 175 ms olarak gerçekleşmiş.

ElasticSearch facets hakkında daha fazla bilgi için proje dokümantasyonunu okumanızı tavsiye ederim.

Follow me on Twitter