Make love, not var_dump())

Captain's Log

Günümüz web uygulamalarında arama, özellikle metin arama (full text search) en karmaşık konulardan birisi haline gelmiş durumda. Performans kaygınız varsa veya seçtiğiniz veritabanı motoru (InnoDB?) full text search desteklemiyorsa harici bir arama motoru kullanmanız gerekli.

Solr ise "Lucene tabanlı arama motorları ailesi"nin bir üyesi. Lucene; temelde metin analizi, indexleme ve arama gibi temel işlevleri sunan bir Apache projesi. Solr ise, Lucene temelini kullanan, onun üstüne filtreleme, faceting(Türkçesi var mı acaba?), önbellekleme, dağıtık mimari desteği gibi şeyleri kolay yapılandırma dosyaları ile yönetebilmenizi ve bu verileri XML, JSON, Binary gibi çeşitli formatlarda almanızı sağlayan bir üst proje.

Solr Kurulumu

http://lucene.apache.org/solr/downloads.html adresinden Apache Solr'ın son sürümü (yazı hazırlanırken 4.4 sürümü vardı) indirebilirsiniz. Solr kaynak kodlarını indirebileceğiniz gibi içinde hazır örnek uygulamaların da olduğu bir paketi (solr-4.4.0.zip) indirebilirsiniz.

Çalıştırma

Askere gidenler bilir; önce yaparak öğretme diye bir şey vardır. Bir eğitimi detaylı anlatmadan önce bir kere gösterir, sonra detaylara geçersiniz. Burada da önce, mevcut örnek uygulamayı bir çalıştırıp, sonra Solr'ın bir kaç yapılandırma dosyasını anlatacağım.
Zip'i açtığınızda içinde bir örnek uygulama bulunmakta. Eğer Java Runtime Environment'ınız kurulu ise aşağıdaki komutlarla Solr'ı çalıştırabilirsiniz.
$ cd solr-4.4.0/example/
$ java -jar start.jar

Solr, öntanımlı olarak 8983 portunda çalışır. Tarayıcınızdan http://localhost:8983/solr/ adresini ziyaret ettiğinizde aşağıdaki gibi bir yönetim paneli arayüzü göreceksiniz. Sol altta da mevcut "collection"ları görebilirsiniz. Örnek collection'a tıkladığınızda da aşağıdaki gibi bir ekran göreceksiniz.

Solr admin

Şu anda hiç "döküman"ımız yok. Solr her şeyi bir "döküman" olarak saklar. Dökümanları veritabanındaki "row"lar olarak düşünebilirsiniz. Örnek solr uygulaması, "exampledocs" isimli bir dizinde bir çok hazır döküman bulunduruyor. Daha sonra bu dökümanlara ve şemalara deyineceğim ama önce "yaparak göstermek" açısından örnek dökümanları ekleyip basit bir kaç arama yapalım.

Örnek birkaç döküman eklemek için komut satırında
$ cd exampledocs 
$ java -jar post.jar *.xml
komutlarını verdiğinizde aşağıdaki gibi bir çıktı alacaksınız
SimplePostTool version 1.5
Posting files to base url http://localhost:8983/solr/update using content-type application/xml..
POSTing file gb18030-example.xml
POSTing file hd.xml
POSTing file ipod_other.xml
POSTing file ipod_video.xml
POSTing file manufacturers.xml
POSTing file mem.xml
POSTing file money.xml
POSTing file monitor2.xml
POSTing file monitor.xml
POSTing file mp500.xml
POSTing file sd500.xml
POSTing file solr.xml
POSTing file utf8-example.xml
POSTing file vidcard.xml
14 files indexed.
COMMITting Solr index changes to http://localhost:8983/solr/update..
Time spent: 0:00:00.268

http://localhost:8983/solr/#/collection1 adresindeki yönetim paneline tekrar döndüğünüzde artık döküman sayınızın artığını görebilirsiniz.

Test etmek için;

Bunun yanında, örnek uygulama içinde, velocity template'ları ile geliştirilmiş "basit bir e-ticaret arama sayfası" da bulunuyor: http://localhost:8983/solr/browse

Artık "yaparak gösterme" kısmını geride bıraktığımıza göre biraz derine inmenin vakti geldi.

solrconfig.xml

İlgili collection'ın "conf" dizini altında bulunan bu dosyada solr genel yapılandırma ayarları, yukarıdaki örneklerde "query", "select", "spell" gibi farklı adreslerde gördüğünüz, bazı öntanımlı değerleri verip bazı ayarları yapabileceğiniz "requestHandler"lar, isteğe dönen cevap tipini ayarlayabileceğiniz queryResponseWriter'ları tanımlayabilirsiniz. Örnek içindeki dosya şuradaki gibi: https://gist.github.com/anonymous/038d4c991297a9352e5a

schema.xml

Daha önce bahsettiğim gibi, solr'daki her kayıt bir "döküman". Schema.xml de bu "döküman"ı tanımlamamız gereken dosya. <fields> alanındaki her <field> relational database'lerden alışık olduğumuz "column"lara denk geliyor. Burada kabaca field'ımızın adı(name), solr aramalarında kullanılıp kullanılmayacağı(indexed), bulunan kayıtlarda cevaplar içinde dönüp dönmeyeceği(stored), birden çok değer alıp alamayacağı(multiValued) ve hangi tipte bir veri içereceği(type)ni belirttiğimiz alanları içerir.

Bunun yanında uniqueId'yi belirleyebileceğimiz, bazı wildcard'lı field adları ile dynamic field'lar belirleyebileceğimiz, birden çok alanı tek alanda birleştirip arama yapabileceğimiz(copy) tanımlamaları bu dosyada belirtiyoruz.

Ayrıca, buradaki veri tiplerinin de class tanımlamaları ve varsa aldığı parametreler bu dosyada tanımlanıyor. Örnek içindeki dosya da şuradaki gibi: https://gist.github.com/anonymous/fb851195ea33e0fae9a7

Kendi döküman tipinizi schema.xml dosyasında belirleyebilirsiniz.

PHP ve Solr

Solr temelde HTTP istekleri ile çalıştığı için, basitçe cURL istekleri ile veriyi alıp, responseWriter olarak da json kullanırsanız basitçe verileri decode edip PHP içinde array ve obje olarak kullanabilirsiniz. Örnek olarak yukarıdaki ilk örneği aşağıdaki gibi Object olarak kullanabilirsiniz PHP tarafında.
<?php 
$url = "http://localhost:8983/solr/collection1/query?q=*";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch));
curl_close ($ch);

var_dump($response);
Ama istekler çeşitlenmeye başladığında istek yapmanız gereken adres çok karmaşık bir hal lamaya başlayacak. Curl ile uğraşıp, url generate etmek istemiyorsanız da Solarium gibi daha "high level" bir kütüphane kullanabilirsiniz.

Solarium, sizin için, daha "anlaşılabilir" bir programlama arayüzü ile Solr istekleri yapmanızı ve bu istekleri "parse etmenizi" sağlıyor. Bunun yanında buffer'lı şekilde döküman ekleme gibi de bir çok güzellik sunuyor.

Solarium kurulum

Solarium'u projenize dahil etmek için birçok yol olsa da, en güzeli composer ile eklemek. composer.json dosyanıza aşağıdaki "require" satırını ekledikten sonra composer install demeniz yeterli.
{
    "require": {
        "solarium/solarium": "3.1.2"
    }
}

Veri ekleme

<?php

require __DIR__. '/vendor/autoload.php';                                                        

$config = array(
    'endpoint' => array(
        'localhost' => array(
            'host' => '127.0.0.1',
            'port' => 8983,
            'path' => '/solr/',
        )
    )
);

$client = new Solarium\Client($config);

$update = $client->createUpdate();

// dokumanı oluşturuyoruz
$doc = $update->createDocument();
$doc->id = 1;
$doc->name = "Sonsuzdongu";
$doc->cat = "Yazılım";

// sonra dokumani commit'liyoruz
$update->addDocument($doc);
$update->addCommit();

// ve update sorgusunu calistiriyoruz
$result = $client->update($update);

// artik dokumanimiz eklendi
var_dump($result);

Veri Filtreleme

<?php

require __DIR__. '/vendor/autoload.php';

$config = array(
    'endpoint' => array(
        'localhost' => array(
            'host' => '127.0.0.1',
            'port' => 8983,
            'path' => '/solr/',
        )
    )
);

$client = new Solarium\Client($config);                         
                                                                
$query = $client->createSelect();                               
                                                                
// filtre olustur
$query->createFilterQuery('name')->setQuery('name:sonsuzdongu');
                                                                
$resultSet = $client->select($query);                           
                      

echo 'Toplam: '.$resultSet->getNumFound(); 
                      
//sonuclari ekrana bas                                                                
foreach($resultSet as $result) {                                
    var_dump($result->name);                                    
}             
Gördüğünüz gibi Solarium ile cURL ile yaptığımızdan daha "semantik" şekilde sorguları çalıştırıp sonuçları alabiliyoruz. Solr ve Solarium'dan şimdilik bu kadar.