1. 程式人生 > >【Lucene】Apache Lucene全文檢索引擎架構之搜尋功能

【Lucene】Apache Lucene全文檢索引擎架構之搜尋功能

  上一節主要總結了一下Lucene是如何構建索引的,這一節簡單總結一下Lucene中的搜尋功能。主要分為幾個部分,對特定項的搜尋;查詢表示式QueryParser的使用;指定數字範圍內搜尋;指定字串開頭搜尋以及多條件查詢。

1. 對特定項的搜尋

  要使用Lucene的搜尋功能,首先得有索引,也就是說Lucene首先得針對特定的檔案生成特定的索引,然後我們才能搜尋,這在第一節裡描述的很清楚,那麼構建索引的例子也是使用第一節中的例子,在這就不再贅述了,然後生成了索引後,如何來搜尋呢?先看第一種搜尋方式:對特定項的搜尋。使用的檔案和建立的索引還是使用第一節的那些,生成好了索引後,就可以對特定項進行搜尋了。

public class SearchTest {

    private Directory dir;
    private IndexReader reader;
    private IndexSearcher search; 

    @Before
    public void setUp() throws Exception {

        dir = FSDirectory.open(Paths.get("D:\\lucene")); //索引的目錄在D:\\lucene
        reader = DirectoryReader.open(dir); //根據目錄獲取IndexReader
search = new IndexSearcher(reader); //根據IndexReader獲取IndexSearcher } @After public void tearDown() throws Exception { reader.close(); //關閉InderxReader } //對特定項進行搜尋 @Test public void testTermQuery() throws Exception { String searchField = "contents"; String q = "particular"
; Term term = new Term(searchField, q); Query query = new TermQuery(term); TopDocs hits = search.search(query, 10); System.out.println("匹配" + q + "總共查詢到" + hits.totalHits + "個文件"); for(ScoreDoc score : hits.scoreDocs) { Document doc = search.doc(score.doc); System.out.println(doc.get("fullPath")); } } }

  首先初始化IndexSearcher,在搜尋的時候,要在特定的Field中對特定的字串q進行搜尋,由上面的程式可知,我要在contents欄位中搜索particular這個字串。contents是在建立索引的時候建立的,包括程式最後一行中的fullPath欄位,都是在建立索引的時候建立的。有了Field和搜尋字串後,就可以生成一個搜尋項Term了,然後根據這個搜尋項建立一個搜尋。最後就可以搜尋出包含這個字串的檔案的路徑。
  這是針對特定項進行搜尋,為什麼叫針對特定項呢?因為如果我搜索particul,那麼結果就為0,也就是說我必須是針對具體的一個單詞,也就是說Lucene在建立索引的時候也是根據一個個單詞來的,如果我只搜尋單詞的一部分,那麼是搜不到的,所以這種針對特定項搜尋其實用的不多,因為在實際中,我如果搜尋particul的話,理論上應該能將particular搜出來才對。所以要用到查詢表示式QueryParser。

2. 使用查詢表示式QueryParser搜尋

首先來看一下如何使用這個QueryParser。

@Test
public void testQueryParser() throws Exception {
    Analyzer analyzer = new StandardAnalyzer(); //標準分詞器,會自動去掉空格啊,is a the等單詞
    String searchField = "contents";
    String q = "particular";    //OR AND particular~
    QueryParser parser = new QueryParser(searchField, analyzer); //查詢解析器
    Query query = parser.parse(q); //通過解析要查詢的String,獲取查詢物件
    TopDocs docs = search.search(query, 10);//開始查詢,查詢前10條資料,將記錄儲存在docs中
    System.out.println("匹配" + q + "總共查詢到" + docs.totalHits + "個文件");
    for(ScoreDoc scoreDoc : docs.scoreDocs) { //取出每條查詢結果
        Document doc = search.doc(scoreDoc.doc); //scoreDoc.doc相當於docID,根據這個docID來獲取文件
        System.out.println(doc.get("fullPath")); //fullPath是剛剛建立索引的時候我們定義的一個欄位
    }
}

  從程式中可以看出,初始化QueryParser需要傳入一個分詞器,這裡使用的是標準分詞器,然後跟上面一樣,得指定具體的Field和要查詢的字串。這看起來好像和上面根據特定項來搜尋沒什麼兩樣,其實不然,使用QueryParser的好處就在於初始化查詢字串q的時候,是有語法的,程式中只是簡單的查詢一個particular單詞而已。
  如果我將q改成”particular OR Unicode”,那麼Lucene就會查詢出所有包含particular或Unicode(不區分大小寫)的文件,這裡的OR也可以省略不寫。同樣的,如果我把OR改成AND,那麼就是查詢出所有包含particular且包含Unicode的文件。那麼如果我要類似於上面提到的模糊查詢呢?比如我輸入particul想查出particular咋整呢?可以將q定義為“particul~”,這樣就OK了。實際中用的比較多的是這個QueryParser,這一塊更多的內容可以看一下官方文件。

3. 指定數字範圍搜尋

  這個主要用於某個欄位是int型的,然後可以根據這個欄位來搜尋,可以搜尋某兩個int值範圍內的所有項。為了模擬這個場景,我使用上一節的例子來建立索引,因為裡面有id,將其修改為Integer型別即可。然後看下如何指定數字範圍內搜尋。

@Test
public void testNumericRangeQuery() throws Exception {
    NumericRangeQuery<Integer> query = NumericRangeQuery.newIntRange("id", 1, 2, true, true);
    TopDocs hits = search.search(query, 10);
    System.out.println("總共查詢到" + hits.totalHits + "個文件");
    for (ScoreDoc score : hits.scoreDocs) {
        Document doc = search.doc(score.doc);
        System.out.println(doc.get("id"));
        System.out.println(doc.get("city"));
        System.out.println(doc.get("desc"));
    }
}

  首先得要建立一個NumericRangeQuery物件,初始化的時候第一個引數是欄位名,第二個和第三個引數是始末數,後面兩個是包含大小寫,一般都設定為true,後面就跟之前的查詢一樣了。上面的程式可以查詢到兩個記錄。

4. 指定字串開頭搜尋

  這個和上面的數字範圍內搜尋有點類似,只不過搜尋的條件不同,初始化也不同,指定字串開頭搜尋的話需要先建立一個PrefixQuery物件,將要搜尋的欄位和開頭的字串傳進去,然後再搜尋。如下搜尋city中以s開頭的所有項。

@Test
public void testPrefixQuery() throws Exception {
    PrefixQuery query = new PrefixQuery(new Term("city", "s"));
    TopDocs hits = search.search(query, 10);
    System.out.println("總共查詢到" + hits.totalHits + "個文件");
    for (ScoreDoc score : hits.scoreDocs) {
        Document doc = search.doc(score.doc);
        System.out.println(doc.get("id"));
        System.out.println(doc.get("city"));
        System.out.println(doc.get("desc"));
    }
}

5. 多條件查詢(組合查詢)

  多條件查詢又稱為組合查詢,顧名思義,就是將多個查詢條件組合到一起進行查詢,這個就比較厲害了。比如我現在想組合上面兩個查詢,首先id為1到2之間,然後city又是s開頭的,可以這麼做:

@Test
public void testBooleanQuery() throws Exception {
    NumericRangeQuery<Integer> query1 = NumericRangeQuery.newIntRange("id", 1, 2, true, true);
    PrefixQuery query2 = new PrefixQuery(new Term("city", "s"));
    BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();

    booleanQuery.add(query1, BooleanClause.Occur.MUST);
    booleanQuery.add(query2, BooleanClause.Occur.MUST);

    TopDocs hits = search.search(booleanQuery.build(), 10);
    System.out.println("總共查詢到" + hits.totalHits + "個文件");
    for (ScoreDoc score : hits.scoreDocs) {
        Document doc = search.doc(score.doc);
        System.out.println(doc.get("id"));
        System.out.println(doc.get("city"));
        System.out.println(doc.get("desc"));
    }
}

  組合查詢使用的是BooleanQuery,然後組合的條件還是上面的那些條件,這些條件中原來該使用什麼類初始化還是使用那些類初始化,只是往BooleanQuery中加就行了。這很方便,一般查詢條件多的時候,就可以採用這種組合的查詢方式來查詢。

文末福利:“程式設計師私房菜”,一個有溫度的公眾號~
程式設計師私房菜