محتویات سایت
        برچسب های محبوب 








 
   معرفی کتابخانه ی مشهور و قدرتمند Lucene.net -- قسمت اول
  در این مقاله به معرفی کتابخانه ی مشهور و قدرتمند Lucene.net و روش های تولید ایندکس ها و جستجوی ساده توسط این کتابخانه می پردازم.
   C#
   ۲۶۴۵۱
   دریافت فایل ضمیمه
   مرتضی صحراگرد
   ۱۳۸۹/۳/۱
نسخه قابل چاپ نسخه قابل چاپ

مقدمه:

قصد دارم در چند مقاله به بررسی کتابخانه ی Lucene.net و تکنیک های مختلف استفاده از آن در برنامه های تحت ویندوز و وب بپردازم. اولین قسمت از این سلسله مقالات را ملاحظه می نمایید.

یکی از چالش های پیش روی توسعه دهندگان نرم افزار های تحت ویندوز و وب، مبحث جستجو می باشد. البته این مشکل زمانی خود را نشان می دهد که مقدار بسیار زیادی اطلاعات در پایگاه داده ذخیره شده باشد.

جهت بالا بردن راندمان برنامه هنگام جستجو از روش های مختلفی استفاده می شود. این روش ها از ساده ترین ترفندها که ایندکس گذاری بر روی فیلدهای جداول باشد شروع شده و تا پیشرفته ترین تکنیک ها در مورد پایگاه داده و حتی در سطح برنامه کاربردی ادامه می یابد.

اما وقتی قرار باشد از عملگر Like برای کوئری زدن بر روی پایگاه داده استفاده شود، دیگر تکنیک های ذکر شده در بالا کارایی ندارند زیرا عملیات جستجو و مقایسه در اینگونه کوئری ها به صورت کاراکتر به کاراکتر انجام می شود و در نتیجه این کوئری ها زمانی که داده های فراوانی در پایگاه داده ذخیره شده باشند، بسیار زمانبر و پر هزینه می باشند.

حال فرض کنید که شما در حال توسعه ی یک سایت خبری یا یک وبلاگ و یا حتی یک برنامه تحت ویندوز که حاوی داده های متنی فراوان می باشد، هستید. چگونه باید در اینگونه مواقع امکان جستجو را برای کاربر فراهم نمود؟

برای رفع این مشکل روش های مختلفی موجود می باشد. یکی از این روش ها استفاده از سرویس Full-Text برنامه Microsoft SQL Server می باشد. این روش دارای معایب و مزایایی می باشد که بررسی آن خارج از اهداف این مقاله می باشد.

روش دیگری که ما در این مقاله قصد بررسی آن را داریم، استفاده از کتابخانه Lucene.net می باشد. با استفاده از این کتابخانه سرعت و راندمان جستجو به طرز شگفت انگیزی افزایش می یابد.

آغاز:

کتابخانه ی Lucene.net در حقیقت نسخه ای سفارشی شده برای استفاده در بستر DotNet Framework می باشد. اصل این کتابخانه با نام Lucene برای زبان جاوا نوشته شده است. البته هم اکنون کتابخانه ی Lucene.net به اندازه کتابخانه ی Lucene غنی نشده است. با این وجود کتابخانه Lucene.net به طور مداوم در حال رشد بوده و همواره قدرتمند تر خواهد شد.

کتابخانه ی Lucene.net بسیار بزرگ و پرکاربرد می باشد و معرفی همه امکانات این کتابخانه امکان پذیر نمی باشد. در این چند مقاله قصد دارم به معرفی پرکاربرد ترین تکنیک ها جهت استفاده از این کتابخانه بپردازم.

متاسفانه با وجود اینکه توسعه گران مختلف مرتبا در حال رفع باگ ها و اضافه نمودن امکانات جدید به این کتابخانه می باشند، نسخه جدید این کتابخانه از سال 2007 تاکنون به طور رسمی منتشر نشده است. بنابراین برای دریافت آخرین نسخه سورس برنامه ودموها و مستندات باید با استفاده از ابزار کنترل سورس SubVersion به اینجا مراجعه نمایید.

تذکر:

جهت رفاه حال کاربرانی که با ابزار کنترل سورس SubVersion آشنایی ندارند و یا به هر نحوی برای دریافت سورس این برنامه مشکل دارند، آخرین نسخه ی باینری این کتابخانه را که در هنگام نوشتن این مقاله منتشر شده است (v 2.9.2) می توانید از لینک بالای صفحه دریافت نمایید.

قبل از اینکه به جزئیات این کتابخانه بپردازم باید کلیاتی را در مورد نحوه ی عملکرد Lucene.net مطرح کنم. این برنامه ایندکس های فیلدهای جداول بانک اطلاعاتی را به صورت فیزیکی بر روی هارد دیسک ذخیره می کند. هنگامی که کاربر قصد دارد عبارتی را جستجو نماید، ما با استفاده از این کتابخانه در ایندکس های تولید شده توسط برنامه، جستجو می کنیم و نتایج مورد نظر خود را پیدا می کنیم.

ایندکس های ذخیر شده توسط این برنامه می مشابه شکل زیر می باشد.


استفاده از این کتابخانه شامل دو مرحله می باشد.

  1. تولید ایندکس های مورد نیاز برای جستجو
  2. جستجو در ایندکس های تولید شده

برای درک صحیح و بهتر مطالب، ادامه مقاله را با ذکر یک مثال ادامه می دهیم. فرض کنید جدولی به نام News در پایگاه داده داریم که ساختار آن به شکل زیر می باشد.

CREATE TABLE [dbo].[News](
    [NewsID] [intIDENTITY(1,1) NOT NULL,
    [WriterID] [intNOT NULL,
    [Title] [nvarchar](500) NOT NULL,
    [Body] [nvarchar](maxNOT NULL,
)

نکات مهم:

به طور کلی هنگام جستجو در بانک های اطلاعاتی با سه گروه داده مواجه هستیم. (به تفاوت ایندکس ها و محتوای فیلد ها دقت کنید)

  1. اطلاعاتی که صرفا در جستجو شرکت می کنند ولی هنگام نمایش نتایج پیدا شده مورد استفاده قرار نمی گیرند. به طور مثال ما قصد داریم در این مقاله در فیلد Body مربوط به خبر که متن کامل خبر در آن قرار دارد، جستجو نماییم. ولی هنگام نمایش لیست نتایج یافت شده، فقط عنوان خبر را نمایش می دهیم. سپس کاربر می تواند روی عنوان خبر کلیک نموده و به فرم نمایش خبر وارد شده و متن کامل را مشاهده نماید. پس در اینجا فیلد Body در جستجو شرکت داده شده است ولی هنگام نمایش نتایج جستجو، شرکت داده نشده است. برای اینگونه اطلاعات ما باید فقط ایندکس ها را نگهداری کنیم و نیازی به ذخیره محتوای فیلد نداریم. (در ادامه مقاله این اعمال را مشاهده خواهید نمود)
     
  2. اطلاعاتی که در جستجو شرکت نمی کنند ولی در هنگام نمایش نتایج جستجو، شرکت داده می شوند. به طور مثال ما در این مقاله قصد نداریم روی فیلد NewsID که شناسه خبر را نگهداری می کند جستجو انجام دهیم ولی بدیهی است که هنگام نمایش نتایج بدست آمده از جستجو، به این فیلد نیاز داریم تا لینک اصلی به خبر را بسازیم. درنتیجه اینگونه اطلاعات در جستجو شرکت نمی کنند ولی هنگام نمایش نتایج مورد استفاده قرار می گیرند. برای اینگونه اطلاعات ما نیازی نیست که ایندکس ها را نگهداری کنیم ولی باید محتوای خود فیلد را جهت بازیابی هنگام جستجو ذخیره نماییم.
     
  3. اطلاعاتی نیز وجود دارند که هم در جستجو شرکت می کنند و هم هنگام نمایش نتایج جستجو مورد استفاده قرار می گیرند. به طور مثال ممکن هست که ما قصد داشته باشیم بر اساس Title نیز جستجو انجام دهیم و قطعا هنگام نمایش نتایج بدست آمده از جستجو نیز به این فیلد برای نمایش نتایج نیازمندیم. در مورد اینگونه اطلاعات، هم باید ایندکس های فیلد و هم محتوای فیلد ذخیره شود.

در صورتی که مفهوم دقیق نکات بالا را متوجه نشده اید، لطفا مجددا مروری بر آن ها نمایید.

تذکر:

با توجه به اینکه اهداف این مقاله صرفا آموزشی می باشد، سعی شده است که ساده ترین سناریو تدوین شده و مشکلات به ساده ترین شکل ممکن حل شوند. بنابراین شما باید در برنامه های کاربردی موارد را بر اساس نیاز خود سفارشی سازی نمایید.

ابتدا کلاسی به نام News خواهیم نوشت که معادل جدول News در پایگاه داده می باشد و دارای تابعی به نام GetAllNews است که وظیفه آن بازیابی کلیه خبر های موجود در جدول News می باشد.

public class News
{
    public int NewsID { get; set; }
    public int WriterID { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
 
 
    public static List<News> GetAllNews()
    {
        using (SqlConnection con = new SqlConnection("Connection String"))
        {
            List<News> lstNews = new List<News>();
            string query = "SELECT NewsID, WriterID, Title, Body FROM News";
            SqlCommand cmd = new SqlCommand(query, con);
            con.Open();
            SqlDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                lstNews.Add(
                    new News()
                        {
                            NewsID = Convert.ToInt32(reader["NewsID"]),
                            WriterID = Convert.ToInt32(reader["WriterID"]),
                            Title = Convert.ToString(reader["Title"]),
                            Body = Convert.ToString(reader["Body"]),
 
                        }
                    );
            }
            return lstNews;
        }
    }
 
}

خوب، اکنون زمان استفاده از این کتابخانه جهت تولید فایل های ایندکس فرا رسیده است. اسمبلی Lucene.Net.dll را به برنامه ی خود اضافه نموده و فضاهای نامی زیر را به کلاس اضافه نمایید.

using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.Search;
using Lucene.Net.Store;

تذکر:

در آخرین نسخه ی ارائه شده از کتابخانه Lucene.net برخی توابع به شکل Obsolete در آمده اند و احتمالا بزودی از این کتابخانه حذف خواهند شد. ولی متاسفانه با توجه به عدم وجود مستندات کافی برای روش های جدید و کارا بودن روش های فعلی، در این مقاله از این توابع استفاده شده است.

تولید ایندکس ها با استفاده از Lucene.net:

اکنون زمان نوشتن تابع BuildIndexes فرا رسید است. ابتدا تابع را نوشته و سپس به بررسی آن می پردازیم.

private Lucene.Net.Store.Directory GetNewsIndexDirectory()
{
    string path = "C:\\IndexesLocation";
    if (!System.IO.Directory.Exists(path))
    {
        System.IO.Directory.CreateDirectory(path);
    }
    return FSDirectory.GetDirectory(path, false);
}
 
private void BuildIndexes()
{
    // Create IndexWriter
    IndexWriter indexWriter = new IndexWriter(GetNewsIndexDirectory(), new StandardAnalyzer(), true);
 
    try
    {
        Lucene.Net.Store.FSDirectory.SetDisableLocks(true);
 
        List<News> newses = News.GetAllNews();
        foreach (News news in newses)
        {
            Document doc = new Document();
 
            doc.Add(new Field("NewsID",
                              news.NewsID.ToString(),
                              Field.Store.YES, Field.Index.NO, Field.TermVector.NO));
            doc.Add(new Field("WriterID", news.WriterID.ToString(), Field.Store.NO, Field.Index.NOT_ANALYZED,
                              Field.TermVector.NO));
            doc.Add(new Field("Title", news.Title, Field.Store.YES, Field.Index.ANALYZED,
                              Field.TermVector.NO));
 
            doc.Add(new Field("Body", news.Body, Field.Store.NO, Field.Index.ANALYZED,
                              Field.TermVector.NO));
 
            indexWriter.AddDocument(doc);
        }
 
        //make sure we optimize the index after building it
        indexWriter.Optimize();
    }
    catch (Exception ex)
    {
 
        //oops
        //  Log.Error(this, e.Message);
    }
    finally
    {
        //we need to make sure that we close this!
        if (indexWriter != null)
        {
            //close the index
            indexWriter.Close();
        }
    }
}

قطعه کد بالا از چندین قسمت تشکیل شده است که به بررسی آن ها می پردازیم.

وظیفه متد GetNewsIndexDirectory برگرداندن یک شیء از نوع کلاس Directory مربوط به کتابخانه Lucene.net می باشد. (این کلاس را با کلاس Directory خود فریم ورک DotNet اشتباه نگیرید). کلاس Directory شامل اطلاعاتی در مورد محل ذخیره سازی ایندکس ها می باشد. همانطور که ملاحظه می شود ما در این مقاله قصد داریم ایندکس ها را در درایو "C" و پوشه IndexesLocation زیر ذخیره نماییم.

private Lucene.Net.Store.Directory GetNewsIndexDirectory()
{
    string path = "C:\\IndexesLocation";
    if (!System.IO.Directory.Exists(path))
    {
        System.IO.Directory.CreateDirectory(path);
    }
    return FSDirectory.GetDirectory(path, false);
}

در قسمت اول از تابع BuildIndexes همانطور که در زیر ملاحظه می نمایید، یک شی از نوع کلاس IndexWriter ایجاد نموده ایم. برای نوشتن ایندکس ها ما به این کلاس نیاز مندیم. سپس به سازنده ی این کلاس پارامتر های مورد نیازش را ارسال نموده ایم. پارامتر اول Directory مربوط به ذخیره سازی ایندکس ها می باشد.

پارامتر دوم تحلیلگری می باشد که ما جهت ایندکس نمودن از آن استفاده نموده ایم. ما از تحلیل گر استاندارد (StandardAnalyzer) که پرکاربرد ترین تحلیل گر می باشد استفاده نموده ایم. تحلیل گر های مختلفی برای این کتابخانه وجود دارند و همواره انواع جدیدی نیز در حال اضافه شدن هستند. برخی تحلیل گر ها قسمت های مختلف یک کلمه را مورد جستجو قرار می دهند. به طور مثال اگر کلمه Develop جستجو شود، متونی که دارای کلمات Developer و Development و غیره هستند نیز پیدا می شوند (که البته اینگونه تحلیل گر ها در زبان پارسی کاربردی ندارند).

اما تحیل گر استاندارد که ما از آن استفاده نموده ایم، کلمات را به حروف کوچک تبدیل نموده و حروف اضافه ای که در جستجو کاربر ندارند (مانندAnd و Or) را حذف نموده و ایندکس ها را ایجاد می نماید.

پارامتر سوم یک متغیر منطقی (Boolean) است که چون ما برای اولین بار در حال ایجاد این ایندکس ها هستیم مقدار true را به آن اختصاص داده ایم. اگر در حال بروز رسانی باشیم باید مقدار false را به آن اختصاص دهیم. در غیر این صورت ایندکس های قبلی از بین می روند.

IndexWriter indexWriter = new IndexWriter(GetNewsIndexDirectory(), new StandardAnalyzer(), true);

در قسمت بعدی قطعه کد زیر را ملاحظه می نمایید. به طور تجربی متوجه شدم که هنگامی که داده هایی که قصد داریم آن ها را ایندکس کنیم بسیار زیاد باشد، هنگام ایجاد ایندکس ها برنامه با خطایی روبرو می شد و از ادامه کار باز می ماند. با توجه به جسجوهایی که انجام دادم، متوجه شدم که با اضافه نمودن این خط مشکل حل می شود.

Lucene.Net.Store.FSDirectory.SetDisableLocks(true);

اما اکنون نوبت به مهمترین بخش می رسد. در این قسمت ما اطلاعات را از پایگاه داده بازیابی نموده و به شکل خاصی ذخیره نموده ایم.

List<News> newses = News.GetAllNews();
foreach (News news in newses)
{
    Document doc = new Document();
 
    doc.Add(new Field("NewsID",
                      news.NewsID.ToString(),
                      Field.Store.YES, Field.Index.NO, Field.TermVector.NO));
    doc.Add(new Field("WriterID", news.WriterID.ToString(), Field.Store.NO, Field.Index.NOT_ANALYZED,
                      Field.TermVector.NO));
    doc.Add(new Field("Title", news.Title, Field.Store.YES, Field.Index.ANALYZED,
                      Field.TermVector.NO));
 
    doc.Add(new Field("Body", news.Body, Field.Store.NO, Field.Index.ANALYZED,
                      Field.TermVector.NO));
 
    indexWriter.AddDocument(doc);
}

به منظور ذخیره نمودن اطلاعات باید داده ها را با استفاده از کلاس Field داخل کلاس Document جای دهیم و سپس با استفاده از indexWriter به قسمت ایندکس ها اضافه کنیم.

پارامتر اولی که به کلاس Field ارسال شده است نام فیلد جدول ما می باشد.

پارامتر دوم مقدار فیلد می باشد که باید به شکل متنی (string) ذخیره شود.

اما سه پارامتر بعدی نیاز به توضیح بیشتری دارند.

در ابتدای مقاله در قسمت "نکات مهم" یک دسته بندی از انواع داده ها هنگام جستجو انجام دادیم. در این قسمت باید مشخص کنیم که داده به چه شکلی باید در فایل های ایندکس ذخیره شود

  • اگر محتوای داده باید در هنگام ایندکس گذاری ذخیره شود و در نتیجه بتوانیم این محتوا را هنگام نمایش نتایج جستجو مورد استفاده قرار دهیم باید مقدار Field.Store.YES را به عنوان پارامتر سوم به کلاس Field ارسال کنیم و در غیر اینصورت مقدار Field.Store.NO را ارسال می کنیم.
    به طور مثال محتوای فیلد Title و NewID قرار است که هنگام نمایش اطلاعات مورد استفاده قرار گیرند پس باید محتوای آن ها ذخیره شود و در نتیجه مقدار Field.Store.YES را به ارسال می کنیم ولی محتوای فیلد Body و WriterID هنگام نمایش نتایج جستجو مورد نیاز ما نیست و در نتیجه نیازی به ذخیره محتوای آن ها نداریم و مقدار Field.Store.NO را به آن ها نسبت می دهیم.
    عملکرد Field.Store.COMPRESS نیز همانند Field.Store.YES می باشد با این تفاوت که هنگام ذخیره نمودن محتوا عملیات فشرده سازی نیز انجام می شود و بدیهی است که هنگام بازیابی نتایج باید داده ها از حالت فشرده بیرون بیایند که روی راندمان کار مقداری تاثیر خواهد گذاشت.
     

  • اگر قرار باشید فقط محتوای یک فیلد هنگام نمایش نتایج جستجو مورد استفاده قرار گیرد و در حقیقت در خود جستجو هیچ نقشی نداشته باشد، مقدار Field.Index.NO را به عنوان پارامتر چهارم ارسال می کنیم. مثلا ما قصد نداریم فیلد NewsID را در جستجو شرکت دهیم ولی نیاز است که هنگام نمایش نتایج جستجو حتما به آن دسترسی داشته باشیم و بنابراین مقدار Field.Index.NO را برای این فیلد ارسال می کنیم.

    اگر قرار باشد محتوای یک فیلد به شکل کلمه به کلمه (در حقیقت قسمتی از کل محتوای فیلد) مورد جستجو قرار گیرد باید مقدار Field.Index.ANALYZED را برای پارامتر چهارم ارسال کنیم. این موضوع در مورد فیلد های Title و Body صدق می کند.

    اما اگر نیاز باشد که محتوای یک فیلد به طور کامل مورد جستجو قرار گیرد از Field.Index.NOT_ANALYZED استفاده می کنیم. به طور مثال فیلد WriterID به شکل کامل مورد جستجو قرار می گیرد و یا اینکه اگر ما فیلدی برای نگهداری شماره دانشجویی داشتیم و قصد داشتیم جستجو بر اساس شماره دانشجویی نیز داشته باشیم می توانستیم از Field.Index.NOT_ANALYZED استفاده کنیم زیرا باید شماره دانشجویی به طور کامل وارد نمود و جستجو را انجام داد.
     

  • به عنوان پارامتر پنجم، ما باید پارامتر TermVector را ارسال نماییم. پارامتر TermVector مشخص کننده ی تعداد رخداد های عبارت جستجو شده در محتوای فیلد می باشد. ممکن است شما نیاز داشته باشید که خبرهایی را که مثلا عبارت "سلام" در آن ها بیشتر تکرار شده است را در هنگام نمایش نتایج جستجو در قسمت بالاتری نمایش دهید. در این گونه مواقع از این پارامتر استفاده می شود. مقدار پیشفرض آن Field.TermVector.NO می باشد و ما نیز استفاده ای از این فیلد ننموده ایم.

قسمت نهایی از تابع BuildIndexes خط زیر می باشد. پس از اینکه تمام Document ها را به indexWriter اضافه کردیم، فقط یکبار تابع Optimize را فراخوانی می کنیم تا پس از ساخته شدن ایندکس ها عملیات بهینه سازی روی آن ها انجام شود.

indexWriter.Optimize();

جستجو با استفاده از Lucene.net:

تاکنون نحوه ی ساخت ایندکس ها را مورد بررسی قرار داده ایم. اکنون به سراغ قسمت جستجو می رویم.

کتابخانه Lucene.net می تواند بر اساس الگوهای مختلفی عملیات جستجو را انجام دهد. از جمله این الگوها، جستجو بر اساس Wild Card ، جستجو بر اساس وجود چند عبارت و جستجو برای عین یک عبارت می باشد.

در تابع زیر ما قصد داریم عبارت مورد جستجو را به تابع SearchIndexes ارسال نماییم.

اگر عبارت مورد جستجوی ما دارای کاراکتر "*" بود، باید جستجو را بر اساس Wild Card انجام دهیم. به طور مثال اگر عبارت "عش*" جستجو شده باشد باید خبرهایی که حاوی کلمات "عشق" و "عشوه" می باشند را بازیابی نماییم. در این حالت از کلاس WildcardQuery استفاده خواهیم نمود.

اگر عبارت مورد جستجوی ما دارای فاصله خالی بین کلمات باشد (به طور مثال یک جمله باشد)، از کلاس MultiPhraseQuery استفاده خواهیم نمود. در این حالت خبرهایی بازیابی می شوند که حاوی تمامی کلمات عبارت جستجو باشند. در این حالت ترتیب مهم است زیرا ابتدا کلمه ی اول جستجو می شود و سپس در نتایج پیدا شده، کلمه دوم جستجو می شود و این عمل برای تمامی کلمات عبارت مورد جستجو تکرار می شود. ولی نیازی نیست که کلمات حتما پشت سر هم باشند و می توانند بین آن ها جملات دیگری نیز وجود داشته باشند.

در غیر اینصورت عین کلمه ی مورد جستجو را با استفاده از کلاس PhraseQuery مورد جستجو قرار می دهیم.

به پیاده سازی تابع SearchIndexes دقت نمایید.

private List<News> SearchIndexes(string InputText)
{
    List<News> results = new List<News>();
 
    Lucene.Net.Search.BooleanQuery.SetMaxClauseCount(int.MaxValue);
 
    IndexReader reader = IndexReader.Open(GetNewsIndexDirectory());
    IndexSearcher searcher = new IndexSearcher(reader);
    Hits hits = null;
 
    //are there any wild cards in use?
    if (InputText.Contains("*"))
    {
        WildcardQuery query = new WildcardQuery(new Term("Body", InputText));
 
        hits = searcher.Search(query);
 
    }
    //is this a multi term query?
    else if (InputText.Contains(" "))
    {
        MultiPhraseQuery query = new MultiPhraseQuery();
        foreach (string s in InputText.Split(' '))
        {
            query.Add(new Term("Body", s));
        }
        hits = searcher.Search(query);
    }
    //single term query
    else
    {
        PhraseQuery query = new PhraseQuery();
        query.Add(new Term("Body", InputText));
        hits = searcher.Search(query);
    }
 
    for (int i = 0; i < hits.Length(); i++)
    {
 
        Document doc = hits.Doc(i);
        News news = new News();
        news.NewsID = Convert.ToInt32(doc.GetField("NewsID").StringValue());
        news.Title = doc.GetField("Title").StringValue();
        results.Add(news);
    }
    return results;
}

در تابع بالا ما با استفاده از کلاس های IndexReader و IndexSearcher به انجام عملیات جستجو پرداخته ایم. عملکرد قطعه کد بالا واضح می باشد ولی ذکر توضیحاتی در اینباره مفید می باشد.

اگر تعداد نتایج پیدا شده برای جستجو بسیار زیاد باشد، هنگام عملیات جستجو با خطا مواجه می شویم . برای رفع این مشکل خط زیر را در تابع نوشته ایم و مشخص نموده ایم که حداکثر تعداد نتایج یافت شده توسط جستجو می تواند به تعداد حداکثر مقدار مورد قبول برای نوع داده Int32 باشد.

Lucene.Net.Search.BooleanQuery.SetMaxClauseCount(int.MaxValue);

نتیجه جستجو را داخل کلاسی به نام Hits قرار می گیرد. دقت کنید که کلاس Hits بسیار سبک و کم حجم می باشد و صرفا تعداد نتایج یافت شده و آدرس محل قرار گیری فیلد ها را نگهداری می کند.

برای بازیابی نتایج جستجو از قطعه کد زیر استفاده نموده ایم.

for (int i = 0; i < hits.Length(); i++)
{
    Document doc = hits.Doc(i);
    News news = new News();
    news.NewsID = Convert.ToInt32(doc.GetField("NewsID").StringValue());
    news.Title = doc.GetField("Title").StringValue();
    results.Add(news);
}

دقت کنید که ما فقط NewsID و Title را بازیابی نموده ایم و اگر قصد بازیابی سایر فیلدها را داشته باشید با خطا روبرو خواهید شد. دلیل این موضوع نیز این می باشد که هنگام ایجاد ایندکس ها مشخص نموده ایم که فقط محتوای این دو فیلد قابل بازیابی است. ( Field.Store.YES)

در مقالات بعدی به نکات مهمی جهت استفاده از کتابخانه Lucene.net در برنامه های تحت وب و جستجوی پیشرفته و بروزرسانی ایندکس ها و انجام عملیات Paging و غیره خواهم پرداخت.

ادامه مقالات:


سورس کامل انجام شده در این مقاله را می توانید از لینک بالای صفحه دریافت نمایید.

منابع :

        ASP.NET 3.5 Social Networking

                    CodeClimber

                StackOverflow

                  30sharp.com


برچسب های مرتبط