針對lucene全文檢索索引引擎寫一下多種查詢檢索的方式
這裡採用的是英文分詞,單獨寫了一篇關於中文分詞與高亮顯示,如下:
lucene全文檢索索引之 中文分詞 以及 高亮顯示,點擊進入
另外分享一個 lucene索引庫查看器 ,點擊進入
lucene官網 http://lucene.apache.org這裡寫連結內容
首先需要引入lucene的maven依賴,如下:
<dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>5.3.1</version></dependency><dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>5.3.1</version></dependency><dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers-common</artifactId> <version>5.3.1</version></dependency>
下面我就直接貼代碼吧,如下:
package luceneDemo;import java.io.IOException;import java.nio.file.Paths;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import org.apache.lucene.analysis.Analyzer;import org.apache.lucene.analysis.standard.StandardAnalyzer;import org.apache.lucene.document.Document;import org.apache.lucene.document.Field;import org.apache.lucene.document.IntField;import org.apache.lucene.document.TextField;import org.apache.lucene.index.DirectoryReader;import org.apache.lucene.index.IndexReader;import org.apache.lucene.index.IndexWriter;import org.apache.lucene.index.IndexWriterConfig;import org.apache.lucene.index.Term;import org.apache.lucene.queryparser.classic.QueryParser;import org.apache.lucene.search.IndexSearcher;import org.apache.lucene.search.Query;import org.apache.lucene.search.ScoreDoc;import org.apache.lucene.search.TermQuery;import org.apache.lucene.search.TopDocs;import org.apache.lucene.store.Directory;import org.apache.lucene.store.FSDirectory;import org.apache.lucene.search.TermRangeQuery;import org.apache.lucene.util.BytesRef;import org.apache.lucene.search.NumericRangeQuery;import org.apache.lucene.search.PrefixQuery;import org.apache.lucene.search.BooleanClause;import org.apache.lucene.search.BooleanQuery;/** * 全文檢索索引案例 * @author mszhou */public class luceneDemo { /** * 示範將資料寫入索引檔案中 * 在項目中我們會定時將資料寫入索引檔案中, * 然後在索引檔案中進行搜尋,這樣比在資料庫中搜尋效率高的多, * 而且檢索遠比資料庫的模糊比對強大的多,這也是檢索工具牛逼之處 * * 需要注意的是,如果在項目中定時寫入索引, * 需要判斷下該資料是否存在,存在則修改,不存在再進行新增 * * @param luceneDir 該參數表示索引所存放的目錄 */ public static void writeIndex(String luceneDir){ IndexWriter writer = null;//聲明建立索引寫入器 try{ //建立索引目錄 , 這裡參數luceneDir 表示索引存放目錄 Directory dir=FSDirectory.open(Paths.get(luceneDir)); //建立標準分詞器(一般英文使用這個,不適合中文使用) Analyzer analyzer=new StandardAnalyzer(); // 標準分詞器 IndexWriterConfig iwc=new IndexWriterConfig(analyzer); //建立索引寫入器執行個體,通過IndexWriterConfig設定相關配置 writer=new IndexWriter(dir, iwc); //=== 這裡造一些資料用作示範,也可以直接讀取資料庫中的資料 開始======== List <Map<String,String>> list=new ArrayList<Map<String,String>>(); //假資料1 Map<String,String> map1 =new HashMap<String, String>(); map1.put("title", "weejij dasa ");//表示標題欄位 map1.put("centent", "hdaiads sd s g r44e r hgg f hd 4 ");//表示標題欄位 list.add(map1); //假資料2 Map<String,String> map2 =new HashMap<String, String>(); map2.put("title", "demo12 i33s ss");//表示標題欄位 map2.put("centent", "hdaiads sd s g r44e r hgg f hd 4 ");//表示標題欄位 list.add(map2); //假資料1 Map<String,String> map3 =new HashMap<String, String>(); map3.put("title", "demo 12i131s ss");//表示標題欄位 map3.put("centent", "hdaiads sd s g r44e r hgg f hd 4 ");//表示標題欄位 list.add(map3); //=== 這裡造一些資料用作示範,也可以直接讀取資料庫中的資料 結束======== //遍曆資料,並且將資料寫入到索引中 for(int i=0 ;i<list.size();i++){ // ====== 擷取資料開始 ====== Map<String,String> mapCen=list.get(i); String title = mapCen.get("title").toString();//擷取標題 String centent = mapCen.get("centent").toString();//擷取內容 // ====== 擷取資料結束 ====== //========= 將資料寫入索引開始 ================= //建立Document對象,Document表示lucene索引的資料結構單元 Document doc=new Document(); //添加id 資料,最後面Field.Store.YES表示儲存該欄位 doc.add(new IntField("id", i, Field.Store.YES)); /*添加標題資料,最後面Field.Store.YES 表示儲存該欄位 *注意:如果需要檢索該欄位,建議使用TextField類型,並且設定為 Field.Store.YES */ doc.add(new TextField("title",title , Field.Store.YES)); //添加標題內容資料,最後面Field.Store.NO 表示不儲存該欄位 doc.add(new TextField("centent", centent, Field.Store.NO)); // 寫入索引 writer.addDocument(doc); //========= 將資料寫入索引結束 ================= } }catch(Exception e){ System.out.println("發生了異常!"); e.printStackTrace(); }finally{ try { writer.close();//關閉流,一定要記得關閉 } catch (IOException e) { } } } /** * 示範對特定項搜尋 * @param luceneDir 該參數表示索引所存放的目錄 * @param field 表示需要搜尋的項(就是寫入時屬性欄位名稱) * @param qu 需要檢索的關鍵詞 */ public static void testTermQuery(String luceneDir,String field, String qu){ IndexReader reader = null;//聲明索引讀取器 try{ //建立索引目錄 , 這裡參數luceneDir 表示索引存放目錄 Directory dir=FSDirectory.open(Paths.get(luceneDir)); //建立索引讀取器 reader=DirectoryReader.open(dir); //建立索引搜尋器 IndexSearcher is=new IndexSearcher(reader); //建立查詢器,參數field表示屬性欄位,qu表示檢索的關鍵詞 Term t=new Term(field,qu); Query query=new TermQuery(t); //這裡查詢前10條匹配的資料,第二個參數為10表示查前十條 TopDocs hits=is.search(query, 10); //列印 System.out.println("檢索文檔欄位"+ field +" ,匹配 "+qu+" , 查詢到"+hits.totalHits+"個記錄"); //遍曆查詢到的資料 for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=is.doc(scoreDoc.doc); String id = doc.get("id")==null?"":doc.get("id").toString();//擷取id String title = doc.get("title")==null?"":doc.get("title").toString();//擷取標題 //由於寫入的時候 ,設定了Field.Store.NO 表示不儲存該欄位,所以這裡讀取不到 //String centent = doc.get("centent")==null"":doc.toString();//擷取內容 //列印資料 System.out.println("id是:"+id+" ,標題是:"+title); } }catch(Exception e){ System.out.println("發生了異常!"); e.printStackTrace(); }finally{ try { reader.close();//關閉流,一定要記得關閉 } catch (IOException e) { } } } /** * 示範解析查詢運算式(這裡示範查詢title欄位屬性) * @param luceneDir 該參數表示索引所存放的目錄 * @param titleStr 需要檢索的關鍵詞 */ public static void testQueryParser(String luceneDir,String titleStr){ IndexReader reader = null;//聲明索引讀取器 try{ //建立索引目錄 , 這裡參數luceneDir 表示索引存放目錄 Directory dir=FSDirectory.open(Paths.get(luceneDir)); //建立索引讀取器 reader=DirectoryReader.open(dir); //建立索引搜尋器 IndexSearcher is=new IndexSearcher(reader); //建立標準分詞器(一般英文使用這個,不適合中文使用) Analyzer analyzer=new StandardAnalyzer(); // 標準分詞器 //建立QueryParser查詢器,查詢title屬性欄位 QueryParser parser=new QueryParser("title", analyzer); //設定查詢關鍵詞 Query query=parser.parse(titleStr); //這裡查詢前10條匹配的資料,第二個參數為10表示查前十條 TopDocs hits=is.search(query, 10); //列印資訊 System.out.println("匹配 "+titleStr+" , 查詢到"+hits.totalHits+"個記錄"); //遍曆查詢到的資料 for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=is.doc(scoreDoc.doc); String id = doc.get("id")==null?"":doc.get("id").toString();//擷取id String title = doc.get("title")==null?"":doc.get("title").toString();//擷取標題 //由於寫入的時候 ,設定了Field.Store.NO 表示不儲存該欄位,所以這裡讀取不到 //String centent = doc.get("centent").toString();//擷取內容 //列印資料 System.out.println("id是:"+id+" ,標題是:"+title); } }catch(Exception e){ System.out.println("發生了異常!"); e.printStackTrace(); }finally{ try { reader.close();//關閉流,一定要記得關閉 } catch (IOException e) { } } } /** * 示範指定項範圍搜尋 * @param luceneDir 該參數表示索引所存放的目錄 */ public static void testTermRangeQuery(String luceneDir){ IndexReader reader = null;//聲明索引讀取器 try{ //建立索引目錄 , 這裡參數luceneDir 表示索引存放目錄 Directory dir=FSDirectory.open(Paths.get(luceneDir)); //建立索引讀取器 reader=DirectoryReader.open(dir); //建立索引搜尋器 IndexSearcher is=new IndexSearcher(reader); /* * 建立TermRangeQuery查詢器,查詢title屬性欄位 從a到e範圍內 * 一般對於英文來說,進行ASC碼範圍查詢還有那麼一點意義, * 中文漢字進行ASC碼值比較沒什麼太大意義, * 一般數字範圍查詢NumericRangeQuery用的比較多一點, * 比如價格,年齡,金額,數量等等都涉及到數字,數字範圍查詢需求也很普遍 * * 後面兩個boolean值用來控制是否包含兩個上下邊界值的。 */ TermRangeQuery query=new TermRangeQuery("title", new BytesRef("a".getBytes()), new BytesRef("e".getBytes()), true, true); //這裡查詢前10條匹配的資料,第二個參數為10表示查前十條 TopDocs hits=is.search(query, 10); //遍曆查詢到的資料 for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=is.doc(scoreDoc.doc); String id = doc.get("id")==null?"":doc.get("id").toString();//擷取id String title = doc.get("title")==null?"":doc.get("title").toString();//擷取標題 //列印資料 System.out.println("id是:"+id+" ,標題是:"+title); } }catch(Exception e){ System.out.println("發生了異常!"); e.printStackTrace(); }finally{ try { reader.close();//關閉流,一定要記得關閉 } catch (IOException e) { } } } /** * 示範指定數字範圍 * @param luceneDir 該參數表示索引所存放的目錄 */ public static void testNumericRangeQuery(String luceneDir){ IndexReader reader = null;//聲明索引讀取器 try{ //建立索引目錄 , 這裡參數luceneDir 表示索引存放目錄 Directory dir=FSDirectory.open(Paths.get(luceneDir)); //建立索引讀取器 reader=DirectoryReader.open(dir); //建立索引搜尋器 IndexSearcher is=new IndexSearcher(reader); /* * 建立NumericRangeQuery查詢器,查詢id屬性欄位 從1到2範圍內 * * 後面兩個boolean值用來控制是否包含兩個上下邊界值的。 */ NumericRangeQuery<Integer> query=NumericRangeQuery.newIntRange("id", 1, 2, true, true); //這裡查詢前10條匹配的資料,第二個參數為10表示查前十條 TopDocs hits=is.search(query, 10); //遍曆查詢到的資料 for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=is.doc(scoreDoc.doc); String id = doc.get("id")==null?"":doc.get("id").toString();//擷取id String title = doc.get("title")==null?"":doc.get("title").toString();//擷取標題 //列印資料 System.out.println("id是:"+id+" ,標題是:"+title); } }catch(Exception e){ System.out.println("發生了異常!"); e.printStackTrace(); }finally{ try { reader.close();//關閉流,一定要記得關閉 } catch (IOException e) { } } } /** * 示範指定字串開頭搜尋 * @param luceneDir 該參數表示索引所存放的目錄 */ public static void testPrefixQuery(String luceneDir){ IndexReader reader = null;//聲明索引讀取器 try{ //建立索引目錄 , 這裡參數luceneDir 表示索引存放目錄 Directory dir=FSDirectory.open(Paths.get(luceneDir)); //建立索引讀取器 reader=DirectoryReader.open(dir); //建立索引搜尋器 IndexSearcher is=new IndexSearcher(reader); /* * 建立NumericRangeQuery查詢器, * 查詢title屬性欄位分詞索引以 w 開頭的資料 */ PrefixQuery query=new PrefixQuery(new Term("title","w")); //這裡查詢前10條匹配的資料,第二個參數為10表示查前十條 TopDocs hits=is.search(query, 10); //遍曆查詢到的資料 for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=is.doc(scoreDoc.doc); String id = doc.get("id")==null?"":doc.get("id").toString();//擷取id String title = doc.get("title")==null?"":doc.get("title").toString();//擷取標題 //列印資料 System.out.println("id是:"+id+" ,標題是:"+title); } }catch(Exception e){ System.out.println("發生了異常!"); e.printStackTrace(); }finally{ try { reader.close();//關閉流,一定要記得關閉 } catch (IOException e) { } } } /** * 示範多條件查詢 * @param luceneDir 該參數表示索引所存放的目錄 */ public static void testBooleanQuery(String luceneDir){ IndexReader reader = null;//聲明索引讀取器 try{ //建立索引目錄 , 這裡參數luceneDir 表示索引存放目錄 Directory dir=FSDirectory.open(Paths.get(luceneDir)); //建立索引讀取器 reader=DirectoryReader.open(dir); //建立索引搜尋器 IndexSearcher is=new IndexSearcher(reader); //========== 建立多個查詢條件 開始=============== BooleanQuery.Builder booleanQuery=new BooleanQuery.Builder(); /* * 條件1, * 建立NumericRangeQuery查詢器,查詢id屬性欄位 從1到2範圍內 * 後面兩個boolean值用來控制是否包含兩個上下邊界值的。 */ NumericRangeQuery<Integer> query1=NumericRangeQuery.newIntRange("id", 1, 2, true, true); /* * 添加查詢條件1 到組合查詢器中 * BooleanClause.Occur.MUST 表示與 (相當於並且) * BooleanClause.Occur.SHOULD 表示或 (相當於或者) * BooleanClause.Occur.MUST_NOT 表示不包含 (相當於not) */ booleanQuery.add(query1,BooleanClause.Occur.MUST); /* * 條件2, * 建立NumericRangeQuery查詢器, * 查詢title屬性欄位分詞索引以 12 開頭的資料 */ PrefixQuery query2=new PrefixQuery(new Term("title","12")); /* * 添加查詢條件2 到組合查詢器中 * BooleanClause.Occur.MUST 表示與 (相當於並且) * BooleanClause.Occur.SHOULD 表示或 (相當於或者) * BooleanClause.Occur.MUST_NOT 表示不包含 (相當於not) */ booleanQuery.add(query2,BooleanClause.Occur.MUST); //========== 建立多個查詢條件 結束=============== /* * 這裡查詢前10條匹配的資料,第二個參數為10表示查前十條 * 這裡查詢檢索表示滿足條件1並且同時滿足條件2的資料 */ TopDocs hits=is.search(booleanQuery.build(), 10); //遍曆查詢到的資料 for(ScoreDoc scoreDoc:hits.scoreDocs){ Document doc=is.doc(scoreDoc.doc); String id = doc.get("id")==null?"":doc.get("id").toString();//擷取id String title = doc.get("title")==null?"":doc.get("title").toString();//擷取標題 //列印資料 System.out.println("id是:"+id+" ,標題是:"+title); } }catch(Exception e){ System.out.println("發生了異常!"); e.printStackTrace(); }finally{ try { reader.close();//關閉流,一定要記得關閉 } catch (IOException e) { } } } /** * 測試上面的方法 * @param args * @throws Exception */ public static void main(String[] args) throws Exception { //表示索引目錄 String luceneDir = "D://lucene"; //測試該方法寫入資料到索引檔案中 //writeIndex(luceneDir); //測試檢索屬性欄位title,檢索關鍵詞為demo //testTermQuery(luceneDir,"title", "de