Lucene in Action-爲應用程序添加搜索功能

QueryParser 類的使用java

    • matchVersion 值的是 版本(爲了兼容)
    • 解析表達式
    • 不成功拋出 ParseException
    • 多個項時,默認是OR

IndexSearcher數據庫

  • IndexReader 打開索引文件、提供底層redaer API 的繁重工做
    • 須要較大系統開銷
    • 在搜索其間最好打開一個你們公用就好
    • 須要限制其打開的頻率(能夠相似 數據庫 connection處理)
      • reopen 消耗較少的系統資源
      • 若是索引變動,會返回新的reader ,和舊的不同 
        • 此時依然有不少程序在使用舊的reader ,請保證線程安全
        • 以下所示,若是不同,想看到新的索引,須要建立新的IndexSearcher

實現搜索功能數組

  • search() 方法
    • 其餘高級搜索包括過濾和排序

使用TopDocs 類緩存

  • TopDocs.totalHits
  • TopDocs.scoreDocs:匹配的頂部文檔數組

搜索結果分頁安全

  • 分頁兩種實現:
      • 從新查詢每每更好,Lucene的高性能查詢彌補了其浪費資源的短板
        • 操做系統的I/O緩存機制,使得從新查詢很快結束

近實時搜索性能

  • 沒必要關閉writer、再向該writer 提交
  • 長期打開一個IndexWriter 完成持續變動
    • 容許對新建立的、還未提交的段進行搜索
  • public class NearRealTimeTest extends TestCase {
      public void testNearRealTime() throws Exception {
        Directory dir = new RAMDirectory();
        IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(Version.LUCENE_30), IndexWriter.MaxFieldLength.UNLIMITED);
        for(int i=0;i<10;i++) {
          Document doc = new Document();
          doc.add(new Field("id", ""+i, Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
          doc.add(new Field("text", "aaa", Field.Store.NO, Field.Index.ANALYZED));
          writer.addDocument(doc);
        }
       // 會將緩存中全部變動,刷新到索引目錄,返回一個包含這些變動的新的reader
        IndexReader reader = writer.getReader();                 // #1  建立近實時 reader
    
        IndexSearcher searcher = new IndexSearcher(reader);      // #A
    
        Query query = new TermQuery(new Term("text", "aaa"));
        TopDocs docs = searcher.search(query, 1);
        assertEquals(10, docs.totalHits);                        // #B
    
        writer.deleteDocuments(new Term("id", "7"));             // #2
    
        Document doc = new Document();                           // #3
        doc.add(new Field("id",                                  // #3
                          "11",                                  // #3
                          Field.Store.NO,                        // #3
                          Field.Index.NOT_ANALYZED_NO_NORMS));   // #3
        doc.add(new Field("text",                                // #3
                          "bbb",                                 // #3
                          Field.Store.NO,                        // #3
                          Field.Index.ANALYZED));                // #3
        writer.addDocument(doc);                                 // #3
        
        // reopen 完成更多變動,效率很高:
                                  // 只會打開上次open、reopen的文件
                                  // 未發生任何改變的被共享(共享先前的reader)                         
        IndexReader newReader = reader.reopen();                 // #4
        assertFalse(reader == newReader);                        // #5
        reader.close();                                          // #6
        searcher = new IndexSearcher(newReader);              
    
        TopDocs hits = searcher.search(query, 10);               // #7
        assertEquals(9, hits.totalHits);                         // #7
    
        query = new TermQuery(new Term("text", "bbb"));          // #8
        hits = searcher.search(query, 1);                        // #8
        assertEquals(1, hits.totalHits);                         // #8
    
        newReader.close();
        writer.close();
      }
    }
    
    /*
      #1 Create near-real-time reader
      #A Wrap reader in IndexSearcher
      #B Search returns 10 hits
      #2 Delete 1 document
      #3 Add 1 document
      #4 Reopen reader
      #5 Confirm reader is new
      #6 Close old reader
      #7 Verify 9 hits now
      #8 Confirm new document matched
    */

理解Lucene的 評分機制優化

  • 評分公式(詳見以前博客的推導公式):

explain() 方法理解搜索結果評分:this

  • 開銷和查詢是同樣的,不要過分使用
  • public class Explainer {
      public static void main(String[] args) throws Exception {
        if (args.length != 2) {
          System.err.println("Usage: Explainer <index dir> <query>");
          System.exit(1);
        }
    
        String indexDir = args[0];
        String queryExpression = args[1];
    
        Directory directory = FSDirectory.open(new File(indexDir));
        QueryParser parser = new QueryParser(Version.LUCENE_30,
                                             "contents", new SimpleAnalyzer());
        Query query = parser.parse(queryExpression);
    
        System.out.println("Query: " + queryExpression);
    
        IndexSearcher searcher = new IndexSearcher(directory);
        TopDocs topDocs = searcher.search(query, 10);
    
        for (ScoreDoc match : topDocs.scoreDocs) {
          Explanation explanation
             = searcher.explain(query, match.doc);     //#A
    
          System.out.println("----------");
          Document doc = searcher.doc(match.doc);
          System.out.println(doc.get("title"));
          System.out.println(explanation.toString());  //#B
        }
        searcher.close();
        directory.close();
      }
    }

     

Lucene 多樣化查詢:操作系統

  • 詳見以前博客內容

解析查詢表達式:QueryParser線程

  • 須要轉義的字符:
  • Query.toString()
    • 查看 QueryParser 對象解析後的樣子

TermQuery

    • 將 查詢自動解析爲 輸出的樣子

項範圍查詢:

  • [] 邊界值包含在內,{} 不包含在內

通配符查詢僅僅在末尾有一個*

  • 會被優化爲前綴查詢
  • 兩者都會轉化大小寫,不過能人爲設置不轉化

布爾查詢::

  • AND OR NOT 必須所有大寫
  • 默認是OR (a  b   默認認爲是 a OR b)
  • 可修改默認:
  • NOT 不能單獨使用

短語查詢:

  • 雙引號括起來的項,轉化爲短語查詢
    • 「this is tom cat*」  
      • 分析結果 tom  cat   
        • 注意 前倆是停用詞
        • 最後的* 在雙引號內不考慮
      • 不設置slop 默認值爲0
    • SpanNearQuery 確保按照順序
    • PhraseQuery 鬆散的,不必定按照順序

模糊查詢:

  • ~0.7 指定最小類似 程度

MatchAllDocsQuery

  • *:*   會被解析爲 MatchAllDocsQuery

分組查詢:

  • 小括號,來創建子查詢

域選擇:

  • 要求用戶知道被搜索的域是不太友善的
    • 默認的域在建立QueryParser 時建立
    • 也能夠指定默認域爲全部域
      • title:lucene 限定查詢域爲title
      • filed (a b c)
        • filed:a OR filed:b OR filed:c

爲子查詢加權

  • junit 加權爲 2.0   testing 1.0

是否必定使用QueryParser

  • QueryParser 提供了快捷強大的查詢構建
    • 不能適合全部的場合
相關文章
相關標籤/搜索