3.4 Proje Gerçeklenmesi
Yüksek
başarım ile çalışan bir Türkçe doğal dilden
SQL dönüşüm uygulaması şu ana kadar
gerçekleştirilmemiş zor bir
çalışma olarak görülmektedir. Bunun nedenleri
genel olarak:
DDİ konularının karmaşıklığı,
Konuların çeşitliliğinin fazla olması ve çalışmaların ticari değer kazanmasının kolay olmaması,
Bu konuda dünyada
İngilizce üzerinde uzun süredir çalışmalar
yapılmaktadır. Bunlardan ticari alanda kullanılmaya en yakın
olanlarından birisi Microsoft English Query ürünü
olarak görülmekteydi ancak SQL Server 2005 sürümü
ile birlikte bu ürüne olan destek kaldırıldı. Bununla
birlikte, kısıtlı bir alanda iyi bir başarım ile çalışan
doğal dil - SQL dönüşüm uygulamaları doğrudan veri
tabanı üzerinden değil, özellikle internet üzerindeki
etkileşimli arama uygulamalarının kullanımını kolaylaştırma
amacına uygun şekilde var olan projelere eklenebilir.
Türkçe
- SQL dönüşümü konusundaki bilinen tek çalışma
NALAN-TS [5]'dır. Bu çalışma, Boğaziçi
üniversitesinde geliştirilen TOY [6] projesinin biçimbilim
ve sözdizimbilim altyapısı üzerine kurulmuş ve Prolog
dili ile geliştirilmiştir. Uygulama tıpkı bu çalışma
gibi dar bir kapsamda kabul edilebilir bir başarıma
sahiptir.
Gerek bahsi geçen NALAN-TS ve TOY
projelerinin kodlarına erişimin güçlüğü
gerekse Prolog diline hakim olunmadığından bu projede farklı bir
yol izlenmiştir. Projenin biçimbilim ihtiyacını karşılamak
için erişimi oldukça kolay olan açık kodlu
Zemberek projesi kullanılmıştır. Zemberek projesi Java dili ile
gerçekleştirildiğinden projenin gerçeklenmesinde de
Java dili tercih edilmiştir.
Zemberek şu an için
sadece kelime tabanlı işlemleri yapabildiğinden projenin ihtiyacı
olan başka doğal dil işleme araçları temin edilememiş, o
nedenle gerçeklemede çeşitli kısıtlamalar ortaya
çıkmıştır.
Yüksek başarımlı bir doğal dil
- SQL dönüşümü çalışması son derece
yoğun çalışma ve araştırma gerektiren bir iştir. Bu tür
bir çalışmanın gerçeklenebilmesi için ileri
düzey doğal dil işleme araçlarına ve uzun süren
araştırmaya ihtiyaç duyulmaktadır. Çok kelimeli
kavramları çözümleyen bir sistem, güçlü
bir cümle yapı çözümleyici, kavramlar ve
kelimeler arası ilişkileri gösteren kelime ağı (wordnet),
yapısal çözümleme sonrası belirsizlik giderimi
mekanizması bu konuda olması gereken araçlar arasındadır.
Türkçe için bu araçların bir kısmı
konusunda çalışmalar yapılmış olsa da kolayca
erişilebilir ve kullanılabilir projeler yok denecek kadar azdır.
Bu nedenle
çalışmada son derece basit bir yöntem izlenmiştir.
Amaç, gerçekleştirilen basit cümle ayrışım ve
sonlu durum makinesi yaklaşımının ihtiyaçlara ne kadar
cevap verdiğinin gözlemlenmesi ve çok dar bir kapsamda
basit sorgu cümlelerinin SQL'e dönüştürülmesidir.
Yaklaşımın basitliği ve kapsamı kullanımında çeşitli
kısıtlamaları da beraberinde getirmiştir.
3.4.1 Kısıtlamalar
Farklı
SQL
üretim mekanizmaları ve durum makineleri gerektiğinden sadece
okuma yapan sorgulama cümleleri (SELECT sorguları)
üretilecektir. Ekleme, silme ve güncelleme işlemleri
projeye dahil edilmemiştir. Bununla birlikte bu işlemlerin dahil
edilmesi zaman alıcı bir iş olmakla birlikte zor
değildir.
Gerçekleme sadece tek tablo üzerinden
yapılan sorgulara izin vermektedir. Çok tablolu yada birleşme
özellikli sorgular (JOIN sorguları) sisteme dahil edilmemiştir.
Bunun nedeni çok tablolu yapıların karmaşık ve birden
fazla cümleler ile ifade edilebilmesi, dönüşümde
kullanılan durum makinesinin elle tasarlanamayacak derecede
karmaşıklaşmasına neden olmasıdır.
Sorgulama
cümlelerinde bilgi içeren kelimeler özel bir şekilde
ifade edilmiştir. Örneğin [Adı
Dilek olan öğrencileri göster]
cümlesi [Adı
"Dilek" olan öğrencileri göster] şeklinde
sorgudaki kısıtlamayı ifade eden bilgi çift tırnak işareti
arasında yazılarak ifade edilmelidir. Yazılım bu tür
kelimeleri gördüğünde otomatik olarak onları “sorgu
kısıtlama bileşeni”
olarak işaretlemekte, yapısal çözümleme
işlemlerine tabi tutmadan sorgu oluşumunda doğrudan
kullanmaktadır.
Sadece tek cümlelik girişler
dönüştürülmektedir.
Sonuçların dizilmesi yada gruplanması,
sonuçlar üzerindeki matematiksel işlemler
gerçekleştirilmemiştir.
3.4.2. Gerçekleştirilebilen İşlemler
Uygulama basit anlamdaki sorgulara karşılık düşen cümleleri dönüştürebilmektedir. Bazı işlemler ve SQL karşılıkları :
Kayıt sonuçlarının temel karşılaştırma işlemleri ile sınırlandırılması, select * from URUNLER where fiyat>500,
Sadece belli sutunlardaki bilgilerin döndürülmesi, select U_ID, U_AD, U_FIYAT from URUNLER ,
Sonuç kayıt sayısının sınırlandırılması, select top 10 * from CALISANLAR,
Yukarıdaki sayılan maddelerin çeşitli kombinasyonları, select top 10 U_ID, U_AD, U_FIYAT from URUNLER where U_AD like 'A%' and U_FIYAT>100 and U_TARIH is not null .
Bununla
birlikte devrik cümleler yada bazı farklı doğal cümle
kurulumları doğru olsa bile dönüştürülememektedir.
Bunlara ilişkin örnekler ileriki bölümlerde
verilecektir.
3.5 Yöntem
Uygulama dört seviyeli bir işlem ile Türkçe doğal dilden SQL dönüşümünü gerçekleştirmektedir(Şekil3.1).
Veri tabanı yapısal bilgilerinin ve gerekli dilbilgisi bilgilerinin okunması ve bellekte özel veri yapılarına yerleştirilmesi.
Doğal dil cümlenin önce kelimelere sonra sorgu cümle bileşeni dizisi şeklinde ayrıştırılması.
Basit bir durum makinesi ile ayrıştırılmış bileşenlerden sorgu bilgisinin taşındığı özel veri yapısının oluşturulması.
Sorgu bilgilerini taşıyan veri yapısından uygun veri tabanına göre gerçek SQL cümlesinin oluşturulması.
4. ARAŞTIRMA BULGULARI
Uygulama başlangıçta sistemden kavram, veri tabanı ve kısıtlanmış kök bilgilerini sistemden okuyup bellekteki nesnelere aktarır.
4.1.1 Kökler ve Kelime Eleyici
Doğal
dilde üretilen cümlelerde çok miktarda farklı
kelime kullanılabilir.
Bu durum doğal dil işleme sırasında kelime belirsizliği adı
verilen bir probleme yol açar. Örneğin “saat kaç”
cümlesindeki kaç kelimesi miktar sorgulayan “kaç”
yada “kaçmak” eyleminden emir olabilir. Benzer şekilde
"elması" kelimesinin kokunun "elma" mi yoksa
“elmas” mi olduğunun belirlenmesi oldukça zor bir
işlem olabilir. Cümlenin konusunun analizi, istatistiksel verilerin
incelenmesi ile hangi kökün doğru olduğunun bulunması
gerekir. Uygulamada karşılaşılan sorunlardan birisi bu olmuştur.
Sorgu cümlelerinde kullanılan kelimeler için Zemberek
uygulaması çok fazla kök ve ek olasılığı üretiyor.
Bu, hangi kökün kullanılacağının bilinememesine yol
açmaktadır.
Örnek:
“elmasın”
kelimesi için Zemberek şu çözümleri
önerir:
[
Kok: elmas, ISIM ] Ekler: ISIM_TAMLAMA_IN
[ Kok:
elmas, ISIM ] Ekler: ISIM_SAHIPLIK_SEN_IN
[ Kok: elma,
ISIM ] Ekler: ISIM_KISI_SEN_SIN
Burada
hangi çözümün doğru olduğuna dair bir öneri
verilmemektedir. Bu nedenle bu hali ile çözümleme
işlemi sorunlara yol açtığından SQL dönüşümü
için kullanılan kök ve eklerin kısıtlanması yoluna
başvurulmuştur. Bu şekilde sadece belirli bir miktarda kök ve
ekin sistem tarafından tanınması sağlanmıştır. Bu kısıtlanmış
kökler Bilgi
dizinindeki temel-kokler.txt
adlı düz metin dosyada saklanmıştır. Bu dosya
Tr2SQLKelimeEleyici
sınıfı tarafından okunur ve okunan String yapısındaki kök
bilgileri Zemberek
içindeki sözlük yapısı ile kök değerlerine
dönüştürülür. Sınıfın basitleştirilmiş
yapısı şu şekildedir:
public
class Tr2SQLKelimeEleyici {
private DilBilgisi
dilBilgisi;
private Set<Kok>
kabulEdilenKokler = new HashSet<Kok>();
private Set<Ek> kisitlananEkler;
public
List<Kelime> ele(Kelime[] kelimeler) { ... }
}
Okunan
kökler kabulEdilenKokler
adındaki Set yapısında saklanır. Set yapısının List yapısından
farkı içinde nesnelerin tekrar edilememesidir. Yani hata ile
bile olsa içine tekrar aynı nesne koyulamaz. Ayrıca bir
nesnenin içinde olup olmadığının kontrolü List
yapısına göre daha hızlıdır.
Bu sınıftaki ele()
metodu çözümleme sonrasında oluşan Kelimelerin
hangilerinin sistem tarafından kabul edileceğini belirler.
Örnek:
“yazma” kelimesi Zemberek tarafından:
[
Kok: yazma, ISIM ]
[ Kok: yaz, FIIL ] Ekler:
FIIL_OLUMSUZLUK_ME
[ Kok: yaz, FIIL ] Ekler:
FIIL_DONUSUM_ME
şeklinde
çözümlenirken kelime eleyici eleme metodu sadece
yazmak kökü ve olumsuzluk ekini kabul edip diğer iki
çözümü eleyecektir:
[
Kok: yaz, FIIL ] Ekler: FIIL_OLUMSUZLUK_ME
Bu
şekilde sistemde birden fazla kelime çözümü
çok ender olarak ortaya çıkacak, ve sadece istenen
kökün elde edilmesi sağlanmıştır.
4.1. Kavramlar
Doğal
dilde aynı ifade farklı
kelime yada kelime grupları ile ifade edilebilir. Bu, aynı şeyin
farklı şekillerde söylenebilmesine neden olur. Normalde bu iyi
düzenlenmiş bir kelime-anlam ilişkisi yapısı ile çözülebilir
ancak bu şekil bir yazılıma erişilemediğinden yazılımda çok
basit bir “kavram”
mekanizması oluşturuldu. Basitçe aynı yada benzer anlama
gelen kelime kökleri aynı kavrama denk düşürüldü.
Bu bilgi uygulamada düz bir metin dosyasında saklanıyor. Bilgi
dizini altındaki kavramlar.txt
dosyanın yapısı aşağıdaki gibidir:
çalışan:
çalışan,işçi,kişi,insan,adam,eleman
numara:
numara,kod,sayı,rakam,no
ad: ad,isim
soyad: soyad
maaş:
maaş,ücret,aylık
BITMEK: bitmek,
sonlanmak
BUYUK:büyük,fazla,çok
KUCUK:küçük,az,ufak
Her
satırda “:”
işaretinden önceki kısım kavramın adını belirler. Daha
sonra bu kavrama karşılık düşen gerçek Türkçe
kelime kökleri virgül işareti ile ayrılarak sıralanır.
Yazılımdaki tr2sql.db
paketindeki KavramOkuyucu
sınıfı bu dosyayı okuyup çözümleyip Kavram
sınıfından nesneler üretir. Kavram
sınıfı aşağıdaki bileşenleri içerir:
public
class Kavram {
private List<Kok> esKokler
= new ArrayList<Kok>();
private String
ad;
....
}
Görüldüğü
kavramın adını ifade eden bir String
ve eş anlama gelen kök nesneleri taşıyan bir List
yapısı. Buradaki bir sorun, dosyadan okuduğumuz Türkçe
kelime köklerinin String
yapısında olmasıdır. Bunların gerçek Türkçe
kelime kökünü ifade eden kök nesne
karşılıklarının bulunması gerekir. Bunun için Zemberek
sınıfından yararlanılır. Dosyadan örneğin “işçi”
kelimesi okunduğunda bu bir String
halindedir. Zemberek içinde yer alan Sözlük
nesnesinin kokBul()
metodu ile bu işçi String
bilgisine karşılık düşen kök bulunur. Burada sistem bir
tahmin yürütmek zorunda kalabilir, kelime “-mek,-mak”
mastar eki ile bitmiş ise bunun fiil olduğu kestirilir.
Tüm
bu işler KavramOkuyucu
sınıfındaki public
List<Kavram> oku(String KavramDosyaAdi)
metodu ile gerçekleştirilir. Bu metot içinde Kavram
nesneleri barındıran bir adet List
yapısı döndürür. Bu dönen yapı daha sonra
TurkceSQLCozumleyici
sınıfında başka bir veri yapısına dönüştürülecektir.
Bu konu çözümle bileşenlerinin ayrıştırılması
sırasında açıklanacaktır.
4.2 Veri Tabanı Yapı Bilgileri
Doğal dilden veri tabanına dönüşüm için bilinmesi gereken şeylerden birisi veri tabanındaki tablo ve sutun bilgilerinin doğal dilde hangi kavramlara karşılık düştüğünün bilinmesidir. Örneğin öğrenci bilgilerini taşıyan “TBL_OGRENCI” adli bir tablonun aslında bir öğrenciyi ifade ettiğinin otomatik olarak bilinmesi mümkün değildir. Benzer şekilde tablo sutunlarının da hangi kavramlara karşılık düştüğünün bir şekilde sistem tarafından önceden bilinmesi gerekir ki “numarası 34 olan işçiyi göster” cümlesindeki numara kökünün veri tabanındaki “C_ID” sutununa karşılık düştüğü tespit edilebilsin.
Bu
nedenle uygulamada veri tabanına ilişkin bu bilgiler bir xml
dosyasında saklanır. Bilgi dizinindeki “basit-veri-tabani.xml”
dosyasında saklanır. Dosyanın yapısı aşağıdaki
gibidir:
<?xml
version="1.0" encoding="UTF-8"?>
<veri-tabani
ad="maden">
<tablolar>
<tablo ad="CALISANLAR" kavram="çalışan">
<sutunlar>
<sutun
ad="C_ID" kavram="numara" tip="SAYI"
anahtar="true"/>
<sutun ad="C_AD" kavram="ad"
tip="YAZI"/>
<sutun ad="C_SOYAD" kavram="soyad"
tip="YAZI"/>
<sutun ad="C_MAAS" kavram="maaş"
tip="PARA"/>
<sutun ad="C_BOLUM" kavram="bölüm"
tip="YAZI"/>
</sutunlar>
</tablo>
<tablo ad="URUNLER" kavram="ürün">
<sutunlar>
<sutun
ad="U_ID" kavram="numara" tip="SAYI"
anahtar="true"/>
<sutun ad="U_AD" kavram="ad"
tip="YAZI"/>
<sutun ad="U_FIYAT" kavram="fiyat"
tip="PARA"/>
<sutun ad="U_MIKTAR" kavram="miktar"
tip="SAYI"/>
</sutunlar>
</tablo>
</tablolar>
</veri-tabani>
Görüldüğü
üzere yapı, bir veri tabanına ilişkin çoğu bilgiyi
taşımaktadır. Ayrıca bazı fazladan çözümleme
için gerekli bilgiler de mevcuttur. <tablo>
bileşenindeki “ad”
özelliği tablonun veri tabanındaki gerçek adını
belirtir. “kavram”
ise önceki bölümde anlatılan, bu tablonun hangi
kavrama karşılık düştüğünü ifade eder. Aynı
özellikle <sutun>
bileşeninde de mevcuttur. Ancak sutun bileşeni ayrıca “tip”
bilgisi taşır. Bu bilgi o sütunun ne tür bir veri taşıdığını
gösterir. Bu veri, dönüşüm sırasındaki bazı
tutarsızlıkları yakalamak için kullanılabilir. Ayrıca
sütunun anahtar sütun (primary key) olup olmadığı da “anahtar”
bileşeni ile anlaşılır. Bu dosya XmlVeriTabaniBilgisiOkuyucu
sınıfı tarafından okunur. Kavram karşılıkları önceden
okunan Kavram’larin
tutulduğu bir veri yapısı yardımıyla belirlenir. Sutunlar
okunduktan sona tr2sql.db
paketindeki
Sutun
sınıfına aktarılır. Bu sınıf aşağıdaki gibi bir yapıya
sahiptir:
public
class Sutun {
private String ad;
private Kavram kavram;
private SutunTipi
tip;
private boolean anahtar = false;
....
}
İçerisinde
sutunun veri tabanındaki adı, hangi kavrama karşılık düştüğü,
tipi ve anahtar sütun olup olmadığı belirtilmiştir. SutunTipi
aşağıdaki Java enum
yapısında, yani sabit verilerin tutulduğu bir sınıf
yapısındaki:
public
enum SutunTipi {
SAYI, YAZI, ZAMAN,
PARA
}
Sutun
bilgileri okunurken okunan tablo bilgileri ise Tablo
nesnelerine dönüştürülür.
public
class Tablo {
private String ad;
private Kavram kavram;
private List<Sutun>
sutunlar = new ArrayList<Sutun>();
...
}
Görüldüğü
gibi Tablo
nesneleri de veri tabanı tablosunun adı, hangi kavrama karşılık
düştüğü ve tabloyu oluşturan sutunların bir
listesi olarak oluşturulur. Yani önceki aşamada oluşturulan
sutunlar bir liste şeklinde Tablo
nesnelerine atanır. Daha sonra tablo bilgileri de VeriTabanı
adı verilen sınıfta toplanır. VeriTabanı
sınıfının yapısı aşağıdaki şekildedir.
public
class VeriTabani {
private String ad;
private List<Tablo> tablolar;
...
}
Özet
olarak veri tabanı bilgilerini taşıyan xml
dosyadan bir adet VeriTabani
nesnesi üretilir. Bu nesne çözümleme sırasında
kullanılacaktır.
4.3 Cümle Bileşenlerinin Ayrıştırılması
SQL
dönüşümünün ikinci aşaması girilen Türkçe
cümlenin sorgu cümle bileşenlerine ayrıştırılmasıdır.
Bileşenlere
ayırma sonrasında Türkçe cümle bir CumleBileseni
nesnesi katarı haline gelir. Bu işlem yapılmadan önce cümle
üzerinde bir iki basit işlem gerçekleştirilir.
TurkceSQLCozumleyici
sınıfı içerisindeki iç sınıf BasitCumleCozumleyici
ayrıştırma işlemlerinin yapıldığı yerdir. Ayrıştırma
işlemine önce giriş cümlesinin istenen kelimelere
ayrılması ile başlanır. Elimizde aşağıdaki cümle
olsun
[adı
"Ali", soyadı "Er" ya da
"Erkan" olan çalışanları
göster]
İlk
aşamada cümledeki boşlukların sayısı bire indirilir.
[adı
"Ali", soyadı "Er" ya da "Erkan" olan
çalışanları göster]
haline
dönüşür. Daha sonra çok kelimeli kavramlar
probleme yol açtığından “ya da”
kelimesi “veya”
ile değiştirilir.
[adı
"Ali", soyadı "Er" veya "Erkan" olan
çalışanları göster]
Ardindan bir düzgün ifade (regular
expression) kullanılarak cümleden kelimeler ve virgül
sembolü ayrıştırılır. Elimizde şu parçalar
oluşur:
[adı]
["Ali"] [,] [soyadı] ["Er"] [veya] ["Erkan"]
[olan] [çalışanları] [göster]
Burada
dikkat edilirse virgül sembolü de ayrıca kelime gibi ele
alındı. çünkü virgül işareti sorguda bağlaç
belirttiği için önem taşır. Bu işlemi yapan kod
aşağıda gösterilmiştir.
public
BasitCumleCozumleyici(String giris) {
// birden fazla bosluklari tek bosluga indir.
String c = giris.replaceAll(" +", "
").trim();
// "ya da" ikilisini "veya" ile degistirelim.
coklu kelimelerle ugrasmamak icin.
c = c.replaceAll("ya da", "veya");
// bu regular expression ile cumledeki kelimeleri
parcaliyoruz.
// eger kelime '' isareti icinde ise parcalanmiyor, butun olarak
aliniyor.
// virgul sembolu de ayrica listede yer aliyor.
Pattern parcalayici = Pattern.compile("(\"[^\"]*\")|[^
\\t\\n,.]+|,");
Matcher m = parcalayici.matcher(c);
while (m.find())
cumleParcalari.add(m.group());
}
Daha
sonra elde ettiğimiz bu kelimeler cumleParcalari
adında bir List<String>
yapısına aktarılır. Bu String
listesindeki tüm kelimeleri tek tek işlenmeye başlanır ve
hangi kelimenin hangi kavrama karşılık düştüğü
bulunur. Daha sonra bu kavram bilgisi ile hangi CumleBileseni
sınıfına karşılık düştüğü ortaya çıkarılmaya
çalışılır. BasitCumleCozumleyici
içindeki List<CumleBileseni>
bilesenler()
metodu bu iş için kullanılır. Bu metot sonrasında
parçalanan her bir kelimeye bir CumleBileseni
karşılık düşer.
Örnek:
[adı]
["Ali"] [,] [soyadı] ["Er"] [veya] ["Erkan"]
[olan] [çalışanları] [göster]
parçalarından aşağıdaki bileşenler
üretilir.
[adı:SUTUN]
[Ali:KISITLAMA_BILGISI]
[soyadı:SUTUN:
on baglac=VIRGUL]
[Er:KISITLAMA_BILGISI]
[Erkan:KISITLAMA_BILGISI:
on baglac=VEYA]
[olan:OLMAK]
[çalışanları:TABLO]
[göster:ISLEM]
Bu
bileşenlerin bulunması sırasında Zemberek
kütüphanesi de kullanılarak kelimeler çözümlenerek
kökleri bulunur. daha sonra bu kökleri kullanarak
elimizdeki Kok-Kavram
tablosundan hangi kavrama karşılık düştüğü
bulunur. Daha sonra bu kavramın tablo, sutun, işlem yada kıyas
türü gibi nesnelere karşılık düştüğü
belirlenip ilgili CumleBileseni
nesnesi üretilir. CumleBileseni
ve ondan türeyen sınıflar şu şekildedir:
CumleBileseni
sınıfı, bir cümledeki bir kelimenin veri tabanı sorgusu
açışından ne tür bir bileşene karşılık düştüğünün
belirlenmesi ve ilgili verilerin taşınması amacı ile
oluşturulmuştur.
Tüm bileşenler CumleBileseni
sınıfından türerler. Bu sınıf içerisinde hepsinde
ortak olarak bulunan bazı parametreler taşınır.
public
class CumleBileseni {
protected
CumleBilesenTipi tip;
protected String
icerik;
protected Kelime kelime;
}
Cümledeki
kelimenin Zemberek
ile çözümlenmiş halı olan Kelime
nesnesi, kelimenin orijinal hali 'içerik' String'i gibi. Tip
bilgisi aşağıdaki enum
sınıfında tanımlanmıştır:
public
enum CumleBilesenTipi {
KISITLAMA_BILGISI,
SUTUN,
SONUC_MIKTAR,
TABLO,
OLMAK,
BAGLAC,
ISLEM,
SAYI,
KIYASLAYICI,
TANIMSIZ
}
Tablo
bileşeni veri tabanı tablo bilgisini temsil eder. İçerisinde
kelimenin karşılık düştüğü tablo nesnesi taşınır.
CumleBilesenTipi
değeri TABLO’dur.
[çalışanları
listele]
cümlesindeki “çalışanlar”
TabloBileseni
nesnesine karşılık düşecek ve nesne CALISANLAR
tablosuna dair bilgileri taşıyan Tablo
nesnesini de içerecektir.
IslemBileseni
yapılan SQL işlemini ifade eden kelimeye karşılık kullanılır.
Bu genellikle bir eylem şeklindedir. IslemBileseni
nesnesi içinde bir adet IslemTipi
enum
parametresi bulunur. IslemTipi
yapısı şu şekildedir:
public
enum IslemTipi {
SORGULAMA, EKLEME, GUNCELLEME,
SILME, TANIMSIZ;
}
Örneğin
[adı
"Ali" olan çalışanları göster] cümlesindeki
“göster”
kelimesi
“sorgula”
kavramına karşılık düşecektir (bu bilgi zemberek ile
çözümleme sonrasında elde edilen göstermek kök
nesnesine karşılık düşen kavram tablosundan elde edilir). Bu
kavram bilgisi kullanılarak ayrıştırma sırasında
IslemTipi.SORGULAMA
tipine sahip bir IslemBileseni
nesnesi üretilir. Uygulama şu an için sadece SORGULAMA
tipi cümleler için çözümleme
yapabilmektedir.
Bu
bilesen kelimenin veri tabanındaki hangi sütuna karşılık
düştüğünü taşır. Bu nesnenin içerisinde
ilgili Sutun nesnesi ve varsa öncesinde tanımlanmış bağlaç
bilgisi taşınır. CumleBilesenTipi
değeri SUTUN’dur.
[ismi
Ali olan çalışanları göster]
cümlesindeki “ismi”
kelimesi CALIŞANLAR
tablosundaki C_AD
sutununa
karşılık düşecek ve onun için bir adet SutunBileseni
üretilecektir.
[ismi
ve soyadı A ile başlayan işçiler] cümlesindeki
ise “soyadı”
kelimesinden önce “ve”
bağlacı vardır. Ayrıştırma sonucunda bu bilgi de ilgili
SutunBileseni
nesnesine aktarılacaktır.
4.3.4
Bilgi Bileşeni
Bu
bilesen cümlede sorguyu kısıtlayan bilgilerin ifadesi için
kullanılır. Örneğin [numarası
5'ten büyük]
cümlesindeki “5”,
sorgunun kısıtlanmasını sağlayan bilgiyi temsil eder. Bu nedenle
ayrıştırma sonrasında bu bilgi BilgiBileseni
nesnesine dönüştürülür. CumleBilesenTipi
değeri KISITLAMA_BILGISI'dir
Ancak
uygulamanın yapısını
karmaşıklaştıracağından bu konuda bir kısıtlamaya gidilmiş,
kısıtlama bilgisi taşıyan kelimelerin çift tırnak
içerisinde yazılacağı varsayılmıştır. Bu, çözümleme
sırasında kelimenin bilgi ifade ettiğinin anlaşılmasını
kolaylaştırmıştır. Aksi taktirde bu ancak daha karmaşık cümle
analizi teknikleri ile ortaya çıkarılabilir. Bu nedenle
uygulamada aşağıdaki türden cümleler
girilebilmektedir:
[ismi
"Ali" olan ve numarası "9" dan büyük
olan çalışanları göster]
Bu
cümledeki
“Ali”
ve “9”
bilgileri BigiBileseni
nesnelerine dönüştürülür. Eğer bilgi
bileşeninden önce “ve,
veya”
türünden bir bağlaç var ise bu da nesneye
aktarılır.
[ismi
"Ali" veya "Ayşe" olan çalışanları
listele]
“Ayşe”
bilgisini taşıyan bilesen içerisinde “veya”
bağlacına ilişkin BaglacTipi
cinsinden bir parametre taşıyacaktır. Bu bilgi daha sonra SQL
üretiminde gerekli olacaktır.
BilgiBileseni
içerisinde ayrıca içerisinde bir adet KiyaslamaBileseni
nesnesi taşır. Bu, yapılan kıyaslamanın ne türden (eşitlik,
büyüklük vs.) olduğunu belirtir. Ancak bu veri bilgi
bileşenine ayrıştırma sırasında değil, üçüncü
kademedeki durum makinesinin işletilmesi sırasında aktarılır. Bu
verinin tespiti için birazdan anlatılacak olan
KiyaslamaBileseni
nesnesine gerek duyulur. Hiç bir kıyaslama değeri yoksa
ortada eşitlik kıyaslaması yapıldığı varsayılır.
4.3.5 Kıyaslama Bileşeni
Bu
bilesen bilgi bileşenlerinin ne tür bir kıyaslama yaptığı
bilgisini taşır. içerisinde bir adet KiyasTipi
enum şifini değeri taşır. Bu enum şifini aşağıdaki
gibidir:
public
enum KiyasTipi {
ESIT,
ESIT_DEGIL,
BASI_BENZER,
BASI_BENZEMEZ,
SONU_BENZER,
SONU_BENZEMEZ,
KUCUK,
BUYUK,
KUCUK_ESIT,
BUYUK_ESIT,
NULL,
NULL_DEGIL,
}
[Numarası
"9" dan büyük işçileri listele]
cümlesindeki büyük kelimesi için ayrıştırma
sınıfı KiyasTipi.BUYUK
tipini taşıyan bir KiyaslamaBileseni
nesnesi üretir. Benzer şekilde [Adı
"A" ile başlayan çalışanlar]
cümlesindeki başlayan kelimesi için
KiyasTipi.BASI_BENZER
türünde bir KiyaslamaBileseni
nesnesi üretilir. Buradaki ince bir nokta ise, kelime içinde
olumsuzluk eki var ise bu bilginin de taşınmasıdır. Yani
[Adı
"A" ile başlamayan çalışanlar]
cümlesi için gene KiyasTipi.BASI_BENZER
tipinde bir KiyaslamaBileseni
üretilir ancak ayrıca içerisinde asıl kıyaslamanın
olumsuz olduğu verisi de saklanır. Bu veri çözümleme
sırasında kullanılacak ve kıyas tipi KiyasTipi.BASI_BENZEMEZ
haline dönüştürülecektir.
Bu
tersine
dönüştürme işlemi gene KiyasTipi
enum sifnindaki ters()
metodu ile gerçekleştirilir.
public KiyasTipi tersi() {
switch (this) {
case BUYUK:
return KUCUK_ESIT;
case KUCUK:
return BUYUK_ESIT;
case BUYUK_ESIT:
return KUCUK;
case KUCUK_ESIT:
return BUYUK;
case ESIT:
return ESIT_DEGIL;
.....
}
}
şeklinde.
Örneğin KiyasTipi.ESIT.tersi()
çağrısı KiyasTipi.ESIT_DEGIL
döndürülecektir.
4.2.6
Miktar Kısıtlama Bileşeni
SQL
sorgularında bazı durumlarda tüm değil belli miktarsa sonucun
dönmesini isteriz. Bunun için farklı veri tabanlarında
farklı anahtar kelimeler kullanılır. MySQL “limit”, SQL Server
“top”
gibi. Bir cümlede dönüş değerinin kısıtlandığı
genellikle “ilk
5”
gibi bir kullanım ile belirlenir. Buradaki “ilk”
kelimesini o nedenle MiktarKisitlamaBileseni
olarak işaretlenir.
[İsmi
"ayse" olan ilk 5 çalışanı göster]
cümlesindeki “ilk”
kelimesi bu nedenle MiktarKisitlamaBileseni
nesnesine karşılık düşürülmüştür.
4.3.7
Olmak Bileşeni
Bu
bilesen olmak eylemi kullanıldığı yerlerde kullanılır.
Genellikle sorgu cümlelerinde çok rastlandığından
ayrıca bilesen olarak tanımlanmıştır. Bu bileşenin içinde
de olumsuz olup olmadığına ilişkin veri taşınır.
4.3.8
Tanımsız Bileşen
Herhangi
bir kelime Zemberek
tarafından çözülemezse yada çözülse
bile eleme mekanizması tarafından elenirse bu sisteme
TanimsizBilesen
olarak eklenir. Örneğin
[adı
"Ali" olan çalışanları göster lütfen]
cümlesindeki lütfen kelimesi TanimsizBilesen
olarak yorumlanır. Bu kelimeler SQL oluşumu sırasında göz
ardı edilir ama uyarı olarak ekrana yazılır.
4.4 Durum Makinesi Ve Sorgu Taşıyıcı Nesne Üretimi
Cümle
CumleBileseni
dizisi haline getirildikten sonra asıl dönüşüm
işleminin yapılacağı basit yapılı bir durum makinesine
aktarılır. Burada amaç cümle bileşenlerinin dizilişine
göre sorguya ilişkin bilgilerin toplanmasıdır. Durum
makinesini anlatmadan önce bir SQL sorgusu içinde
bulunabilecek çeşitli bilgilerin yer aldığı SorguTasiyici
sınıfının anlaşılması gerekir. Bu sınıf aşağıdaki
yapıdadır:
public
class SorguTasiyici {
// SQL islem tipi
public IslemTipi islemTipi;
// ilgili
tablo.
public Tablo tablo;
// sorguyu kisitlayan sutun bilgileri
public
List<SutunKisitlamaBileseni> sutunKisitlamalari =
new ArrayList<SutunKisitlamaBileseni>();
// sonuc miktari. -1 ise miktar kisitlamasi yok demektir.
public int sonucMiktarKisitlamaDegeri = -1;
// sonucta listelenmesi gereken sutunlar.
public List<Sutun> sonucSutunlari = new
ArrayList<Sutun>();
public
StringBuilder rapor = new StringBuilder();
}
Yapıda
görüldüğü üzere bir sorgu bir adet
IslemTipi
(sorgulama, silme, ekleme vs.), Bir Tablo
nesnesi, sorgudaki kısıtlama bilgilerinin taşındığı bir
SutunKisitlamaBileseni
nesne listesi, eğer varsa dönüş miktar kısmını
belirten sonucMiktarKisitlamaDegeri,
eğer sorgu sonucunda sadece belli sutunların gösterilmesi
isteniyorsa onların taşındığı bir SutunListesi
ve son olarak çözümleme sırasında ortaya çıkacak
bazı uyarı mesajlarının tutulduğu rapor parametresi yer
almaktadır. Sorgu sonucunu kısıtlayan SutunKisitlamaBileseni
sınıfının yapısı şu şekildedir:
public
class SutunKisitlamaBileseni {
public Sutun
sutun;
public List<BilgiBileseni>
kisitlamaBilgileri =
new ArrayList<BilgiBileseni>();
public
BaglacTipi oncekiBilsenIliskisi = BaglacTipi.YOK;
}
Görüldüğü
gibi içerisinde bir adet Sutun nesnesi, bir liste içinde
önceden bahsi geçen BilgiBileseni
nesneleri
ve son olarak bu SutunKisitlamaBileseni
nesnesinden önce VE, VEYA gibi bağlaç bilgisi varsa ona
ilişkin bilgi yer alır.
Örnek:
[numarası
"9" dan büyük, adı "Ali" olan
işçileri listele]
cümlesi çözümlendikten sonra iki adet
SutunKisitlamaBileseni
nesnesi oluşur. Bunlarda ilki
[Sutun=
C_ID, kavram:numara ]
[kisitlamaBilgileri= Bilgi içeriği:9
, KiyasTipi.BUYUK]
[oncekiBilsenIliskisi = BaglacTipi.YOK]
ve
ikincisi
[Sutun=
C_AD, kavram:çalışan ]
[kisitlamaBilgileri = bilgi
içeriği:Ali, KiyasTipi.ESIT]
[oncekiBilsenIliskisi =
BaglacTipi.VIRGUL]
verilerini
taşıyacaktır.
Türkçe cümle CumleBilseni
dizisine
dönüştürüldükten sonra bundan SorguTasiyici
nesnesini oluşturmak için bir durum makinesinden
yararlanılır. Bunun için uygulamada BasitDurumMakinesi
sınıfı yazılmıştır. İçerisinde durumları ifade eden
bir enum yapısı bulunur.
public enum Durum {
BASLA,
SUTUN_ALINDI,
COKLU_SUTUN_ALINDI,
BILGI_ALINDI,
COKLU_BILGI_ALINDI,
KIYAS_ALINDI,
SONUC_SUTUNU_ALINDI,
OLMAK_ALINDI,
TABLO_BULUNDU,
SONUC_KISITLAMA_SAYISI_BEKLE,
SONUC_KISITLAMA_SAYISI_ALINDI,
ISLEM_BELIRLENDI,
}
Ayrıca sınıf
içerisinde aşağıdaki üye değişkenler
bulunur.
public class BasitDurumMakinesi {
private Durum suAnkiDurum = Durum.BASLA;
private SorguTasiyici sorguTasiyici = new SorguTasiyici();
private List<CumleBileseni> bilesenler;
private List<SutunBileseni> sutunBilesenleri = new
ArrayList<SutunBileseni>();
private
List<BilgiBileseni> bilgiBilesenleri = new
ArrayList<BilgiBileseni>();
private
List<Sutun> sonucSutunlari = new ArrayList<Sutun>();
public BasitDurumMakinesi(List<CumleBileseni> bilesenler)
{
this.bilesenler =
bilesenler;
}
}
BasitDurumMakinesi
nesnesi oluşturmak için parametre olarak ayrıştırılmış
cümledeki CumleBilesen
listesi kullanılır. Durum makinesinin farklı durumlar arasında
geçiş yapmasına da bu bileşenlerin tiplerine bakarak
karar verilir. Başlangıç anında makine durumu “BASLA”
konumundadır.
BasitDurumMakinesi
içindeki islet()
sınıfı elimizdeki CumleBileseni
listesindeki tüm elemanları baştan sona alarak durum makinenin
sürülmesini sağlar. Kod aşağıdaki gibidir:
//durum makinesini işleten metot burasıdır.
public SorguTasiyici islet() {
for (CumleBileseni bilesen : bilesenler) {
if (bilesen.tip == CumleBilesenTipi.TANIMSIZ) {
//gereksiz ve işlenemeyen bileşenler burda ihmal
ettirilir.
raporla("Uyari: Islenemeyen bilesen:" +
bilesen.icerik);
continue;
}
suAnkiDurum = gecis(bilesen);
}
// sorgu
tasiyiciya toplanan bazi bilgileri ekle.
sorguTasiyici.sonucSutunlari = sonucSutunlari;
sorguTasiyici.raporla(cozumRaporu.toString());
return sorguTasiyici;
}
Sıradaki
CumleBileseni
ele alındıktan sonra önce onun TANIMSIZ
türden olup olmadığına bakılır. Eğer tanımsız ise bu
bilesen ihmal edilir ama uyarı olarak not edilir. Daha sonra durum
makinesini süren suAnkiDurum
= geçiş(bilesen); satırı
işletilir. Burada sistem o anki durumundan bileşenin tipine göre
farklı bir duruma geçiş yapar. Bu geçiş sırasında
yeri geldiğinde bileşen içindeki veriler kullanılarak
sorguTasiyici
içerisine gerekli bilgiler eklenir.
Örnek:
[adı "Ali" olan işçileri listele] cümlesi ayrıştırıldığında şu CumleBileseni listesi ortaya çıkar:
[adı:SUTUN]
[Ali:KISITLAMA_BILGISI]
[olan:OLMAK]
[işçileri:TABLO]
[listele:ISLEM]
Bu
liste durum makinesinde şu adımları izler:
Bu aşamadan sonra elimizde işlenecek başka
CumleBileseni
kalmadığından sistem o ana kadarki oluşan SorguTasiyici
sınıfını döndürecektir. Yukarıdaki senaryo sadece bir
tür cümle için yazılmıştır ve CumleBileseni
dizilişine göre durum makinesinin yapısı son derece karmaşık
hale kolayca gelebilmektedir(Şekil4.1). Uygulamada durum makinesi
gerçeklemesi switch-case blokları ile gerçekleştirilmiştir. Asagidaki sekilde durum makinesi basitlestirilmis bir sekilde gosterilmistir.
private
Durum gecis(CumleBileseni bilesen) {
CumleBilesenTipi gecis = bilesen.tip();
switch (suAnkiDurum) {
case BASLA:
switch (gecis) {
case SUTUN:
return sutunBileseniGecisi(bilesen);
case TABLO:
return tabloBileseniGecisi(bilesen);
case SONUC_MIKTAR:
return Durum.SONUC_KISITLAMA_SAYISI_BEKLE;
}
break;
case
SUTUN_ALINDI:
switch (gecis) {
case KISITLAMA_BILGISI:
return bilgiBileseniGecisi(bilesen);
case SUTUN:
return cokluSutunBileseniGecisi(bilesen);
}
break;
}
Eğer
herhangi bir durumda beklenmeyen bir geçiş bilgisi gelmiş
ise sistem SQLUretimHatasi
hatası üretir. Bu hata durum makinesini çağıran nesne
tarafından yakalanıp gerekli mesajın gösterilmesi sağlanır.
SQL Cümlesinin Üretimi
DurumMakinesi
sınıfındaki islet()
metodu geriye bir adet SorguTasiyici
nesnesi yollar. Yani cümle bileşenlerindeki veriler sorguya
dair bilgilerin derli toplu bulunduğu SorguTasiyici
nesnesine aktarılmıştır. Bundan sonraki aşama bu nesne
içerisindeki verileri kullanarak gerçek SQL cümlesinin
üretimidir. Bunu yapmak için MsSqlDonusturucu
sınıfı yazılmıştır. Bu sınıf, SorguTasiyici
sınıfı içindeki bilgileri kullanarak SQL Server veri
tabanına uygun şekilde SQL cümlesinin üretilmesini
sağlar.
public
String donustur(SorguTasiyici sorgu) metodu
içerisinde gerçekleştirilen dönüşüm şu
adımlar ile yapılır.
1. Önce bir StringBuilder
nesnesi oluşturulur. Bu yapı arka arkaya String
verileri ulamak için elverişli bir yapıya sahiptir. İlk
olarak SorguTasiyici
içindeki işlem bilgisine göre cümleye nasıl
başlanacağı
belirlenir.
sonuc.append(sorgu.islemTipi.sqlDonusumu());
Burada
IslemTipi
bilgisine göre “select,
delete, insert..”
gibi bilgiler eklenir. Bunu yapan metot IslemTipi
enum sınıfı içindedir.
public String sqlDonusumu() {
switch (this) {
case SORGULAMA:
return "select ";
case EKLEME:
return "insert ";
case GUNCELLEME:
return "update ";
case SILME:
return "delete";
}
}
Daha
sona eğer sorgu dönüş değerini kısıtlayan bir bilgi
var ise bu sorgu cümlesine eklenir. Normalde bu bilginin var
olup olmadığı sorgu.sonucMiktarKisitlamaDegeri
değerinin varsayılan -1
değerinden farklı olması ile anlaşılır.
if (sorgu.sonucMiktarKisitlamaDegeri > -1)
sonuc.append(" top ")
.append(sorgu.sonucMiktarKisitlamaDegeri)
.append(" ");
Örneğin
değer 5 ise “
top 5”
kelimesi oluşmakta olan cümleye eklenir.
Eğer sorgu
dönüş değerlerinde sadece belli sutunların gösterilmesi
istenmişse SorguTasiyici
içindeki sonucSutunlari
listesinde
Sutun
nesneleri
olması gerekir. Eğer varsa, bu sutunların adları virgül ile
ayrılarak sonuç cümlesine eklenir. Eğer bu sutunlar yok
ise yıldız ‘*’
simgesi
eklenir.
if
(sorgu.sonucSutunlari.isEmpty())
sonuc.append("
* ");
Sorgu
taşıyıcı içindeki Tablo
nesnesinden gerçek veri tabanı tablo adı elde edilir ve
“from”
kelimesi ile birlikte sorgu cümlesine eklenir.
sonuc.append("
from ").append(sorgu.tablo.getAd()).append(" ");
Eğer
sorgu taşıyıcı nesnesinde SutunKisitlamaBileseni
listesi varsa “where”
kelimesi eklenir ve listedeki SutunKisitlamaBileseni
nesnelerinin hepsi için sqlDonusumu()
metodu çağrılarak oluşacak kelimeler cümleye eklenir.
bu metot içerisinde sutun, bilgi ve kıyas türüne
göre SQL cümlesi uygun şekilde üretilir. Aşağıda
bir kod parçası gösterilmiştir:
KiyasTipi kiyasTipi = bilgiBileseni.getKiyasTipi();
boolean matematikselKiyas = true;
String deger = "";
switch (kiyasTipi) {
case BUYUK:
deger = ">";
break;
case KUCUK:
deger = "<";
break;
case BUYUK_ESIT:
deger = ">=";
break;
case KUCUK_ESIT:
deger = "<=";
break;
case ESIT:
deger = "=";
break;
case ESIT_DEGIL:
deger = "!=";
break;
default:
matematikselKiyas = false;
break;
}
Sonuçta
ortaya çıkan cümle String
olarak döndürülür. İstenirse bu bilgi gerçek
bir veri tabanına gönderilebilir.
Örnek: ismi "A" ile başlamayan, numarası "9" dan büyük olmayan ilk 100 çalışanın numara ve adını göster. Cümlesi için program çıktıları aşağıdaki gibidir.
“Çözümle” çıktısı aşağıdaki gibidir(Şekil5.1).
Şekil 5.1. Çözümle çıktısı
“Kısıtlı Çözümle” çıktısı aşağıdaki gibidir(Şekil5.2).
Şekil 5.2. Kısıtlı çözümle çıktısı
Şekil 5.3. Ayrıştır çıktısı
Şekil 5.4 SQL üret çıktısı
Bu çalışmada Türkçe doğal dil cümlelerden SQL diline dönüşüm konusu incelenmiş, ve dar bir kapsamda gerçekleştirilen bir uygulama ile basit bir yaklaşımla bile belirli bir derecede başarılı dönüşümleri sağlanabildiği gösterilmiştir. Yaklaşımda cümlede kullanılan kelimeler belirli bir miktar soyutlama ile veri tabanı sorgu kavramlarına denk düşürülmeye çalışılmış, daha sonra oluşturulan bu cümle bileşenleri basit yapılı bir durum makinesi kullanılarak sorgu verilerini taşıyan bir yapıya aktarılmıştır. Bu veri yapısı kullanılarak gerçek SQL cümle dönüşümü sağlanmıştır.
Eldeki doğal dil işleme araçlarının eksikliği ve
konunun genel olarak zor olması nedeni ile uygulamada kullanılan
yaklaşımın çok tablolu, karmaşık sorgu yapılarına uygun
olmadığı söylenebilir. Yine de bu hali
ile bile sistem dar kapsamda pek cok cümlelyi
dönüştürebilmektedir. Cümle analizi
konusunda başarılı bir mekanizma kurulup oluşan cümle
parçalarının yorumlanmasını sağlayan karmaşık durum
makinesi grupları ile çok daha yüksek başarımlı
dönüştürme mekanizması çalışmalarının
yapılması mümkündür.