Lucene.Net ile arama

Bir e-ticaret projesi yaptığınızı düşünün. Arama sayfasınız nasıl yapardınız? Input'a "Asus" kelimesi girdiğinde yapacağınız en şey an basit haliyle aşağıdaki sql sorgusunu çalıştırmak olacak.

Select * from dbo.Urun where dbo.Urun.Adi like '%Asus%'

Bu sorgu 1000 belki 10.000 ürün bulunan bir tablo için çok yorucu olmayabilir. Peki ya tablonuzda 500.000 ürün varsa?  Yaptığınız arama çok fazla performans kaybına ve kullanıcının saniyelerce beklemesine neden olabilir. İşte bu gibi durumları aşmak için kullanılan bir kütüphane Lucene.  Lucene.Net ise orjinali Java olan Lucene'nin C# ile yazılımış ve .Net geliştiricilerinin kullanımına sunulmuş .Net portu.

Lucene.Net bir uygulama değil bir dll'dir. Yaptığı şey ise, indekslemek istediğiniz veriyi diskinizde kendine özel bir dosyaya kaydetmesi ve aradığınız zaman bu dosyadan araması. İlişkisel veritabanlarındaki text aramasına göre oldukça performanslı bir sonuç döndürüyor. Örnek uygulamamızı incelersen daha iyi anlayacaksınız.

Uygulamada  NorthWind isimli örnek veritabanını kullanmayı düşünüyorum. http://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=23654 Bu linkten sql sorgusunu indirip çalıştırdığınızda veritabanı kurulacaktır. Daha sonra Yeni bir console application açıp nuget ile Lucene.Net paketini yüklüyorum.


 

Yapacağımız ilk şey veritabanındaki Products Tablosundaki ürünleri indekslemek olacak. Ben ugulamada sadece ürünlerin adlarını ve id'lerini indexliyorum. Daha sonra lucene ve sql aramaları arasındaki farkı karşılaştıracak metodları yazıyorum. Elimden geldiğince basit ve olarak yazmaya çalıştım kodu. Kodun içindeki açıklamaları anlayacağınızı düşünüyorum.

Sonuca gelince,  kullandığımız NorthWind veritabanında çok az veri olduğu için sql arama daha hızlı çalışıyor. 

Ama içinde 300.000 satır olan bir tabloda yaptığım denemede lucene ile sql server arasındaki  yaklaşık 100 kat farkı görebilirsiniz.

using System;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
 
namespace LuceneUygulama
{
    class Program
    {
        static void Main(string[] args)
        {
            string indeksYolu = @"D:\LuceneDeneme";
            Directory.CreateDirectory(indeksYolu);
            // indeks'in oluşturulacağı klasörü oluşturuyoruz. 
            // Projeyi çalıştırdıktan sonra bu adrese gidip oluşan dosyaları görebilirsiniz.  // İndeksleme yapıyoruz. Projeyi bir kere çalıştırdıktan sonra tekrar çalıştırırsanız ikinci defa indeksleme yapacaktır. 
            // Bu da aynı ürünü n defa indexlemiş olmak anlamına geliyor. o yüzden ilk çalıştırmadan sonra bu kısmı commentlemeniz gerekiyor.             SqlArama("Original");
            LuceneArama(indeksYolu, "Original");
            // ve aramalar
        }
 
 
        static void Indeksleme(string indeksYolu)
        {
 
            Analyzer analyzer = new StandardAnalyzer();
            var indeksYazici = new IndexWriter(indeksYolu, analyzer, IndexWriter.MaxFieldLength.UNLIMITED);
            
            SqlConnection sqlConn = new SqlConnection();
            sqlConn.ConnectionString = "Server=YBB-PC; Initial Catalog=NorthWind; Integrated Security=true;";         
            sqlConn.Open();
            SqlCommand cmd = new SqlCommand("Select ProductId, ProductName From Products", sqlConn);
            SqlDataReader oku = cmd.ExecuteReader();
 
 
            DateTime indexBaslangic = DateTime.Now;
            Console.WriteLine("İndeksleme başlıyor => " + indexBaslangic);
 
            while (oku.Read())
            {
                var doc = new Document();
                doc.Add(new Field("Id", oku["ProductId"].ToString(), Field.Store.YES, Field.Index.NO)); 
                // Id' kolonu dökümanda yer alsın ama indekslenmesin diyoruz
 
                doc.Add(new Field("Name", oku["ProductName"].ToString(), Field.Store.YES, Field.Index.ANALYZED)); 
                //ProductName kolonu indekslensin
 
                indeksYazici.AddDocument(doc);
            }
            indeksYazici.Optimize();
            indeksYazici.Commit();
            DateTime indexBitis = DateTime.Now;
            Console.WriteLine("İndeksleme bitti => " + indexBitis);
            Console.WriteLine("Süre: " + (indexBitis - indexBaslangic));
            Console.WriteLine("İndekslenen döküman sayısı: " + indeksYazici.DocCount());
            indeksYazici.Close();
        }
 
        private static void LuceneArama(string indeksYolu, string metin)
        {
 
            Analyzer analyzer = new StandardAnalyzer();
 
            var parser = new QueryParser("Name", analyzer);
            Query query = parser.Parse(metin);
            // döküman içindeki Name isimli Fiel'da verilen metin değerini arayacak bir sorgu oluşturduk.
 
            var aramaAraci = new IndexSearcher(indeksYolu, true);
            // yukardaki oluşturduğumuz sorguyu, verilen indeks yolundaki dökümanda arayacak.
 
            DateTime baslangic = DateTime.Now;
            Console.WriteLine("Lucene arama başladı =>" + baslangic);
 
            Hits hits = aramaAraci.Search(query);
            //Arama yapılır
 
            DateTime bitis = DateTime.Now;
            Console.WriteLine("Lucene arama bitti =>" + bitis);
            Console.WriteLine("Süre: " + (bitis - baslangic));
            int sounucSayisi = hits.Length();
            Console.WriteLine("Yapılan aramada {0} sonuç bulundu", sounucSayisi);
 
            aramaAraci.Close();
 
        }
 
 
        static void SqlArama(string metin)
        {
 
            SqlConnection sqlConn = new SqlConnection();
            sqlConn.ConnectionString = "Server=YBB-PC; Initial Catalog=NorthWind; Integrated Security=true;";
            sqlConn.Open();
            SqlCommand cmd = new SqlCommand("Select ProductId From Products where ProductName like '%"+metin+"%'", sqlConn);
            SqlDataReader oku = cmd.ExecuteReader();
 
            DateTime baslangic = DateTime.Now;
            Console.WriteLine("Sql arama başlıyor => " + baslangic);
 
            DataTable dt = new DataTable();
            dt.Load(oku);
           
            DateTime bitis = DateTime.Now;
            Console.WriteLine("İndeksleme bitti => " + bitis);
            Console.WriteLine("Süre: " + (bitis - baslangic));
            Console.WriteLine("Yapılan aramada {0} sonuç bulundu", dt.Rows.Count);
 
            sqlConn.Close();
        }
    }
}

5 thoughts on “Lucene.Net ile arama

  1. mevlüt bostancı Reply

    merhaba;çalışma için ellerine sağlık,
    bir sorum olacak, varsayalım ki içerikleri html ler içinde ve bu html belgelerden yüzlerce var lucene ile bu html belgeler içindeki veriyi nasıl inxdexleriz

    teşekküler.

  2. Mustafa Reply

    Merhaba,

    Makale için teşekkürler. Yalnız verilen örnekte performansı bir de ms-sqlde de ürün adına index vererek deneyebilir misiniz? Bakalım sonuç nasıl olacak.

    İyi çalışmalar

    • Ekrem Reply

      Bu denemeyi yaparken birde Take uygularmısınız. O zaman sonuçlara bakarsanız durum daha fazla değişecektir.

Leave a Reply

Your email address will not be published. Required fields are marked *