Kibana Query Language (KQL) nedir?

27 Haz

Kibana Query Language (KQL), Kibana uygulamasında kullanılan bir sorgulama dilidir. Bu dil, Kibana uygulamasındaki Discover bölümünün en temel bileşenidir. KQL sayesinde Elasticsearch üzerindeki index’lerde basit bir şekilde arama işlemleri gerçekleştirilebilmektedir. Yazılan basit sorgular, arka planda otomatik olarak Elasticsearch DLS query formatına dönüştürülerek aramalar yapılmaktadır.

Kibana Query yazma penceresi

Örnek KQL kodu:

product.price : 200

Dönüştürülmüş Elasticsearch query kodu:

 "query": {
   "bool": {
	"filter": [
	  {
		"bool": {
		  "should": [
			{
			  "match": {
				"product.price": "200"
			  }
			}
		  ]
		}
	  }
	]
  }
}

Karmaşık Elasticsearch DSL sorguları tek bir satırda basit bir şekilde KQL ile gerçekleştirilebilmektedir.

KQL free text search

Her bir kelimeyi ayrı ayrı arayıp bulmak için kelimeler arasında boşluklar bırakılarak yapılan aramalardır. Örneğin içinde apple veya banana geçen dokümanları elde etmek için aşağıdaki gibi aralarında boşluklar bırakarak arandığında ilgili Elasticsearch DSL sorgusu üretilecektir. Free text search, elastic tarafında multi_match türünde bir filter uygular. Dokümandaki tüm alanlarda ilgili kayıtların aranması sağlanır. Bunu sağlayan “best_fields” parametresidir.

apple banana
"query": {
  "bool": {
	"filter": [
	  {
		"multi_match": {
		  "type": "best_fields",
		  "query": "apple banana",
		  "lenient": true
		}
	  }
	]
  }
}

KQL free text – phrase search

Bu tür aramalarda, ilgili metin tırnak içinde aratılarak ilgili kelime veya cümle ile tam eşleşen dokümanlar aranmaktadır. Bunu sağlayan “phrase” parametresidir. Örneğin “apple banana” araması yapıldığında, doküman alanlarında aranan metni aynı şekilde arar. apple ve banan şeklinde ayırıp aramaz.

"apple banana"
"query": {
  "bool": {
	"filter": [
	  {
		"multi_match": {
		  "type": "phrase",
		  "query": "apple banana",
		  "lenient": true
		}
	  }
	]
  }
}

KQL field level search

Bu aramalarda arama kutusuna <field name><operator><value> şeklinde giriş yapılır.

product.name: computer

Bu aramalarda; “:” operatörü eşittir anlamına gelmektedir. Bu aramanın DSl sorgusu aşağıdaki gibidir.

"query": {
  "match": {
	"product.name": "computer"
  }
}
Range Operatörleri:    <      >      <=      >=

Bu operatörlerin DSL dönüşümü aşağıdaki gibidir.

product.price < 2500 
"query": {
  "range": {
	"product.name": { "lt": 250 }
  }
}

KQL boolean operators

product.size >= 100 and product.size <= 500
"query": {
  "bool": {
	"filter": [
	  {
		"range": {
		  "product.size ": { "gte": 100 }
		}
	  },
	  {
		"range": {
		  "product.size ": { "lte": 500 }
		}
	  }
	]
  }
}
product.size: 100 or product.size: 500
"query": {
  "bool": {
	"should": [
	  {
		"match": {
		  "product.size ": 100
		}
	  },
	  {
		"match": {
		  "product.size ": 500 
		}
	  }
	]
  }
}

Boolean kısa yazım şekli

product.size: ( 100 or 500 )

KQL boolean operator grup

Sorgulamalarda parantezlar yardımı ile boolean ifadeler gruplanabilir.

product.price: 1500 and (product.size: M or Product.size: L)

KQL boolean ters

İfadelerin başına “not” yazılarak ters mantıksal işlem yapılabilir. Örneğin kategorisi “computers” olmayanlar için sorgu:

not product.category: computers

Gruplanmış ifadelerde de ters mantıksal işlemler kulanılabilir.

product.price: 1500 and not (product.size: M or Product.size: L)

Wildcard (*) ifadesiyle ilgili alana sahip dokümanların aranması

İlgili alana sahip olan dokümanların aranması için aşağıdaki sorgu kullanılabilir. Örneğin product.category alanını içeren dokümanları aramak için:

product.category: *
"query": {
  "exist": {
	"field": "product.name"
  }
}

Wildcard (*) ifadesiyle ilgili alana ve değere sahip dokümanların aranması

product.category: phones*
"query": {
  "query_string": {
	"fields": ["product.name"],
        "query": "phones*"
  }
}

Elasticsearch null value

24 Şub

Elasticsearch üzerinde null değerler index üzerine eklenemez ve aranamaz. Yani property değeri null olan kayıtları getirmek için bir sorgu yazdığınızda doğru cevap alınamaz. Bir alan null olarak girildiğinde, o alanın değeri yokmuş gibi değerlendirilir. Bir alan boş bir dizi olarak girildiğinde de yokmuş gibi değerlendirilir.

Örneğin, aşağıdaki sorguda, name değeri null olan kayıtlar alınamaz.

GET products-idx/_search
{
  "query": {
    "term": {
      "name": null 
    }
  }
}

Bu gibi durumlarda, index mapping oluşturulurken, alana ait property ayarlarında null_value parametresi ile null yerine bir değer atanır.

PUT products-idx
{
  "mappings": {
    "properties": {
      "name": {
        "type": "keyword",
        "null_value": "NULL" 
      }
    }
  }
}

Burada, null olarak gelen name değerleri, “NULL” ile değiştirilmiştir. Bu sayede NULL şeklinde arama yapmak mümkün hale gelmektedir. Aşağıdaki şekilde yeni değerler eklendiğinde;

PUT products-idx/_doc/1
{
  "name": null
}

PUT products-idx/_doc/1
{
  "name": []
}

Artık aşağıdaki gibi arama yapılabilmektedir.

GET products-idx/_search
{
  "query": {
    "term": {
      "name": "NULL" 
    }
  }
}

Boş diziler null değere sahip olmadığı için “NULL” ile değiştirilmez. Bu sorgunun sonucunda 1 numaralı doküman elde edilir ancak 2 numaralı doküman elde edilemez.

Not: null_value değeri alan veri tipi ile uygun olmalıdır. Örneğin long tipinde bir alana string tipinde null_value değeri atanamaz.

Not: null_value yalnızca verilerin index’e alınma şeklini etkiler, _source dokümanını değiştirmez.

Elasticsearch Mapping

22 Oca

Elasticsearch dünyasında mapping, bir dokümanın (document) ve onun alanlarının (fields) nasıl tutulacağı ve indeksleneceğini belirtir.

Mapping tanımlamalarının düzgün yapılması ile;

  • Elasticsearch performansı optimize edilir.
  • Disk alanlarından tasarruf edilir.

(Bu yazıda, doküman indeksleme işlemleri sırasında gösterilen örnekler, kibana developer tools üzerinden yapılabilir.)

Doküman indexleme

Aşağıdaki istek ile bir doküman indeksi oluşturmak mümkündür.

POST <index-name>/_doc
{
  "field": "value"
}

Örnek:

POST fruits/_doc
{
  "name": "Banana",
  "produce_type": "Fruit",
  "date_purchased": "2020-06-02T12:15:35",
  "quantity": 120,
  "unit_price": 6.21,
  "description": "a tropical fruit.",
  "vendor_details": {
    "vendor": "Tropical Fruit Growers of Turkey",
    "contact": "Bayram Üçüncü",
    "location": "Turkey"
  }
}

Bu istek çalıştırıldığında aşağıdaki gibi bir cevap dönecektir.

{
  "_index" : "fruits",
  "_type" : "_doc",
  "_id" : "68LPgn4BbIMmYeUuNtYB",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

Elasticsearch üzerinde indeksler için iki tür mapping oluşturma yöntemi vardır. Bunlar;

  • Dynamic mapping (Dinamik Eşleştirme)
  • Explicit mapping (Önceden Belirlenen Eşleştirme)

Dynamic Mapping (Dinamik Eşleştirme)

Bir kullanıcı yukarıdaki örnekte olduğu gibi mapping tanımını önceden yapmadığında, Elasticsearch eşlemeyi varsayılan olarak gerektiği gibi oluşturur veya günceller. Bu dinamik eşleştirme (dynamic mapping) olarak bilinir.

Dinamik eşleştirmede, alan tipleri veri içeriğinden çıkarılmaktadır. Bu noktada eğer index oluşturulurken girilen verilerden biri sayısal olarak girilmişse ve başka verilere göre metinsel değerler de alabilecek durumdaysa, örneğin float olarak oluşturulan bir alana metinsel bir ifade yazılmaya çalışıldığında hata oluşacaktır.

Dinamik eşleme ile Elasticsearch her alana bakar ve alan içeriğinden veri türünü çıkarmaya çalışır. Ardından, her alana bir tür atar ve eşleme olarak bilinen alan adları ve türlerin bir listesini oluşturur.

Atanan alan türüne bağlı olarak, her alan dizine alınır ve farklı istek türleri (full text search, aggregations, sorting) için hazırlanır. Bu nedenle eşleştirme, Elasticsearch’ün verileri nasıl depoladığı ve aradığı konusunda önemli bir rol oynar.

Bir indekse ait mapping değerlerine ulaşmak için şu sorgu çalıştırılabilir.

GET fruits/_mapping
{
  "fruits" : {
    "mappings" : {
      "properties" : {
        "date_purchased" : {
          "type" : "date"
        },
        "description" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "name" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "produce_type" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "quantity" : {
          "type" : "long"
        },
        "unit_price" : {
          "type" : "float"
        },
        "vendor_details" : {
          "properties" : {
            "contact" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "location" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "vendor" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }
          }
        }
      }
    }
  }
}

Mapping JSON verisi içerisinde, indeksteki alanların adlarının ve türlerinin bir listesini bulunur. Elasticsearch alan türleri (field types) bilgilerine detaylı olarak buradan ulaşılabilir.

Metin İndeksleme

Metinsel alanlar için iki farklı tür bulunur:

  1. Text field
  2. Keyword field

Varsayılan olarak, her string, metin (text) alanı ve anahtar kelime (keyword) çoklu alanı olarak iki kez eşlenir. Her alan türü, farklı istek türleri için hazırlanır.

Text alan türü, full-text search yapabilmek için tasarlanmıştır.

Keyword alan türü, kesin aramalar (exact searches), toplamlar (Aggregations) ve sıralamalar (sorting) için tasarlanmıştır.

Text Field (Metin Alanlar)

Metin Analizi

Elasticsearch’te arama yaptığınızda, büyük/küçük harfe duyarlı olmadığını veya noktalama işaretlerinin önemi yoktur. Bunun nedeni, veriler indekse eklendiğinde, metin analizinin gerçekleşmesidir.

Varsayılan olarak, metinler indekse alındığında analiz edilir. Metin, belirteçler (token) olarak da bilinen ayrık sözcüklere bölünür. Çözümleyici (analyzer), her kelimeyi küçük harflere dönüştürür ve noktalama işaretlerini kaldırır.

Analiz sonucunda ortaya çıkan metinler, bir inverted index tablosuna kaydedilir.

Bu işlem, indekse her doküman eklendiğinde tekrarlanır. Zamanla inverted index tablosu aşağıdaki gibi değişikliğe uğrar.

Bu durumda, “this book” şeklinde bir arama yapıldığında, arama sonucunda 1,2,3 numaralı dokümanlar bulunur.

Keyword Field (Anahtar Alanlar)

Keyword alanlar, kesin aramalar, toplamlar ve sıralamalar için kullanılır. Aranan değerlerde, ilgili dokümanın sahip olduğu değerleri bulmak için doküman kimliğini tarar

PUT fruits/_doc/1
{
  "country": "İstanbul"
}
PUT fruits/_doc/2
{
  "country": "Ankara"
}
PUT fruits/_doc/3
{
  "country": "Manisa"
}
PUT fruits/_doc/4
{
  "country": "Trabzon"
}
PUT fruits/_doc/5
{
  "country": "Van"
}
PUT fruits/_doc/6
{
  "country": "Antalya"
}
PUT fruits/_doc/7
{
  "country": "Bolu"
}

Elasticsearch sizin için dinamik olarak bir eşleme oluşturduğunda, bir metni ne için kullanmak istediğinizi bilemez, bu nedenle tüm metinleri her iki alan türüne eşler. Örneğin vendor alanı:

            "vendor" : {
              "type" : "text",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            }

Her iki alan türüne de ihtiyacınız olmadığı durumlarda, disk alanları israf edilmiş olur. Çünkü inverted index kullanımına gerek olmayan durumlarda gereksiz yere inverted index alanları oluşturulmuş olur.

Explicit Mapping (Önceden Belirlenen Eşleştirme)

Mapping (tip eşleştirme) işlemini kendiniz tanımlamak istediğiniz durumlarda, bunu index oluşturma sırasında yapabilirsiniz. Bu sayede eşleştirme tanımını kendiniz tasarlayabilirsiniz. Örneğin:

  • Hangi metinsel alanların Full-text search alanları olarak ele alınacağını belirtebilirsiniz.
  • Hangi alanların sayı, tarih veya geolocation olacağını belirtebilirsiniz.
  • Tarih alanlarının biçimini belirtebilirsiniz.
  • Kaynak kullanımını ve performansı dengelemek için çalışma zamanı alanlarını (runtime fields) indekse alınmış alanlarla birlikte kullanabilirsiniz. Index daha küçük olacaktır, ancak arama performansı daha yavaş olacaktır.

Her index için tek bir eşleştirme (mapping) belirlenir. İndeks oluşturulduktan sonra, bir eşlemeye yalnızca yeni alanlar eklenebilir. Mevcut bir alanın eşleşmesi değiştirilemez. Eğer mevcut bir alanın türünün değiştirilmesi gerekiyorsa, istenen eşlemeyle yeni bir indeks oluşturulur ve ardından tüm dokümanlar yeni dizine yeniden aktarılır.

PUT fruit_index
{

    "index": {
      "number_of_replicas": "1",
      "number_of_shards": "2"
    },
    "mappings" : {
      "properties" : {
        "date_purchased" : {
          "type" : "date"
        },
        "description" : {
          "type" : "text"
        },
        "name" : {
          "type" : "text"
        },
        "produce_type" : {
          "type" : "text"
        },
        "quantity" : {
          "type" : "long"
        },
        "unit_price" : {
          "type" : "float"
        },
        "vendor_details" : {
          "enabled" : false
        }
      }
    }
}

Field tanımlamaları properties içerisinde belirtilmektedir. Her field için ad ve tip belirtilmektedir. Kendi oluşturduğunuz eşleştirmelerde gereksiz alan tanımlamalarını iptal ederek optimum bir indeks oluşturabilirsiniz. Oluşturduğunuz indekslerde, alanlara istediğiniz analyzer atamalarını yapabilirsiniz.

Dikkat

Bir indekste çok fazla alan tanımlamak, yetersiz bellek hatalarına ve kurtarılması zor durumlardan kaynaklanabilecek bir eşleme patlamasına neden olabilir. Eklenen her yeni belgenin dinamik eşleme gibi yeni alanlar sunduğu bir durumu düşünün. Her yeni alan, indekste mapping olarak eklenir ve mapping büyüdükçe bir sorun haline gelebilir. Bu durumlara karşı Elasticsearch ayarlarında “mapping limit settings” kullanılarak, mapping alan sayısı sınırlandırılabilir.

Kaynaklar

  • https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html
  • https://github.com/LisaHJung/Part-5-Understanding-Mapping-with-Elasticsearch-and-Kibana
  • https://www.elastic.co/guide/en/elasticsearch/reference/current/runtime-mapping-fields.html
  • https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-settings-limit.html