۱۳۹۱/۰۱/۲۶

معرفی و آموزش پیاده‌سازی Apache Lucene

سلام،
Apache Lucene
Apache Lucene
حدود یک سال و نیم قبل در مطلبی به معرفی خزنده‌های وب (Web crawlers) و آموزش راه اندازی یک خزنده وب ساده به زبان جاوا پرداختم. در طول این مدت افراد بسیاری در مورد خزنده‌های وب با من تماس گرفتند و سوالات مختلفی در مورد این خزنده‌ها داشتند. در این مطلب به معرفی و آموزش Apache Lucene که به منظور جستجو فایل‌های متین استفاده می شود، می پردازم. همچنین در آینده به بررسی ۳ محصول Hadoop, Apache Solr و Apache Nutch خواهم پرداخت.

Apache Lucene


آپاچی لوسن کتابخانه ای برای راه اندازی موتورهای جستجوی متن می باشد. این کتابخانه آزاد بوده و تحت لایسنس Apache Licene 2.0 منتشر می شود. این کتابخانه به زبان جاوا (Java) نوشته شده و سپس به زبان های Delphi, Perl, C#, C++, Python, Ruby, و PHP پیاده سازی (پورت) شده است.
این کتابخانه این امکان را برای شما فراهم می آورد تا هر نوع موتور جستجوی متنی مانند موتور جستوی وب، لوکال (محلی) ویا فقط ویژه یک وب سایت را ایجاد نمایید. در نظر داشته باشید که Apache Lucene فقط برای جستجو استفاده می‌شود و برای جمع‌آوری اطلاعات و تحلیل آن‌ها نیاز به ابزارهای دیگری مانند Solr و Nutch دارید.
برای دریافت این کتابخانه به صفحه دانلود آپاچی لوسن مراجعه نمایید. همچنین می توانید نسخه فعلی (۳.۶.۰) را از آدرس زیر دریافت نمایید:
دانلود کتابخانه آپاچی لوسن نسخه ۳.۶.۰

استفاده از کتابخانه Apache Lucene


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

مرحله اول: ایندکس کردن اطلاعات


قبل از هر چیز شما باید اطلاعاتی را که قصد جستجو در آن‌ها را دارید ایندکس نمایید. ایندکس کردن اطلاعات فواید زیادی دارد که یکی از مهم‌ترین آن‌ها مرتب‌سازی و افزایش سرعت جستجو است.
ایندکس اطلاعات در لوسن توسط دو کلاس Document و Field صورت می پذیرد. Document سند شما و Field اطلاعات مرتبط با سند مانند عنوان، محتوا و... است. این وظیفه شماست که اطلاعات خود از جمله رشته‌ها، انواع فایل‌ها، اطلاعات ذخیره شده در پایگاه داده و... را به کلاس Document تبدیل و ایندکس نمایید. در زیر تعامل Index, Document و Field را مشاهده می کنید:



 Index 

Document 1
Field A (name/value)

Field B (name/value)



Document 2
Field A (name/value)

Field B (name/value)




بعد از آماده سازی Document باید توسط کلاس IndexWriter، ایندکس را ذخیره می کنیم. پارامتر اول در فراخوانی IndexWriter دایکتوری ذخیره‌سازی ایندکس را مشاهده می‌کند.
با کدهای زیر چند عبارت را در حافظه دسترسی تصادفی (RAM) ذخیره کردیم:
Directory index = new RAMDirectory();
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_36, analyzer);

IndexWriter w = new IndexWriter(index, config);
addDoc(w, "Lucene in Action");
addDoc(w, "Lucene for Dummies");
addDoc(w, "Managing Gigabytes");
addDoc(w, "The Art of Computer Science");
w.close();
متد addDoc رشته‌ها را به ایندکس اضافه می کند:
private static void addDoc(IndexWriter w, String value) throws IOException {
Document doc = new Document();
doc.add(new Field("title", value, Field.Store.YES, Field.Index.ANALYZED));
w.addDocument(doc);
}
}

مرحله دوم: ایجاد کوئری (Query)


در این برنامه ما عبارت مورد نظر برای جستجو را از ورودی (stdin) دریافت می‌کنیم:
String querystr = args.length > 0 ? args[0] : "lucene";
Query q = new QueryParser(Version.LUCENE_36, "title", analyzer).parse(querystr);

مرحله سوم: انجام جستجو


در این مرحله کوئری (جستار؟) ساخته شده را بر روی ایندکس جستجو می کنیم. همچنین از کلاس TopScoreDocCollector برای بدست آوردن ۱۰ نتیجه مرتبط‌تر استفاده کرده ایم:
int hitsPerPage = 10;
IndexReader reader = IndexReader.open(index);
IndexSearcher searcher = new IndexSearcher(reader);
TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true);
searcher.search(q, collector);
ScoreDoc[] hits = collector.topDocs().scoreDocs;

مرحله چهارم: مشاهده نتایج


در انتها نتایج بدست آمده را نمایش می‌دهیم:
System.out.println("Found " + hits.length + " hits.");
for(int i=0;i < hits.length;++i) {
int docId = hits[i].doc;
Document d = searcher.doc(docId);
System.out.println((i + 1) + ". " + d.get("title"));
}
همین!

کلاس HelloLucene


در زیر کلاس HelloLucene را یکجا مشاهده می فرمایید:
import java.io.IOException;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;

public class HelloLucene {
public static void main(String[] args) throws IOException, ParseException {
// 0. Specify the analyzer for tokenizing text.
//    The same analyzer should be used for indexing and searching
StandardAnalyzer analyzer = new StandardAnalyzer(Version.LUCENE_36);

// 1. create the index
Directory index = new RAMDirectory();

IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_36, analyzer);

IndexWriter w = new IndexWriter(index, config);
addDoc(w, "Lucene in Action");
addDoc(w, "Lucene for Dummies");
addDoc(w, "Managing Gigabytes");
addDoc(w, "The Art of Computer Science");
w.close();

// 2. query
String queryStr = args.length > 0 ? args[0] : "lucene";

// the "title" arg specifies the default field to use
// when no field is explicitly specified in the query.
Query q = new QueryParser(Version.LUCENE_35, "title", analyzer).parse(queryStr);

// 3. search
int hitsPerPage = 10;
IndexReader reader = IndexReader.open(index);
IndexSearcher searcher = new IndexSearcher(reader);
TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage, true);
searcher.search(q, collector);
ScoreDoc[] hits = collector.topDocs().scoreDocs;

// 4. display results
System.out.println("Found " + hits.length + " hits.");
for (int i = 0; i < hits.length; ++i) {
int docId = hits[i].doc;
Document d = searcher.doc(docId);
System.out.println((i + 1) + ". " + d.get("title"));
}

// searcher can only be closed when there

// is no need to access the documents any more.

searcher.close();
}

private static void addDoc(IndexWriter w, String value) throws IOException {
Document doc = new Document();
doc.add(new Field("title", value, Field.Store.YES, Field.Index.ANALYZED));
w.addDocument(doc);
}
}

منابع


شاد و پیروز باشید :)

۸ نظر:

  1. ممنون از سایت خوبتون و هم چنین مقاله بالا
    من نیاز فوری به مطلبی درباره Apache Nutch دارم. مطلبی فارسی در این زمینه مد نظر دارید که بتواند به من کمک بکند؟
    با تشکر

    پاسخحذف
  2. سلام،

    متاسفانه من مطلبی به زبان فارسی پیدا نکردم. بزودی خودم مطلبی در مورد Apache Nutch خواهم نوشت.

    پاسخحذف
  3. سلام.
    من برنامه‌نویس پی‌اچ‌پیم و آشنایی نسبی هم با سی++ دارم. الان دارم روی یه کراولر کار میکنم و اونو تا به حال با پی‌اچ‌پی نوشتم، ولی اونطور که میخوام از تمام توان سیستم استفاده نمیکنه، به نظرم اومد که شاید به خاطر زبان برنامه‌نویسی باشه، به نظرتون این حس من درسته؟ (اینکه پی‌اچ‌پی به درد این موضوع نمیخوره) ارزش داره که بشینم با سی++ اون بازنویسی کنم؟ (با توجه به بازآشنا شدن با سی++).

    پاسخحذف
  4. سلام،

    به نظر من اگر به برنامه نویسی C++ مسلط هستید، از اون استفاده کنید. پی اچ پی خیلی خوبه ولی برای اینکار پیشنهاد من سی ویا جاوا است.

    پاسخحذف
  5. طراحی شاخص معکوس بزرگ:
    باسلام
    قصد دارم یک شاخص معکوس از روی حجم زیادی از اطلاعات (در حد 1میلیارد سند باحجم تقریباً 500گیگابایت) بسازم که قابل گسترش و البته بهینه باشد.
    از لیست زیر کدوم یکی:
    Hadoop Map-Reduce
    NoSQL Cassandra
    Apache Lucene
    Sphinx
    Trrier
    Stupa
    نی‌تونم به تلفیقی ازشون فکر کنم؟ کدومشون بصورت کتابخانه‌ای برا دات‌نت هستن ؟ اگه بتونم با C++ بنویسمش خیلی خوبه..
    لطفا مـــــــرا یاری کنید!
    مرسی آقا سعید گل
    سپاس فراوان

    پاسخحذف
  6. سلام،

    متاسفانه اطلاعات من در این زمینه محدود است و نمی‌توانم شما را به طور صحیح راهنمایی نمایم.

    پاسخحذف