Oracle Full Text Search

Oracle, VERITABANI | Fatih Özdemir | October 8, 2009 at 3:17 pm

oracleFull Text Search Nedir?:

Bilişim dünyasında veritabanı kullanmış veya veritabanıyla işlem yapmış herkes index kelimesini duymuştur. Index in veritabanındaki kullanımını hemen bir örnekle açıklayalım. Mesela epeyce kalın kaufmann’ın eski pascal türü bir kitap aldınız ve kitabın içinde bir şeylere bakacaksınız, 1000 sayfalık kitabın her sayfasını tek tek çevirmek epeyce uzun ve zahmetli bir iş. Bunun için kitabın hemen başındaki indexine bakarız ve hızlıca göz gezdirip aradığımız konunun hangi sayfada olduğunu bulup direkt o sayfayı açarız.Aynen bu örnekte olduğu gibide databaselerde verileri kendilerine özel algoritmalarla indexler ve o veriye gitmek gitmek istendiğinde bu indexleri kullanarak o satırı bulur.Full Text Index ise indexlenmek istenen alan eğer text içeriyorsa ki bu bir html dosyası pdf de olabilir, bu tip verileri indexlemek için kullanılır.

Mesela bir gazetecesiniz ve 10 yıllık tüm gazetelerde çıkmış “ekonomik kriz” kelimelerini bulmak istiyorsunuz. Karşınızdaki datanın büyüklüğünü bir gözünüzün önüne getirin. Burada hemen aklınıza şu soru gelebilir google bunu peki nasıl yapıyor? Google Search’ün arkasında kullanılan index algoritmasının kesinlikle çok büyük önemi var fakat bu algoritmanın kullandığı arkada yüz binlerce server olduğunu unutmamak gerekir. Halbuki siz şirketinizde yada web sayfanızda arama yaparken 1-2 bilgisayar ile yetinmek zorundasınız. Onun için şimdilik google ile boy ölçüşmeye kalkışmamak gerekJ

Bu devasa text datalarını işe yarar şekilde indexlenmesine full text index denir. Burarada işe yarar şekilde kelimesi önemli bunu dokümanın ilerleyen kısımlarında anlatacağım. Her database’in kendine has bir full text algoritması ve kullanım biçimi var. Ben bu dokümanda Oracle Full Text nedir ne değildir biraz bahsetmeye çalışacağım ve bunu soru cevap şeklinde yapacağım. Dokümanın bundan sonra kısımlarında Full Text Index FTI olarak kısaltılacaktır.

FTI çeşitleri

Oracle da 3 çeşit index çeşidi vardır bunlar

CONTEXT Indexes

CTXCAT Indexes

CTXRULE Indexes

Bu indexlerin ne manaya geldiğine bakalım.

Context Indexes : Bu index çok yüksek miktarda text içeren datalar için kullanılır. Örneğin pdf,word,html gibi datalar için.

CtxCat Indexes : Bu index contexte göre daha az olan text alanlar i.in kullanılır. Örneğin varchar2 tipinde bir field için bu index kullanılabilir. CtxCat index için synchronize işlemi gerekmez bu otomatik olarak yapılır.

CtxRule Indexex : Daha çok document based indexlerde kullanılır. Detaylı bilgi için aşağıdaki adresi ziyaret edebilirsiniz.

www.oracle- base.com/articles/9i/FullTextIndexingUsingOracleText9i.php#CONTEXTIndexes

FTI de kullanılan terimler ve açıklamaları

Keyword: Arama yapmak için kullandığımız kelime veya kelimeler için kullanıyoruz

Phrase Search : Aramaya yaparken belli kelimelerin bütününü aramak için kullanılır. “Fatih Özdemir” kelimelerini beraber aramak gibi. Genellikle aranacak kelimelerin başına sonuna “ koyarak aradığınızda arama motorları phrase search yapar. Oracle’da bunu destekliyor.Proximity Search : Aranılan kelimeler arasında yakınlığa bakarak yapılan aramadır. Bir çok arama motoru bu özelliği desteklemektedir(Yahoo,Google).

Örneğin :

“uncle * bob” OR “bob * uncle” aramasını yazdığınızda uncle ile bob ve uncle ile bob arasında en fazla 2 kelime olacak şekilde arama sonucu döner.

Wildcard Search : Arama yaparken bir harfin yerine geçecek bir karakter kullanılarak yapılan aramalardır.

Örneğin Kara* Bu aramanın sonucunda Karadeniz,Karabük,Karaman vb sonuçlar dönecektir

Wildcard search aramayı yavaşlatıcı bir tekniktir.

 

FTI İşlemleri

Create Index

CREATE INDEX index_name ON table_name(field_name) INDEXTYPE IS CTXSYS.CONTEXT

 

En basit şekilde bir tablodaki bir alana fti yukarıdaki sql ile atılır. Burada field_name alanı ile verilen field text bir field olmak zorunda yani bu sql doğal olarak numeric bir field için çalışmaz zaten hata verecektir.

Bu sql execute olduğunda tablodaki satır sayınıza ve alan içinde text in büyüklüğüne göre epeyce sürebilir. Onun için böyle bir sqli çalıştırmadan önce iki kere düşünün zira database’i lock edebilirsiniz ve bu tabloyu kullanan tüm yazılımlarınız durabilir.

Querying

Normalde fti atılmamış bir text alanda sorgulama yapmak şöyle bir sql kullanırız:

SELECT * FROM table_name WHERE field_name LIKE ‘%FATIH%’

 

Fti atıldıktan sonraki sorgumuz artık şöyle olacaktır:

SELECT * FROM table_name WHERE CONTAINS(field_name,’FATIH’)>0

 

Score

Score operatörü etxt içinde geçen kelimelerin sıklıklarına göre size bir değer döndürür. Örneğin haebrler tablosundaki title alanında “java” kelimesini arıyorsunuz.

title “java is best language” olan ve “i like java. java is best language” olan 2 satır var

bu sorguda score operatörü kullanırsanız satırlardan birinde daha fazla java geçtiği için onun score’u daha yüksek dönecektir.

SELECT score(1),title FROM news WHERE CONTAINS(title,’JAVA’,1)>0

 

Bu sorguda contains’in içinde kullandığımız 1 rakamı label için verilmiş bir değerdir. Score kullanırken de burada yazdığımız değeri kullanıyoruz. Başka bir örnek yapalım. Bu örnekte double contains nasıl kullanılır onu göstermeye çalıştım.

SELECT score(1),title,score(2),body FROM news WHERE CONTAINS(title,’JAVA’,1)>0

OR CONTAINS(body,’ORACLE’,2)>0

Alter Index

Bir fti indexin adını değiştirmek için aşağıdaki sqli çalıştırmanız yeterlidir.

ALTER INDEX index_name RENAME TO new_index_name;

 

Bir fti indexin parametrelerini değiştirmek yada rebuild etmek için kullanılan ALTER INDEX REBUILD syntax’ını dokümanın ilerleyen bölümlerinde inceleyeceğiz.

Drop Index

Aşağıdaki sql ile atılan indexi kaldırabilirsiniz. Kaldırma işlemi atılan index ne kadar büyük olursa olsun çok uzun sürmez saniyeler içerisinde index drop edilir.

DROP INDEX index_name

FTI Elementleri

Fti için kullanılan tüm elementleri ve parametreleri bu dokümanda anlatamayacağım çünkü epeyce uzun bir doküman olur. Ben çok sık kullanılan genel elementlerden bahsedeceğim.

Multi_Column_Data_Store

Bir databasede birden fazla alanda arama yapmak istiyorsak bu elementi kullanıyoruz. Hemen örnekle açıklayalım

News adında bir tablo var ve bu tablonun title,headline,writer,body adından alanları olsun. Aramayı tüm bu alanların hepsinde birden yapmak istiyorum. Bunun nasıl olacağına bakalım

Önce my_multi_datastore adında bir preference oluşturuyorum.

ctx_ddl.create_preference(‘my_datastore’, ‘MULTI_COLUMN_DATASTORE’);

 

Daha sonra bu oluşturduğum multi_colum_datastore preference olan my_datastore’a beraber kullanmak istediği alanlari attribute olarak ekliyorum.

ctx_ddl.set_attribute(‘my_datastore’,'COLUMNS’,'title,headline,writer,body’)

 

Sonra bu alanların hepsini sorgulamak için kullanacağımız dummy bir field oluşturuyoruz. Index atarken bu dummy alanı kullanacağız. Aslında bu field sanal sadece bizim diğer fieldları tek bir field üzerinden sorgulayabilmemiz için gerekli. Bu dummy alan değiştiğinde index syncronize triggeri yazılmışsa çalışır.

full_text adında dummy bir field oluşturuyorum.

ALTER TABLE news ADD full_text VARCHAR(1);

 

Bu tablonun indexini atmak için aşağıdaki sql’i çalıştırıyoruz.

CREATE INDEX IX_NEWS_TEXT ON NEWS(FULL_TEXT) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS(‘datastore my_datastore’);

 

Sql çalıştığında IX_NEWS_TEXT adında NEWS tablosunun FULL_TEXT alanı için bir index oluşturulur. Parametre olarak yukarıda tanımladığım my_datastore verdiğim için full_text alanın birden fazla field ile bağlantılı olduğunu da söylemiş oluyorum.

SELECT * FROM news WHERE CONTAINS(full_text,’ORACLE’)>0

Yukarıdaki sql ‘title,headline,writer,body’ alanlarında “oracle” kelimesini arayacak ve sonuçlarını getirecek.

Burada önemli bir konuda kayıtlar değiştiğinde indexlerin yeniden düzenlenmesi sorunu ki buna syncronize diyoruz. Yine yukarıda verdiğim örneğe devam ediyorum. News tablosunda title’ı “Başbakan Düzce’de incelemelerde bulundu” olan bir kayıt olduğunu farzedelim. Biz create index dediğimizde oracle buradaki kelimeleri verdiğimiz parametrelere göre indexledi ve index oluşturdu. Ama biz sonra bu title’ın yanlış olduğunu düşündük ve title’ı şu hale getirdik “Başbakan Erdoğan Akyazı’da incelemelerde bulundu”.

Oracle artık index’inde tuttuğu düzce kelimesi için yukarıdaki satırı silecektir ve Erdoğan Akyazı kelimeleri için ise yukarıdaki kaydı ekleyecektir. Siz bir kaydı güncellediğinizde oracle index synronization işlemini otomatik olarak yapmaz. Bunu nasıl yapacağımıza bakalım.

Oracle 10G ile gelen bir özellik ile bir veri database e kayıt edildiğinde otomatik syncronize olacak şekilde bir parametre kullanarak bunu gerçekşeltirebiliriz. Yada bu işlemi belli periodlar halinde yapılması için oracle_jobs için ayarlayabiliriz. Başka bir yöntemde synchronize işlemini tablonun after insert event’ına trigger olarak yazarız.

Bu üç yönteme de sırayla bakalım.

Synch On(Commit)

Yukarıda parametre verilerek nasıl çoklu fieldlarda arama yapılabildiğini yazmıştım. Aynı index üzerinden devam edersek aşağıdaki sql yazıldığında haber tablosuna bir kayıt eklendiğindei silindiğinde veya güncellediğinde kısacası her hangi bir commit event’ı olduğunda fti synchronize olacaktır

CREATE INDEX IX_NEWS_TEXT ON NEWS(FULL_TEXT) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS(‘datastore my_datastore sync(on commit)’);

Burada şunu da söylemeliyim.Eğer ki tablonuzda commit işlemi çok sık oluyorsa yani veriler çok eklenip silinip güncelleniyorsa bu yöntem performansınızı düşürecektir.

 

Triggera yazma

Aşağıdaki şekilde bir trigger oluşturuyoruz.

create or replace

TRIGGER my_trigger

before insert or update or delete on news

BEGIN

ctxsys.ctx_ddl.sync_index(‘IX_NEWS_TEXT’);

END my_trigger;

 

News tablosuna herhangi bir kayıt eklendiğinde silindiğinde veya tablodan bir kayıt silindiğinde trigger çalışıp indexi synchronize edecektir.

Oracle Jobs’a ekle

Oracle enterprise manager üzerinden jobs kısmına indexin ne kadar zamanda bir synchronize olmasını istiyorsak ayarlıyoruz. Bu yöntem performans olarak en iyi yöntemdir. Toplu olarak belli periodlarda verileri synchronize eder ve indexleri günceller.

ctx_ddl.sync_index(‘my_index_name’);

Lexer

 

Lexer bir dile özgü işlemler yapmak için kullanılır. Bu konuda epeyce detaylı bir konu ben Türkçe için en çok ihtiyaç duyacağımız özelliklerinden bahsetmeye çalışacağım.

Lexer çeşitlerinden Basic_Lexer çok işimize yarayacak bir element. Basic_Lexer’ın çok bazı attributelerini aşağıda yazdım.

continuation 

Satır sonunda bir kelimeyi ayırmak için kullanılan karakter. Bizdeki tire(-) işareti 

printjoins 

Bir kelimenin içinde geçecek geçerli karakterler. Mesela gel-git gelmesini gel-git olarak indexlemek istiyorsam burada belirtmem gerekiyor. 

punctuations 

Kelime sonlarındaki noktala işaretleridir. Default olarak nokta(.) soru işareti(?) ve ünlem(!) set edilmiştir. Örneğin tavşan. Kelimesiiçin nokta(.) işareti indexlenmez sadece tavşan kelimesi indexlenir. Bu attribute sadece son karaktere bakar yani printjoins de nokta işareti kullanılsa bile punctutations da var ise nokta işareti göz önüne alınmadan indexlenecektir. 

skipjoins 

Bir kelimenin içinde geçtiğinde bu karakterleri pas geçerek kelimeyi indexler. Örneğin gel-git kelimesin gelgit olarak indexlemek istiyorsak tire(-) işaretini buraya ekleriz. printjoins and skipjoins için aynı karakterler kullanılamaz. 

newline 

Satır sonu karakterini belirlemek için kullanılır. Default olarak newline(\n) karakteri kullanılır. Seçim yapabileceğiniz direkt karakter ise carriage_return(\r) dür. 

base_letter 

Bu attribute default olarak NO yani disabled olarak gelir. Fakat Türkçe için çok gerekli bir özellik. Mesela arama yaparken eğer u-ü o-ö gibi noktalı noktasız harfleri beraber olarak aramak istiyorsak bu özelliği YES(enabled) yapmamız gerekiyor. Örneğimizde bunu attribute kullanacağız. 

mixed_case 

Default olarak NO(disabled) olarak gelir ve tüm token(kelime)lar büyük harfe çevrilir. 

index_stems 

Index atılırken hangi kelime kökleri için hangi dilin seçileceği seçilir. 

Türkçe aramalarda daha çok istenen özellik noktalı veya noktasız arama da yapılsa sonuçların dönmesi. Örneğin “fotoğraf “ kelimesinin bulunmasını istiyorsam fotograf da yazılsa fotoğraf da yazılsa aynı sonuçların dönmesi. Aynı şey tabi diğer harfler içinde geçerli yani büyük için buyuk yazılarak aranması gibi.

 

Bunu yapabilmek için yukarıda bahsettiğim gibi lexer kullanacağız. Önce bir lexer create ediyoruz sonra buna attribute veriyoruz ve indeximiz create ederken bu daha önce oluşturduğumuz lexerı parametre olarak veriyoruz. Tıpkı multi_column_datastore gibi.

Aşağı bununlar ilgili sqlleri yazdım.

ctx_ddl.create_preference(‘my_lexer’,'basic_lexer’);

ctx_ddl.set_attribute(‘my_lexer’,'BASE_LETTER’,'YES’);

Basic Wordlist

 

Bu preference’i prefix_index substring_index gibi indexleme özelliklerini kullanmak için uyguluyoruz. Doğal olarak bu özellikler nedir diye soracaksınız hemen anlatmaya çalışacağım.

Basic Wordlist bazı attribute lerini yazacağım. Yukarıda da olduğu gibi bunlar benim daha önce Türkçe için kullandığım özellikler.

substring_index 

Default olarak No(disabled) olarak geliyor. Bir kelime indexlenirken substringlere ayrılarak indexleniyor. Index süresini 4 kata kdar arttırıyor. Fakat soldan ve iki taraflı wildcard sorgularında çok daha iyi performans sağlıyor. Örneğin %mail% %konsolos 

prefix_index 

Defalult olarak No(Disabled) olarak geliyor. Sağdan yapılan wildcard sorguları için daha fazla performans sağlıyor. Tabi doğal olarak daha fazla yer ve daha uzun index süresi demek. 

prefix_length_min 

Default değeri 1 dir. Prefix_indexin minimum kac karakterden itibaren yapılacağını belirtir. 

prefix_length_max 

Default değeri 64 dür. Maximum kac karaktere kadar prefix_index atılacağını belirtir. 

wlidcard_maxterms 

Default değeri 5000 dir ve 1-15000 arasında değer alır. Wildcard bir arama sonucunda dönene kelime sayısının maximum miktarını belirtir. Eğer bu sayıdan fazla sonuç dönerse oracle exception atar.Örnek vermek gerekirse eğer siz ev% şeklinde bir arama yaparsanız ve ev% ile başlayan 5000 den fazla index değeri dönerse hata alırsınız.

 

Şimdi gerçek örnekler ile anlatmaya çalışayım. mywordlist adında bir basic_wordlist oluşturacağım.

ctx_ddl.create_preference(‘mywordlist’,'BASIC_WORDLIST’);

ctx_ddl.set_attribute(‘mywordlist’,'PREFIX_INDEX’,'TRUE’);

ctx_ddl.set_attribute(‘mywordlist’,'PREFIX_MIN_LENGTH’,3);

ctx_ddl.set_attribute(‘mywordlist’,'PREFIX_MAX_LENGTH’,4);

 

Yukarıdaki özelliklere göre prefix_index yapılacak ve minimum 3 ve maximimum 4 karakter uzunluğundaki keliemeler prefix_index e tabi tutulacak. Bunun nedemek olduğunu açıklamak isterim.

Tabloda her bir satırda aşşağıdaki gibi olsun
Ev
Süt
Araba
Bebek
Mama
Oracle yukarıdaki özelliklere göre bu textleri indexlediğinde
Ev
Süt
Araba
Bebek
Mam
Mama

Şeklinde indexleyecektir. 3ten küçük olanlar için ekstra bir indexleme yapmıyor. Boyutu 4 ten büyük olanlar içinde prefix indexleme yapmıyor(Bebek,Araba) fakat Mama kelimesi için hem mam hemde mama kelimelerin ayrı ayrı indexledi.

Stoplist

 

Indexleme yaparken bazı kelimelerin indexlenmesini istemeyebiliriz. Örneğin googleda arama yaparken “the,as,in,at” gibi kelimeleri kullandığınızda google bu kelimeleri ignore ederek aradım gibi bir mesaj veriyor. Bu tip kelimelerin indexlenmemesi için stoplist preference kullanıyoruz. Nasıl kullanacağımıza bakalım.

ctx_ddl.create_stoplist (’stopwords’, ‘BASIC_STOPLIST’);

 

Sql’ile stopwords isminde bir basic_stoplist oluşturuyoruz. Oluşturduğumuz bu stopliste kelimeler eklemek için

ctx_ddl.add_stopword(’stopwords’, ‘şu’);

ctx_ddl.add_stopword(’stopwords’, ‘bu’);

ctx_ddl.add_stopword(’stopwords’, ‘için’);

ctx_ddl.add_stopword(’stopwords’, ‘göre’);

 

stoplist’den kelime çıkarmak için ise aşağıdaki şekilde sql’imizi yazıyoruz.

ctx_ddl.remove_stopword(’stopwords’, ‘göre’);

 

Yukarıda anlattığım tüm parametreleri kullanarak News tablomuza index atmak istersek aşağıdaki gibi bir sql yazmamız gerekli.

CREATE INDEX IX_NEWS_FT_INDEX ON NEWS(FULL_TEXT) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS(‘datastore mydatastore lexer mylexer stoplist stopwords wordlist mywordlist’);

Tabi aslında Full Text Search çok daha kapsamlı bir konu ben Türkçe için gerekli olan ve işinize yarayacak sık karşılaşacağınız bazı özelliklerini ve parametrelerini anlatmaya çalıştım umarım faydalı olmuştur.

 

Tags: , ,

2 Comments

  1. Kamil Demir says:

    Oracle Full Text Search çok faydalı oldu. Teşekürler, emeğinize sağlık reklamalarada tıkliyim mi ? şaka şaka

  2. İsa yıldırım says:

    Full Text olayında Microsoft mu daha başarılı yoksa Oracle’mı?

Leave a Reply