PostgreSQL'de Tam Metin Araması Temelleri
Tam metin araması, arama yapmak için kullanılan kelime filtrelerinin teker teker veritabanımızdaki
kelimelerle karşılaştırmanın kapsamlı yoludur ve birçok yöntemi bulunmaktadır.
PostgreSQL'de tam metin aramasında kullanılan bazı fonksiyonlar vardır: to_tsvector(): İlgili kolondaki veriyi tsvector veri tipine dönüştürür. to_tsquery(): Tsquery operatörleriyle ayrıştırılmış token'lardan oluşan bir tsquery değeri oluşturur.
Kelimelerin sorgulanmasını kolaylaştırmak için kullanılır. to_tsquery ve to_tsvector fonksiyonlarının kullanım örnekleri:
postgres=# SELECT to_tsvector('Tam metin arama örneği') @@ to_tsquery('arama'); ?column? ---------- t (1 row) postgres=# SELECT to_tsvector('Tam metin arama örneği') @@ to_tsquery('Arama'); ?column? ---------- t (1 row) postgres=# SELECT to_tsvector('Tam metin arama örneği') @@ to_tsquery('Tammetin'); ?column? ---------- f (1 row) postgres=# SELECT to_tsvector('İşte size bir tam metin araması örneği') @@ 'işte'; ?column? ---------- f (1 row) |
‘işte’ kelimesinin cümle içinde bulunamamasının nedeni, to_tsquery fonksiyonu ile bazı kelimeleri
token’a dönüştürürken kökünü veya kısasını almasıdır.Aşağıdaki örnekte kelimenin to_tsquery ile tutulduğu halini görebilirsiniz:
postgres=# SELECT 'işte'::tsquery, to_tsquery('işte'); tsquery | to_tsquery ---------+------------ 'işte' | 'işt' |
Bu nedenle aşağıdaki gibi arama yaparsak sonuç true dönecektir:
postgres=# SELECT to_tsvector('İşte size bir tam metin araması örneği') @@ to_tsquery('işte'); ?column? ---------- t (1 row) |
Tam metin aramasında operatörlerin kullanımları Öncelikle bir tablo oluşturalım ve 2 satır veri ekleyelim:
postgres=# CREATE TABLE kitaplar (isim varchar(20), ozet text, yazar varchar(20)); CREATE TABLE postgres=# postgres=# INSERT INTO kitaplar postgres=# VALUES ('Master Algoritma', 'Belki farkında değilsiniz ama yapay öğrenme, hayatın her alanında yayılmış durumda. Kitap satın almak için Amazon.com sitesine veya video izlemek için Netflix’egirdiğinizde yapay öğrenme sistemi size yardımcı olmak için hoşlanabileceğiniz bazı tavsiyelerde bulunur. Facebook size gösterilecek güncellemeleri belirlemek için yapay öğrenmeyi kullanır. Bilgisayarı her kullandığınızda yapay öğrenme muhtemelen bir yerlerde devreye girer.Geleneksel olarak bilgisayarların iki sayıyı toplamaktan bir uçağı uçurmaya kadar herhangi bir şeyi yapmasının tek yolu, bunun nasıl yapılacağını açıklayan bir algoritmayı bütün ayrıntılarıyla yazmaktı.Cep telefonunuz da algoritmalarladoludur. Bunlar yazım hatalarınızı düzeltmek, sesli komutlarınızı anlamak, iletim hatalarını azaltmak, barkodları okumak ve daha birçok şeyi yapmak için sürekli görev başındadır.' , 'Pedro DOMINGOS'); INSERT 0 1 postgres=# INSERT INTO kitaplar VALUES ('Gen Bencildir', 'Şempanze ve insanın evrimsel geçmişlerinin yaklaşık yüzde 99,5’i ortaktır; yine de birçok mantıklı insan şempanzeye eğribüğrü, insanla ilgisiz, tuhaf bir yaratık olarak bakar ve kendisini Mutlak Yaradan’a erişme yolunda bir basamak taşı olarak görür. Evrimci için böyle bir şey olamaz. Bir türü, diğer bir türden üstün kılacak hiçbir nesnel dayanak yoktur. Şempanze ve insan, kertenkele ve mantar, hepimiz, üç milyar sene kadar önce doğal seçilim olarak tanıdığımız bir süreç içerisinde evrimleştik. Her tür içerisinde, kimi bireyler diğerlerinden daha çok sayıda, yaşamını sürdürebilen döl vermişlerdir. Buna bağlı olarak da, üreme bakımından başarılı olan bireyin kalıtsal özellikleri (genler), bir sonraki nesilde sayıca artmıştır. İşte bu doğal seçilimdir (Genlerin farklı, gelişigüzel olmayan üremesi). Bizi doğal seçilim inşa etmiştir ve eğer kendi kimliklerimizi kavrayabilmek istiyorsak anlamamız gereken de bu doğal seçilimdir. ', 'Richard DAWKINS'); INSERT 0 1 |
Tam metin arama için geliştirilmiş fonksiyonları kullanmasaydık, aşağıdaki gibi
bir arama yapacaktık:
postgres=# SELECT isim FROM kitaplar WHERE ozet LIKE '%insan%' OR ozet LIKE '%bir%'; isim ------------------ Master Algoritma Gen Bencildir (2 rows) |
Bu aramayı yapmak için PostgreSQL tüm tabloyu taradı. Büyük veri setlerinde bu
sorgu çok uzun sürer ve çok performans harcar. Bunun yerine tam metin araması
yapmak için geliştirilen fonksiyonları kullanmak daha karlıdır. Örneğin yukarıdaki sorgunun aynısını to_tsvector() ve to_tsquery() fonksiyonları ile
yazalım. ‘ozet’ kolonunda ‘insan’ veya ‘tür’ geçen kelimelerin olduğu kayıtların
‘isim’ kolonundaki bilgisini sorgulayalım:
postgres=# SELECT isim FROM kitaplar WHERE to_tsvector(ozet) @@ to_tsquery('insan | bir'); isim ------------------ Master Algoritma Gen Bencildir (2 rows) |
‘ozet’ kolonunda ‘insan’ ve ‘tür’ geçen kelimelerin olduğu kayıtların ‘isim’ kolonundaki
bilgisini sorgulayalım:
postgres=# SELECT isim FROM kitaplar WHERE to_tsvector(ozet) @@ to_tsquery('insan & bir'); isim --------------- Gen Bencildir (1 row) |
‘ozet’ kolonunda ‘insan’ kelimesi geçmeyen kayıtların ‘isim’ kolonundaki bilgisini sorgulayalım:
postgres=# SELECT isim FROM kitaplar WHERE to_tsvector(ozet) @@ to_tsquery('!insan'); isim ------------------ Master Algoritma (1 row) |
‘isim’ kolonunda ‘Ben’ ile başlayan verilerin bilgisini sorgulayalım:
postgres=# SELECT isim FROM kitaplar WHERE to_tsvector(isim) @@ to_tsquery('Ben:*'); isim --------------- Gen Bencildir (1 row) |
‘bir’ ve ‘süreç’ kelimelerinin yan yana olduğu kayıtların bilgisini sorgulayalım:
postgres=# SELECT * FROM kitaplar WHERE ozet @@ to_tsquery('bir <-> süreç'); -[ RECORD 1 ]--------------------------------------------------------------------------------------------------------------------------- isim | Gen Bencildir ozet | Şempanze ve insanın evrimsel geçmişlerinin yaklaşık yüzde 99,5’i ortaktır; yine de birçok mantıklı insan şempanzeye eğribüğrü, insanla ilgisiz, tuhaf bir yaratık olarak bakar ve kendisini Mutlak Yaradan’a erişme yolunda bir basamak taşı olarak görür. Evrimci için böyle bir şey olamaz. Bir türü, diğer bir türden üstün kılacak hiçbir nesnel dayanak yoktur. Şempanze ve insan, kertenkele ve mantar, hepimiz, üç milyar sene kadar önce doğal seçilim olarak tanıdığımız bir süreç içerisinde evrimleştik. Her tür içerisinde, kimi bireyler diğerlerinden daha çok sayıda, yaşamını sürdürebilen döl vermişlerdir. Buna bağlı olarak da, üreme bakımından başarılı olan bireyin kalıtsal özellikleri (genler), bir sonraki nesilde sayıca artmıştır. İşte bu doğal seçilimdir (Genlerin farklı, gelişigüzel olmayan üremesi). Bizi doğal seçilim inşa etmiştir ve eğer kendi kimliklerimizi kavrayabilmek istiyorsak anlamamız gereken de bu doğal seçilimdir. yazar | Richard DAWKINS |
‘kitap’ kelimesinden sonraki 7. karakterin ‘video’ olduğu kayıtları sorgulayalım:
postgres=# SELECT * FROM kitaplar WHERE ozet @@ to_tsquery('Kitap <7> video'); -[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------- isim | Master Algoritma ozet | Belki farkında değilsiniz ama yapay öğrenme, hayatın her alanında yayılmış durumda. Kitap satın almak için Amazon.com sitesine veya video izlemek için Netflix’egirdiğinizde yapay öğrenme sistemi size yardımcı olmak için hoşlanabileceğiniz bazı tavsiyelerde bulunur. Facebook size gösterilecek güncellemeleri belirlemek için yapay öğrenmeyi kullanır. Bilgisayarı her kullandığınızda yapay öğrenme muhtemelen bir yerlerde devreye girer.Geleneksel olarak bilgisayarların iki sayıyı toplamaktan bir uçağı uçurmaya kadar herhangi bir şeyi yapmasının tek yolu, bunun nasıl yapılacağını açıklayan bir algoritmayı bütün ayrıntılarıyla yazmaktı.Cep telefonunuz da algoritmalarladoludur. Bunlar yazım hatalarınızı düzeltmek, sesli komutlarınızı anlamak, iletim hatalarını azaltmak, barkodları okumak ve daha birçok şeyi yapmak için sürekli görev başındadır. yazar | Pedro DOMINGOS |
to_tsvector() fonksiyonu ile kolondaki verilerin kaçıncı sırada/sıralarda tutulduğunu görebiliriz.
Örneğin ‘5’ kelimesi 9. sıradaki kelimeymiş ve ‘bir’ kelimesi 22. , 33. , 41. , 44. , 47. , 71. , 100.
sıradaki kelimeymiş. Bu fonksiyonda kelimeler tam haliyle tutulmaz, kısaltılır veya köklerine
ayrılarak tutulur. Örneğin 105. karakter olan ‘işte’ kelimesi, bu fonksiyon sayesinde ‘işt’ olarak
tutulmuş.
postgres=# SELECT to_tsvector(ozet) FROM kitaplar WHERE isim=('Gen Bencildir'); to_tsvector ----------------------------------------------------------------------------------------- '5':9 '99':8 'anlamamız':125 'artmıştır':104 'bakar':25 'bakımından':93 'basamak':34 'bağlı':89 'başarılı':94 'bir':22,33,41,44,47,71,100 'bireyin':96 'bireyl':79 'birçok':14 'bizi':114 'bu':106,128 'buna':88 'böyle':40 'da':91 'daha':81 'dayanak':53 'de':13,127 'diğer':46 'diğerlerinden':80 'doğal':67,107,115,129 'döl':86 'erişm':31 'etmiştir':118 'evrimci':38 'evrimleştik':74 'evrimsel':4 'eğer':120 'eğribüğrü':18 'farklı':110 'gelişigüzel':111 'genler':99 'genlerin':109 'gereken':126 'geçmişlerinin':5 'görür':37 'hepimiz':61 'hiçbir':51 'ilgisiz':20 'insan':16,57 'insanla':19 'insanın':3 'inşa':117 'istiyorsak':124 'içerisind':73,77 'için':39 'işt':105 'kadar':65 'kalıtsal':97 'kavrayabilmek':123 'kendi':121 'kendisini':27 'kertenkel':58 'kimi':78 'kimliklerimizi':122 'kılacak':50 'mantar':60 'mantıklı':15 'milyar':63 'mutlak':28 'nesild':102 'nesnel':52 'olamaz':43 'olan':95 'olarak':24,36,69,90 'olmayan':112 'ortaktır':11 'sayıca':103 'sayıda':83 'sene':64 'seçilim':68,116 'seçilimdir':108,130 'sonraki':101 'sürdürebilen':85 'süreç':72 'tanıdığımız':70 'taşı':35 'tuhaf':21 'tür':76 'türden':48 'türü':45 've':2,26,56,59,119 'vermişlerdir':87 'yaklaşık':6 'yaradan':29 'yaratık':23 'yaşamını':84 'yine':12 'yoktur':54 'yolunda':32 'yüzde':7 'çok':82 'önce':66 'özellikleri':98 'üreme':92 'üremesi':113 'üstün':49 'üç':62 'şempanz':1,55 'şempanzey':17 'şey':42 (1 row) |
Büyük veri setlerinde çalışırken kolondaki karakterleri sıralarına göre ayırıp liste
oluşturmak zor bir işlem olacağı için performans artışı sağlamak için sık sık arama
yapacağınız kolona index eklemeniz gerekir. Gin index tipi bunun için uygun
bir index tipidir. Kolonun veri tipini ‘tsvector’ olarak değiştirip gin index’i aşağıdaki
gibi ekleyebilirsiniz:
postgres=# ALTER TABLE kitaplar ALTER COLUMN ozet TYPE tsvector USING ozet::tsvector; ALTER TABLE postgres=# CREATE INDEX ON kitaplar USING GIN (ozet); CREATE INDEX postgres=# |
Tsvector veri tipi örneği Öncelikle bir tablo oluşturalım ve tsvector haricindeki kolonlara veri girelim:
postgres=# CREATE TABLE dersler (ders_id SERIAL, ders_adi TEXT, ders_tokenlari TSVECTOR); CREATE TABLE postgres=# INSERT INTO dersler (ders_adi) VALUES ('Bilgisayar Bilimlerine Giriş'), ('Bilgisayar Programlama I'), ('Bilgisayar Bilimleri İçin Ayrık Matematik'), ('Kombinatorik ve Çizge Kuramı'), ('Mantıksal Devre Tasarımı Laboratuvarı');INSERT 0 5 |
ders_adi kolonundaki verilerin tokenlara ayrılmış halini ders_tokenlari kolonunda
tutmak istiyoruz, o yüzden UPDATE sorgusu ile ders_adi kolonundaki verilerin
tokenlara ayrılmış halini ders_tokenlari kolonuna aşağıdaki gibi ekleyelim:
postgres=# UPDATE dersler d1 SET ders_tokenlari = to_tsvector(d1.ders_adi); UPDATE 5 |
ders_tokenlari kolonuna ders_adi kolonundaki veriler aşağıdaki gibi geldi:
postgres=# select * from dersler; ders_id | ders_adi | ders_tokenlari ---------+-------------------------------------------+--------------------------------------------------------------- 1 | Bilgisayar Bilimlerine Giriş | 'bilgisayar':1 'bilimlerin':2 'giriş':3 2 | Bilgisayar Programlama I | 'bilgisayar':1 'programlama':2 3 | Bilgisayar Bilimleri İçin Ayrık Matematik | 'ayrık':4 'bilgisayar':1 'bilimleri':2 'için':3 'matematik':5 4 | Kombinatorik ve Çizge Kuramı | 'kombinatorik':1 'kuramı':4 've':2 'çizg':3 5 | Mantıksal Devre Tasarımı Laboratuvarı | 'devr':2 'laboratuvarı':4 'mantıksal':1 'tasarımı':3 (5 rows) |
tsvector veri tipine sahip bir kolona veri aktarmak istersek to_tsvector fonksiyonunu
aşağıdaki gibi kullanabiliriz:
postgres=# INSERT INTO dersler (ders_adi, ders_tokenlari) VALUES ('Biçimsel Diller ve Otomata', to_tsvector('Biçimsel Diller ve Otomata')); INSERT 0 1 |
NOT: Tam metin araması yaparken tsvector tipindeki verileri ayrı bir kolonda tutmadan,
sorgu sırasında to_tsvector fonksiyonu ile tam metin araması yapabiliriz veya yukarıdaki
örnekte olduğu gibi tsvector veri tipli halini ayrı bir kolonda tutabiliriz.
Kitaplar örneğinde olduğu gibi index eklersek sorgu esnasında to_tsvector fonksiyonu
ile tam metin aramasını hızlıca yapabiliriz, böylece ayrı bir kolona ihtiyacımız kalmaz.
Sözlükler: PostgreSQL’de var olan birçok dil bulunmaktadır. \dF ile var olan dilleri görebilirsiniz:
postgres=# \dF List of text search configurations Schema | Name | Description ------------+------------+--------------------------------------- pg_catalog | arabic | configuration for arabic language pg_catalog | danish | configuration for danish language pg_catalog | dutch | configuration for dutch language pg_catalog | english | configuration for english language pg_catalog | finnish | configuration for finnish language pg_catalog | french | configuration for french language pg_catalog | german | configuration for german language pg_catalog | hungarian | configuration for hungarian language pg_catalog | indonesian | configuration for indonesian language pg_catalog | irish | configuration for irish language pg_catalog | italian | configuration for italian language pg_catalog | lithuanian | configuration for lithuanian language pg_catalog | nepali | configuration for nepali language pg_catalog | norwegian | configuration for norwegian language pg_catalog | portuguese | configuration for portuguese language pg_catalog | romanian | configuration for romanian language pg_catalog | russian | configuration for russian language pg_catalog | simple | simple configuration pg_catalog | spanish | configuration for spanish language pg_catalog | swedish | configuration for swedish language pg_catalog | tamil | configuration for tamil language pg_catalog | turkish | configuration for turkish language (22 rows) |
Tam metin araması yaparken kullandığınız dili özellikle belirtmek isterseniz aşağıdaki
gibi bir sorgu yazabilirsiniz:
postgres=# select to_tsvector('english', 'This text is very long and hard for processed'); to_tsvector ---------------------------------------- 'hard':7 'long':5 'process':9 'text':2 (1 row) |
Aynı cümleyi turkçe sözlüğe göre tokenlara ayırmak istersek doğru şekilde ayırmayacaktır:
postgres=# select to_tsvector('turkish', 'This text is very long and hard for processed'); to_tsvector ------------------------------------------------------------------------------------ 'and':6 'for':8 'hard':7 'is':3 'long':5 'processedi':9 'text':2 'this':1 'very':4 (1 row) |
Aşağıdaki örnekte de türkçe bir cümlenin türkçe sözlüğe göre tokenlara ayrıldığını ve
tokenların oldukça kısaltıldığını görebilirsiniz:
postgres=# select to_tsvector('turkish', 'Bu metin oldukça uzundur ve işlenmesi zordur.'); to_tsvector ------------------------------------------------- 'işlenmes':6 'met':2 'oldukça':3 'uz':4 'zor':7 (1 row) |
Var olan sözlükleri ve dil kurallarını kullanmak istemezseniz yeni bir sözlük ve dil kuralları
oluşturabilirsiniz, veya bunun için yazılmış eklentileri de kullanabilirsiniz.
Örneğin “dict-in” eklentisi tam sayıların indekslenmesini kontrol eden tam metin aramaları
için kullanılan bir sözlüktür. “unaccent” eklentisi ise kelimelerdeki dil aksanlarına dayalı
değişimleri filtreleyerek tam metin araması yapmak için kullanılan bir sözlüktür. Örnek kullanımları:
postgres=# create extension dict_int ; CREATE EXTENSION postgres=# select ts_lexize('intdict', '252281774'); ts_lexize ----------- {252281} (1 row) postgres=# create extension unaccent; CREATE EXTENSION postgres=# select ts_lexize('unaccent','Hôtel'); ts_lexize ----------- {Hotel} (1 row) |
Yorumlar
Yorum Gönder