Elasticsearch: Eş anlamlı kelimeleri nasıl yönetiriz?
sıfat, dil bilgisi Anlamları aynı veya birbirine çok yakın olan (kelimeler), anlamdaş, müradif, müteradif, sinonim
Yukarıdaki satırlar TDK’nın resmi sitesinden alıntı. İşin güzel tarafı ise “eş anlamlı” kelimesi ile eş anlamlı 4 kelimenin daha güzel dilimizde yer bulması.
Methal
Bu başlık da neyin nesi! dediğinizi duyar gibiyim. “Giriş” kelimesinin eş anlamlısı imiş. Dilimizin zenginliği, edebiyatçılarımız başta olmak üzere dil özelliklerini ve güzelliklerini aktif olarak kullanan kişiler için oldukça verimli. Ancak biz teknoloji meraklıları için tam bir kara delik!
Bu kara deliğin uygulamalarımızda en çok hissedildiği yer ise “Arama” olarak ifade ettiğimiz modüllerimiz. Özellikle içerik üreten bir uygulamanın geliştirici iseniz bu konuda karşılaşacağınız isteklerin ve bu isteklere ürettiğiniz çözümlerin sınırları olmadığını kısa sürede hissedeceksiniz.
Sorun Tespiti
- “Doktor” kelimesinin en bilinen eş anlamlısı “hekim”.
- “AFP” kısaltması “Fransız Basın Ajansı (Agence France Presse)” özel ismini ifade eder.
İçerik yönetim sistemimizde iki adet içerik; “Fransız Basın Ajansı prestij ödüllerini dağıttı” ve “Doktorlara kamu görevi zorunluluğu kalkıyor” başlığı ile kayıtlı halde. Biz de sistem üzerinde “afp” ile arama yaptığımızda ilk haberin sonuç setine; “hekim” ile arama yaptığımızda ise ikinci haberin sonuç setine dahil olmasını istiyoruz.
Elasticsearch’ün Synonym (Eş anlamlı) — Abbreviation (Kısaltma) Çözümü
Biz de sistemimizde arama çözümü için Elasticsearch ürünü kullanıyoruz. Bu ürünün yapabileceklerinin sınırı ve başarısı ile ilgili internet ortamında onlarca takdire şayan içerik edinmek mümkün. Bu yazıda bunlara hiç değinmeden anahtar teslim bir çözüm üzerine çalışacağız.
Elasticsearch bu durumlara çözüm olarak “Synonym Token Filter” (1) denilen filtre yöntemini sunuyor. Bu yönteme göre eş anlamlı ve kısaltma ifadelerini bir kural bütünü çerçevesinde oluşturulmuş metin dosyası üzerinde tutabiliyor ve sisteme bu dosya üzerinden filtre operasyonu yapmasını söyleyebiliyorsunuz.
Adım Adım Çözüm…
Öncelikle geliştirme ortamımızda kullanmak üzere Docker üzerinde bir elasticsearch cluster’ı oluşturalım:
Container başarıyla indirildikten sonra ise çalıştıralım:
Yukarıdaki komutun 2 parametresini açıklamakta fayda var diye düşünüyorum:
- -v: Eş anlamlı ve kısaltmaları içeren dosyamı lokal ortamda düzenleyebilmek için bir volume mapping oluşturuyorum.
- -e “xpack.security.enabled=false”: Elasticsearch container’ı bir güvenlik önlemi ile birlikte (kullanıcı adı ve şifre) geliyor. Geliştirme ortamımda bu güvenliğe ihtiyacım olmadığı için environment variable olarak komuta dahil ediyorum.
Sonrasında ise “docker ps” komutu ile container’ın çalışıp çalışmadığını kontrol ediyorum. Eğer aşağıdaki gibi bir ekran bizi karşılıyor ise sorunsuz devam edebiliriz:
İlk olarak aşağıdaki kısaltmaların yer aldığı bir metin dosyası oluşturup C:/volume-mappings/elastic-config/source.txt şeklinde kayıt oluşturalım:
afp => fransız basın ajansı
sağın, sağaltman, sağman, doktor, tabip, hekim
NOT: Config dosyasının doğru oluşturulup oluşturulmadığını; içerikte bir hata olup olmadığını container üzerinde kontrol etmek isterseniz “docker exec -it elastic /bin/bash” komutu ile container’ın içerisine girip “cat config/synonyms/source.txt” komutunu çalıştırabilir ve ekrana dosya içeriğini yazdırabilirsiniz.
Index Oluşturma
Aşağıdaki curl komutu ile index’imizi oluşturalım:
Bu komut aslında en kritik nokta. Öncelikle index içerisinde 2 adet özelleştirilmiş filtre kullanacağımızı belirtiyoruz:
İlkinin adına “synonym” dedik. Bu filtre synonym token filter yapısını kullanacak, eş anlamlı/kısaltma kelimelerini /synonyms/source.txt dosyasından temin edecek ve büyük-küçük harf ayrımı yapmayacak. Dosyadaki ilk satıra göre arama yapılan kelime “afp” olduğunda bu kelime yok sayılacak ve karşılığı olan ifade aramaya dahil edilecek. İkinci satıra göre ise “sağın, sağaltman, sağman, doktor, tabip, hekim” kelimelerinin tamamı eş anlamlı kabul edilecek.
İkinciye ise “ngram” adını verdik. Bu filtre (2) ile kelimenin minimum — 1 — ve maksimum — 20 — karaktere bölünerek varyasyonlarının çıkartılması sağlanacak. Örneğin “kamu” kelimesi “k, ka, kam, kamu, a, am, amu, m, mu, u” varyasyonlarını sağlayacaktır. nGram’ın performansı ile ilgili güzel bir tartışma bu linkte yapılmış. Özetle, index’inizin boyutunu büyütecektir ancak bazı alanlarınızda “içeriyor mu?” sorusuna cevap arayan aramalar yapacaksanız kullanımını öneriyorum.
Sonrasında ise 2 adet analiz metodunu kullanacağımızı belirtiyoruz. Neden iki analiz metodu diye soracak olursanız Elasticsearch şöyle diyor (4):
Arama ve indexleme için kullanacağınız analiz yöntemlerinin farklı olması mantıklı olabilir. Örneğin bir kelimenin eş anlamlılarını indexlemek isteyebileceğiniz gibi, bunların tamamının arama zamanında kullanılmasını istemeyebilirsiniz. Bu durumda analiz yöntemlerinizi farklılaştırmanız önerilir.
Gelelim analiz metodlarımıza:
İlkinin adı “index”. Bu analiz metodu, içerik indexlenirken kullanılacak. İçerik önce ngram filtresinden sonrasında ise sırasıyla synonym, lowercase (harfleri küçültür), asciifolding (karakterleri ASCII karşılıklarına dönüştürür), word_delimeter (kelimeleri bazı kriterlere göre (3) böler) filtrelerinden geçecek ve indexlenecek.
İkincinin adı ise “search”. Bu analiz metodu ise arama esnasında çalışacak. Aranacak olan kelimenin varsa eş anlamlıları alınacak; sonrasında ise sırasıyla lowercase, asciifolding ve word_delimeter filtrelerinden geçirilip indexlenmiş token değerleri ile eşleştirme yapılacak.
NOT — 2: Bu esnada iki analiz metodunun birbirine çok benzediğini fark ettim ve internette “elasticsearch analyzer inheritance” anahtar kelimesi ile arama yaptım. Ancak bulduğum sonuç pek iç açıcı gelmedi.
En sonda ise mapping’imizi yapıyor ve “title” adını verdiğimiz filtrenin index ve search zamanlarında kullanacağı analyzer’ları tanımlıyoruz.
Örnek Kayıtları Ekleme
Aşağıdaki komutu çalıştırarak iki örnek kaydı sisteme dahil edelim:
Sonuç
Şimdi de aşağıdaki sorgu ile “tabip” kelimesini arayalım:
Eğer aşağıdaki sonuç setini aldıysanız aynı mutluluğu paylaşıyor olmalıyız.
“afp” aramasını da sizin örnek uygulamalarınıza bırakıyorum. Alacağınız sonuç bir önceki kadar şok etmese de(!) sevindirici olacaktır.
Bonus — 1
localhost:9200/medium_sample_index/_analyze?text=TABİP&analyzer=search&pretty=true’ şeklinde bir GET isteği yaptığınızda “TABİP” kelimesini ararken hangi token’ların üretildiğini gözlemleyebilirsiniz. Sırasıyla; sağaltman (SYNONYM) -> sağaltman (LOWERCASE) -> sagaltman (ASCIIFOLDING) -> sagaltman (WORD_DELIMETER).
Bonus — 2
http://localhost:9200/medium_sample_index/sample_type/2/_termvectors?fields=title şeklinde bir GET isteği yaptığınızda 2 id’li kayıt için oluşturulan tüm token’ları (index adlı analizden geçen) gözlemleyebilirsiniz.
Bu satıra kadar sabredip okuduğunuz ve bana eşlik ettiğiniz için teşekkürler. Bir sonraki yazıda görüşene dek, kara deliklerinize gün doğsun :)
Kaynakça
(1) https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html
(2) https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-ngram-tokenfilter.html
(4) https://www.elastic.co/guide/en/elasticsearch/guide/current/_controlling_analysis.html