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 bi
r çalışma olarak görülmektedir. Bunun nedenleri genel olarak:



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ı :


Bunu
nla 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).

  1. Veri tabanı yapısal bilgilerinin ve gerekli dilbilgisi bilgilerinin okunması ve bellekte özel veri yapılarına yerleştirilmesi.

  2. Doğal dil cümlenin önce kelimelere sonra sorgu cümle bileşeni dizisi şeklinde ayrıştırılması.

  3. Basit bir durum makinesi ile ayrıştırılmış bileşenlerden sorgu bilgisinin taşındığı özel veri yapısının oluşturulması.

  4. Sorgu bilgilerini taşıyan veri yapısından uygun veri tabanına göre gerçek SQL cümlesinin oluşturulması.


Şekil 3.1. İzlenen yöntem


4. ARAŞTIRMA BULGULARI


4.1 Bilgilerin Okunması


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 kulla
nı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
}


4.3.1 Tablo Bileşeni


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.


4.3.2 İşlem Bileşeni


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.


4.3.3 Sütun Bileşeni


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 ter
sine 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:

  1. Şu anki durum: BASLA, geçiş bileşeni: [adı:SUTUN], sütun bilgisini sutunBilesenleri listesine köy. Yeni durum: SUTUN_ALINDI
  2. Şu anki durum: SUTUN_ALINDI, geçiş bileşeni: [Ali:KISITLAMA_BILGISI], bilgiyi bilgiBilesenleri listesine koy, Yeni durum: BILGI_ALINDI
  3. Şu anki durum: BILGI_ALINDI, geçiş bileşeni: [olan:OLMAK]. Olmak bileşenine rastlandığında şu ana kadarki toplanmış sutun ve bilgi bileşenleri SutunKisitlamaBileseni'ne dönüştürülerek sorguTasiyici içine eklenir ve sutunBilesenleri, bilgiBilesenleri listeleri sıfırlanır.
  4. Yeni Durum: OLAN_ALINDI. Eğer OlmakBileseni içinde olumsuzluk değeri olsaydı, o ana kadar toplanmış BilgiBilesenleri içindeki kıyas tipi değerleri tersine çevrilecekti.
  5. Şu anki durum: OLAN_ALINDI, geçiş bileşeni [işçileri:TABLO]. SorguTasiyici nesneye bilesen içindeki Tablo nesnesi aktarılır. Bu durum için CALISANLAR tablosu atanacaktır.
  6. Şu anki durum : TABLO_BULUNDU, geçiş bileşeni [listele:İŞLEM].   SorguTasiyici nesneye bilesen içinde yer alan IslemTipi değeri aktarılır. Bu durum için islemTipi değeri SORGULAMA olacaktır.


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.

Şekil 4.1.Basit durum makinesinin çalışması
Aşağıda ilgili koddan bir parça gösterilmiştir.


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ı


Ayrıştır” çıktısı aşağıdaki gibidir (Şekil 5.3).

Şekil 5.3. Ayrıştır çıktısı


Sql Üret” çıktısı aşağıdaki gibidir (Şekil 5.4).

Şekil 5.4 SQL üret çıktısı



5. SONUÇ


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.