【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中加就行了。這很方便,一般查詢條件多的時候,就可以採用這種組合的查詢方式來查詢。