kingpub

          海內存知己,博客若比鄰

           

          lucene簡單例子

          lucene的組成結構:對于外部應用來說索引模塊(index)和檢索模塊(search)是主要的外部應用入口

          org.apache.Lucene.search/ 搜索入口
          org.apache.Lucene.index/ 索引入口
          org.apache.Lucene.analysis/ 語言分析器
          org.apache.Lucene.queryParser/ 查詢分析器
          org.apache.Lucene.document/ 存儲結構
          org.apache.Lucene.store/? 底層IO/存儲結構
          org.apache.Lucene.util/ 一些公用的數據結構

          簡單的例子演示一下Lucene的使用方法:

          索引過程:從命令行讀取文件名(多個),將文件分路徑(path字段)和內容(body字段)2個字段進行存儲,并對內容進行全文索引:索引的單位是Document對象,每個Document對象包含多個字段Field對象,針對不同的字段屬性和數據輸出的需求,對字段還可以選擇不同的索引/存儲字段規則,列表如下:

          方法 切詞 索引 存儲 用途
          Field.Text(String name, String value) Yes Yes Yes 切分詞索引并存儲,比如:標題,內容字段
          Field.Text(String name, Reader value) Yes Yes No 切分詞索引不存儲,比如:META信息,
          不用于返回顯示,但需要進行檢索內容
          Field.Keyword(String name, String value) No Yes Yes 不切分索引并存儲,比如:日期字段
          Field.UnIndexed(String name, String value) No No Yes 不索引,只存儲,比如:文件路徑
          Field.UnStored(String name, String value) Yes Yes No 只全文索引,不存儲
          public class IndexFiles { 
          //使用方法:: IndexFiles [索引輸出目錄] [索引的文件列表] ...
          public static void main(String[] args) throws Exception {
          String indexPath = args[0];
          IndexWriter writer;
          //用指定的語言分析器構造一個新的寫索引器(第3個參數表示是否為追加索引)
          writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);

          for (int i=1; i System.out.println("Indexing file " + args[i]);
          InputStream is = new FileInputStream(args[i]);

          //構造包含2個字段Field的Document對象
          //一個是路徑path字段,不索引,只存儲
          //一個是內容body字段,進行全文索引,并存儲
          Document doc = new Document();
          doc.add(Field.UnIndexed("path", args[i]));
          doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));
          //將文檔寫入索引
          writer.addDocument(doc);
          is.close();
          };
          //關閉寫索引器
          writer.close();
          }
          }
          具體的子類只需要提供一個返回getName()方法的實現,當在Spring上下文中定義MessageProcessor接口時,該方法返回接口特定實現的bean名字。子類也可以提供自己的MessageConverter,使用不同的策略填充MessageData。以下是MessageProcessor的一個簡單實現:
          public class SimpleMessageProcessor implements MessageProcessor {  private Log log = LogFactory.getLog(getClass());  public Object process( MessageData messageData) {    log.info(messageData);    return null;  }}

            最后,具體的MDB看起來如下所示。注意,我們使用Xdoclet注釋來聲明部署描述符的元數據:

          /** * SimpleMdb *  * .bean  *   name="org.javatx.mdb.SimpleMdb"  *   type="MDB" *   destination-type="javax.jms.Queue"  *   transaction-type="Bean" *  * .pool  *   initial-beans-in-free-pool= *                ""  *   max-beans-in-free-pool= *                "" *  * .message-driven  *   connection-factory-jndi-name= *                  "-e" *   destination-jndi-name= *                  "-e" *   jms-polling-interval-seconds= *                  "" *    * .env-entry *   name="BeanFactoryPath"  *   value="applicationContext.xml" */public class SimpleMdb extends MessageDataDrivenBean {  protected String getName() {    return "simpleProcessor";  }  }

            在上述代碼中,BeanFactoryPath的env-entry被Spring的EJB類用來定位應用程序上下文。應用程序上下文中應該有simpleProcessor bean的聲明,這個bean會處理所有的處理邏輯,以及非功能性的需求,比如:事務、防止消息的重復處理以及可選的跟蹤和性能監控。

            顯然,將所有非功能方面移到通知中,并利用包裝了MessageProcessor實際實現的ProxyFactoryBean來定義攔截器鏈是很有意義的。定義可能如下所示:

            圖1中的順序圖說明了消息處理過程以及支持該服務質量模型所需的advisor堆棧:


          圖 1.處理傳入消息的advisor堆棧(單擊圖像查看大圖)

            實現消息攔截器

            現在我們來仔細看一下mdbTransactionInterceptor和mdbDuplicateHandlingAdvisor,它們使用上述方法提供了保證服務質量所需的功能。

            mdbTransactionAdvisor是利用標準的Spring TransactionInterceptor以及process()方法的PROPAGATION_REQUIRES_NEW事務屬性定義的。

          PROPAGATION_REQUIRES_NEW,timeout_300      

            在WebLogic Server中,可以將Spring包裝器用作服務器JNDI中javax.transaction.UserTransaction所公開的平臺事務管理器,并定義應用程序上下文如下:

            鏈中的下一個通知是mdbDuplicateHandlingAdvisor。因為它還要將一些獨有鍵保存到數據庫表中,所以需要一個數據源:

          更通用的輸入輸出接口

          雖然lucene沒有定義一個確定的輸入文檔格式,但越來越多的人想到使用一個標準的中間格式作為Lucene的數據導入接口,然后其他數據,比如PDF只需要通過解析器轉換成標準的中間格式就可以進行數據索引了。這個中間格式主要以XML為主,類似實現已經不下4,5個:

          數據源: WORD       PDF     HTML    DB       other
          \ | | | /
          XML中間格式
          |
          Lucene INDEX

          目前還沒有針對MSWord文檔的解析器,因為Word文檔和基于ASCII的RTF文檔不同,需要使用COM對象機制解析。這個是我在Google上查的相關資料:http://www.intrinsyc.com/products/enterprise_applications.asp
          另外一個辦法就是把Word文檔轉換成text:http://www.winfield.demon.nl/index.html


          索引過程優化

          索引一般分2種情況,一種是小批量的索引擴展,一種是大批量的索引重建。在索引過程中,并不是每次新的DOC加入進去索引都重新進行一次索引文件的寫入操作(文件I/O是一件非常消耗資源的事情)。

          Lucene先在內存中進行索引操作,并根據一定的批量進行文件的寫入。這個批次的間隔越大,文件的寫入次數越少,但占用內存會很多。反之占用內存少,但文件IO操作頻繁,索引速度會很慢。在IndexWriter中有一個MERGE_FACTOR參數可以幫助你在構造索引器后根據應用環境的情況充分利用內存減少文件的操作。根據我的使用經驗:缺省Indexer是每20條記錄索引后寫入一次,每將MERGE_FACTOR增加50倍,索引速度可以提高1倍左右。

          搜索過程優化

          lucene支持內存索引:這樣的搜索比基于文件的I/O有數量級的速度提升。
          http://www.onjava.com/lpt/a/3273
          而盡可能減少IndexSearcher的創建和對搜索結果的前臺的緩存也是必要的。

          Lucene面向全文檢索的優化在于首次索引檢索后,并不把所有的記錄(Document)具體內容讀取出來,而起只將所有結果中匹配度最高的頭100條結果(TopDocs)的ID放到結果集緩存中并返回,這里可以比較一下數據庫檢索:如果是一個10,000條的數據庫檢索結果集,數據庫是一定要把所有記錄內容都取得以后再開始返回給應用結果集的。所以即使檢索匹配總數很多,Lucene的結果集占用的內存空間也不會很多。對于一般的模糊檢索應用是用不到這么多的結果的,頭100條已經可以滿足90%以上的檢索需求。

          如果首批緩存結果數用完后還要讀取更后面的結果時Searcher會再次檢索并生成一個上次的搜索緩存數大1倍的緩存,并再重新向后抓取。所以如果構造一個Searcher去查1-120條結果,Searcher其實是進行了2次搜索過程:頭100條取完后,緩存結果用完,Searcher重新檢索再構造一個200條的結果緩存,依此類推,400條緩存,800條緩存。由于每次Searcher對象消失后,這些緩存也訪問那不到了,你有可能想將結果記錄緩存下來,緩存數盡量保證在100以下以充分利用首次的結果緩存,不讓Lucene浪費多次檢索,而且可以分級進行結果緩存。

          Lucene的另外一個特點是在收集結果的過程中將匹配度低的結果自動過濾掉了。這也是和數據庫應用需要將搜索的結果全部返回不同之處。

          我的一些嘗試

          • 支持中文的Tokenizer:這里有2個版本,一個是通過JavaCC生成的,對CJK部分按一個字符一個TOKEN索引,另外一個是從SimpleTokenizer改寫的,對英文支持數字和字母TOKEN,對中文按迭代索引。
          • 基于XML數據源的索引器:XMLIndexer,因此所有數據源只要能夠按照DTD轉換成指定的XML,就可以用XMLIndxer進行索引了。
          • 根據某個字段排序:按記錄索引順序排序結果的搜索器:IndexOrderSearcher,因此如果需要讓搜索結果根據某個字段排序,可以讓數據源先按某個字段排好序(比如:PriceField),這樣索引后,然后在利用這個按記錄的ID順序檢索的搜索器,結果就是相當于是那個字段排序的結果了。

          從Lucene學到更多

          Luene的確是一個面對對象設計的典范

          • 所有的問題都通過一個額外抽象層來方便以后的擴展和重用:你可以通過重新實現來達到自己的目的,而對其他模塊而不需要;
          • 簡單的應用入口Searcher, Indexer,并調用底層一系列組件協同的完成搜索任務;
          • 所有的對象的任務都非常專一:比如搜索過程:QueryParser分析將查詢語句轉換成一系列的精確查詢的組合(Query),通過底層的索引讀取結構IndexReader進行索引的讀取,并用相應的打分器給搜索結果進行打分/排序等。所有的功能模塊原子化程度非常高,因此可以通過重新實現而不需要修改其他模塊。?
          • 除了靈活的應用接口設計,Lucene還提供了一些適合大多數應用的語言分析器實現(SimpleAnalyser,StandardAnalyser),這也是新用戶能夠很快上手的重要原因之一。

          這些優點都是非常值得在以后的開發中學習借鑒的。作為一個通用工具包,Lunece的確給予了需要將全文檢索功能嵌入到應用中的開發者很多的便利。

          此外,通過對Lucene的學習和使用,我也更深刻地理解了為什么很多數據庫優化設計中要求,比如:

          • 盡可能對字段進行索引來提高查詢速度,但過多的索引會對數據庫表的更新操作變慢,而對結果過多的排序條件,實際上往往也是性能的殺手之一。
          • 很多商業數據庫對大批量的數據插入操作會提供一些優化參數,這個作用和索引器的merge_factor的作用是類似的,
          • 20%/80%原則:查的結果多并不等于質量好,尤其對于返回結果集很大,如何優化這頭幾十條結果的質量往往才是最重要的。
          • 盡可能讓應用從數據庫中獲得比較小的結果集,因為即使對于大型數據庫,對結果集的隨機訪問也是一個非常消耗資源的

          posted on 2006-07-23 19:01 xiaofeng 閱讀(1166) 評論(0)  編輯  收藏 所屬分類: weblucene

          導航

          統計

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          收藏夾

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 焦作市| 宁都县| 河南省| 色达县| 萨嘎县| 如皋市| 横山县| 尤溪县| 剑川县| 五大连池市| 河间市| 灵宝市| 富源县| 台湾省| 和政县| 永靖县| 苗栗市| 西和县| 年辖:市辖区| 徐州市| 辽阳县| 巩留县| 新竹市| 万宁市| 平和县| 玛纳斯县| 连山| 沙洋县| 温宿县| 上犹县| 平安县| 丰城市| 泾源县| 望城县| 丰原市| 无极县| 西华县| 江陵县| 武宣县| 内江市| 靖远县|