Lucene簡介
Lucene是apache下的一個開源的全文檢索引擎工具包芯肤。
1.1?全文檢索(Full-text Search)?
1.1.1?定義
全文檢索就是先分詞創(chuàng)建索引,再執(zhí)行搜索的過程。
分詞:就是將一段文字分成一個個單詞
全文檢索就將一段文字分成一個個單詞去查詢數(shù)據(jù)!D吨印本谜!
1.1.2 應用場景
1.1.2.1?搜索引擎(了解)
搜索引擎是一個基于全文檢索痕寓、能獨立運行、提供搜索服務的軟件系統(tǒng)寒匙。
1.1.1.1電商站內(nèi)搜索(重點)
思考:電商網(wǎng)站內(nèi)零如,我們都是通過輸入關鍵詞來搜索商品的。如果我們根據(jù)關鍵詞锄弱,直接查詢數(shù)據(jù)庫考蕾,會有什么后果?
答:我們只能使用模糊搜索会宪,來進行匹配肖卧,會導致很多數(shù)據(jù)匹配不到。所以掸鹅,我們必須使用全文檢索塞帐。
1.1Lucene實現(xiàn)全文檢索的流程
全文檢索的流程分為兩大部分:索引流程拦赠、搜索流程。
索引流程:采集數(shù)據(jù)--->構建文檔對象--->創(chuàng)建索引(將文檔寫入索引庫)壁榕。
搜索流程:創(chuàng)建查詢--->執(zhí)行搜索--->渲染搜索結果矛紫。
使用Lucene實現(xiàn)電商項目中圖書類商品的索引和搜索功能。
(1)搭建環(huán)境(先下載Lucene)
(2)創(chuàng)建索引庫
(3)搜索索引庫
1.3.1?第一部分:搭建環(huán)境(創(chuàng)建項目牌里,導入包)
前提:已經(jīng)創(chuàng)建好了數(shù)據(jù)庫(直接導入book.sql文件)
第一步:下載Lucene
Lucene是開發(fā)全文檢索功能的工具包颊咬,使用時從官方網(wǎng)站下載,并解壓牡辽。
官方網(wǎng)站:http://lucene.apache.org/?
下載地址:http://archive.apache.org/dist/lucene/java/
下載版本:4.10.3(要求:jdk1.7及以上)
核心包lucene-core-4.10.3.jar(附常用API)
第二步:創(chuàng)建項目喳篇,導入包
mysql5.1驅(qū)動包:mysql-connector-java-5.1.7-bin.jar
核心包:lucene-core-4.10.3.jar
分析器通用包:lucene-analyzers-common-4.10.3.jar
查詢解析器包:lucene-queryparser-4.10.3.jar
步驟說明:
(1)采集數(shù)據(jù)
(2)將數(shù)據(jù)轉(zhuǎn)換成Lucene文檔
(3)將文檔寫入索引庫,創(chuàng)建索引
第一步:采集數(shù)據(jù)
Lucene全文檢索态辛,不是直接查詢數(shù)據(jù)庫麸澜,所以需要先將數(shù)據(jù)采集出來。
(1)創(chuàng)建Book類
public?class?Book {
private?Integer bookId;??//圖書ID
private?String name;???//圖書名稱
private?Float price;????//圖書價格
private?String pic;????//圖書圖片
private?String description;?//圖書描述
????// 補全get\set方法
}
(2)創(chuàng)建一個BookDao類
package?cn.gzsxt.lucene.dao;
import?java.sql.Connection;
import?java.sql.DriverManager;
import?java.sql.PreparedStatement;
import?java.sql.ResultSet;
import?java.sql.SQLException;
import?java.util.ArrayList;
import?java.util.List;
import?cn.gzsxt.lucene.pojo.Book;
public?class?BookDao {
public?List<Book> getAll() {
//數(shù)據(jù)庫鏈接
Connection connection?= null;
//預編譯statement
PreparedStatement preparedStatement?= null;
//結果集
ResultSet resultSet?= null;
//圖書列表
List<Book> list?= new?ArrayList<Book>();
try?{
//加載數(shù)據(jù)庫驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
//連接數(shù)據(jù)庫
connection?= DriverManager.getConnection(
"jdbc:mysql://localhost:3306/lucene", "root", "gzsxt");
// SQL語句
String sql?= "SELECT * FROM book";
//創(chuàng)建preparedStatement
preparedStatement?= connection.prepareStatement(sql);
//獲取結果集
resultSet?= preparedStatement.executeQuery();
//結果集解析
while?(resultSet.next()) {
Book book?= new?Book();
book.setBookId(resultSet.getInt("id"));
book.setName(resultSet.getString("name"));
book.setPrice(resultSet.getFloat("price"));
book.setPic(resultSet.getString("pic"));
book.setDescription(resultSet.getString("description"));
list.add(book);
}
} catch?(Exception e) {
e.printStackTrace();
}finally?{
if(null!=resultSet){
try?{
resultSet.close();
} catch?(SQLException e) {
// TODO?Auto-generated catch block
e.printStackTrace();
}
}
if(null!=preparedStatement){
try?{
preparedStatement.close();
} catch?(SQLException e) {
// TODO?Auto-generated catch block
e.printStackTrace();
}
}
if(null!=connection){
try?{
connection.close();
} catch?(SQLException e) {
// TODO?Auto-generated catch block
e.printStackTrace();
}
}
}
return?list;
}
}
(3)創(chuàng)建一個測試類BookDaoTest
package?cn.gzsxt.lucene.test;
import?java.util.List;
import?org.junit.Test;
import?cn.gzsxt.lucene.dao.BookDao;
import?cn.gzsxt.lucene.pojo.Book;
public?class?BookDaoTest {
@Test
public?void?getAll(){
BookDao dao?= new?BookDao();
List<Book> books?= dao.getAll();
for?(Book book?: books) {
System.out.println("圖書id:"+book.getBookId()+",圖書名稱:"+book.getName());
}
}
}
(4)測試結果奏黑,采集數(shù)據(jù)成功
第二步:將數(shù)據(jù)轉(zhuǎn)換成Lucene文檔
Lucene是使用文檔類型來封裝數(shù)據(jù)的炊邦,所有需要先將采集的數(shù)據(jù)轉(zhuǎn)換成文檔類型。其格式為:
修改BookDao熟史,新增一個方法馁害,轉(zhuǎn)換數(shù)據(jù)
public?List<Document> getDocuments(List<Book> books){
// Document對象集合
List<Document> docList?= new?ArrayList<Document>();
// Document對象
Document doc?= null;
for?(Book book?: books) {
//創(chuàng)建Document對象,同時要創(chuàng)建field對象
doc?= new?Document();
//根據(jù)需求創(chuàng)建不同的Field
Field id?= new?TextField("id", book.getBookId().toString(), Store.YES);
Field name?= new?TextField("name", book.getName(), Store.YES);
Field price?= new?TextField("price", book.getPrice().toString(),Store.YES);
Field pic?= new?TextField("pic", book.getPic(), Store.YES);
Field desc?= new?TextField("description", book.getDescription(), Store.YES);
//把域(Field)添加到文檔(Document)中
doc.add(id);
doc.add(name);
doc.add(price);
doc.add(pic);
doc.add(desc);
docList.add(doc);
}
return?docList;
}
第三步:創(chuàng)建索引庫
說明:Lucene是在將文檔寫入索引庫的過程中蹂匹,自動完成分詞碘菜、創(chuàng)建索引的。因此創(chuàng)建索引庫限寞,從形式上看忍啸,就是將文檔寫入索引庫!
修改測試類履植,新增createIndex方法
@Test
public?void?createIndex(){
try?{
BookDao dao?= new?BookDao();
//分析文檔计雌,對文檔中的field域進行分詞
Analyzer analyzer?= new?StandardAnalyzer();
//創(chuàng)建索引
// 1)創(chuàng)建索引庫目錄
Directory directory?= FSDirectory.open(new?File("F:\\lucene\\0719"));
// 2)創(chuàng)建IndexWriterConfig對象
IndexWriterConfig cfg?= new?IndexWriterConfig(Version.LATEST, analyzer);
// 3)創(chuàng)建IndexWriter對象
IndexWriter writer?= new?IndexWriter(directory, cfg);
// 4)通過IndexWriter對象添加文檔對象(document)
writer.addDocuments(dao.getDocuments(dao.getAll()));
// 5)關閉IndexWriter
writer.close();
System.out.println("創(chuàng)建索引庫成功");
} catch?(Exception e) {
e.printStackTrace();
}
}
測試結果,創(chuàng)建成功>材帷0追邸!
說明
搜索的時候鼠渺,需要指定搜索哪一個域(也就是字段)鸭巴,并且,還要對搜索的關鍵詞做分詞處理拦盹。
執(zhí)行搜索
修改測試類鹃祖,新增searchDocumentByIndex方法
@Test
public?void?searchDocumentByIndex(){
try?{
// 1、 創(chuàng)建查詢(Query對象)
//創(chuàng)建分析器
Analyzer analyzer?= new?StandardAnalyzer();
QueryParser queryParser?= new?QueryParser("name", analyzer);
Query query?= queryParser.parse("name:java教程");
// 2普舆、 執(zhí)行搜索
// a)指定索引庫目錄
Directory directory?= FSDirectory.open(new?File("F:\\lucene\\0719"));
// b)創(chuàng)建IndexReader對象
IndexReader reader?= DirectoryReader.open(directory);
// c)創(chuàng)建IndexSearcher對象
IndexSearcher searcher?= new?IndexSearcher(reader);
// d)通過IndexSearcher對象執(zhí)行查詢索引庫恬口,返回TopDocs對象
//第一個參數(shù):查詢對象
//第二個參數(shù):最大的n條記錄
TopDocs topDocs?= searcher.search(query, 10);
// e)提取TopDocs對象中前n條記錄
ScoreDoc[] scoreDocs?= topDocs.scoreDocs;
System.out.println("查詢出文檔個數(shù)為:"?+ topDocs.totalHits);
for?(ScoreDoc scoreDoc?: scoreDocs) {
//文檔對象ID
int?docId?= scoreDoc.doc;
Document doc?= searcher.doc(docId);
// f)輸出文檔內(nèi)容
System.out.println("===============================");
System.out.println("文檔id:"?+ docId);
System.out.println("圖書id:"?+ doc.get("id"));
System.out.println("圖書name:"?+ doc.get("name"));
System.out.println("圖書price:"?+ doc.get("price"));
System.out.println("圖書pic:"?+ doc.get("pic"));
System.out.println("圖書description:"?+ doc.get("description"));
}
// g)關閉IndexReader
reader.close();
} catch?(Exception e) {
// TODO?Auto-generated catch block
e.printStackTrace();
}
}
測試結果校读,非常成功!W婺堋歉秫!
Lucene全文檢索,確實可以實現(xiàn)對關鍵詞做分詞养铸、再執(zhí)行搜索功能雁芙。并且結果更精確。
分詞是全文檢索的核心钞螟。
?
所謂的分詞兔甘,就是將一段文本,根據(jù)一定的規(guī)則鳞滨,拆分成一個一個詞洞焙。
Lucene是根據(jù)分析器實現(xiàn)分詞的。針對不同的語言提供了不同的分析器拯啦。并且提供了一個通用的標準分析器StandardAnalyzer
--說明:我們通過分析StandardAnalyzer核心源碼來分析分詞過程
@Override
??protected?TokenStreamComponents createComponents(final?String fieldName, final?Reader reader) {
????final?StandardTokenizer src?= new?StandardTokenizer(getVersion(), reader);
????src.setMaxTokenLength(maxTokenLength);
????TokenStream tok?= new?StandardFilter(getVersion(), src);
????tok?= new?LowerCaseFilter(getVersion(), tok);
????tok?= new?StopFilter(getVersion(), tok, stopwords);
????return?new?TokenStreamComponents(src, tok) {
??????@Override
??????protected?void?setReader(final?Reader reader) throws?IOException {
????????src.setMaxTokenLength(StandardAnalyzer.this.maxTokenLength);
????????super.setReader(reader);
??????}
????};
??}
對應Lucene分詞的過程澡匪,我們可以做如下總結:
(1)分詞的時候,是以域為單位的褒链。不同的域仙蛉,相互獨立。
同一個域中碱蒙,拆分出來相同的詞,視為同一個詞(Term)
不同的域中夯巷,拆分出來相同的詞赛惩,不是同一個詞。
其中趁餐,Term是Lucene最小的語匯單元喷兼,不可再細分。
(2)分詞的時候經(jīng)歷了一系列的過濾器后雷。如大小寫轉(zhuǎn)換季惯、去除停用詞等。
我們這里借助前面的示例來說明
從上圖中臀突,我們發(fā)現(xiàn):
(1)索引庫中有兩個區(qū)域:索引區(qū)勉抓、文檔區(qū)。
(2)文檔區(qū)存放的是文檔候学。Lucene給每一個文檔自動加上一個文檔編號docID藕筋。
(3)索引區(qū)存放的是索引。注意:
索引是以域為單位的梳码,不同的域隐圾,彼此相互獨立伍掀。
索引是根據(jù)分詞規(guī)則創(chuàng)建出來的,根據(jù)索引就能找到對應的文檔暇藏。
Luke作為Lucene工具包中的一個工具(http://www.getopt.org/luke/)蜜笤,可以通過可視化界面,連接操作索引庫盐碱。
(1)雙擊start.bat啟動把兔!
(2)連接索引庫
問題:我們已經(jīng)知道,Lucene是在寫入文檔時甸各,完成分詞垛贤、索引的。那Lucene是怎么知道的呢趣倾?
答:Lucene是根據(jù)文檔中的域的屬性聘惦,來確定是否要分詞、創(chuàng)建索引的儒恋。所以善绎,我們必須搞清楚域有哪些屬性。
]是否分詞(tokenized)
只有設置了分詞屬性為true诫尽,lucene才會對這個域進行分詞處理禀酱。
在實際的開發(fā)中,有一些字段是不需要分詞的牧嫉,比如商品id剂跟,商品圖片等。
而有一些字段是必須分詞的酣藻,比如商品名稱曹洽,描述信息等。
是否索引(indexed)
只有設置了索引屬性為true辽剧,lucene才為這個域的Term詞創(chuàng)建索引送淆。
在實際的開發(fā)中,有一些字段是不需要創(chuàng)建索引的怕轿,比如商品的圖片等偷崩。我們只需要對參與搜索的字段做索引處理。
是否存儲(stored)
只有設置了存儲屬性為true撞羽,在查找的時候阐斜,才能從文檔中獲取這個域的值。
在實際開發(fā)中诀紊,有一些字段是不需要存儲的智听。比如:商品的描述信息。
因為商品描述信息,通常都是大文本數(shù)據(jù)到推,讀的時候會造成巨大的IO開銷考赛。而描述信息是不需要經(jīng)常查詢的字段,這樣的話就白白浪費了cpu的資源了莉测。
因此颜骤,像這種不需要經(jīng)常查詢,又是大文本的字段捣卤,通常不會存儲到索引庫肤粱。
(1)三大屬性彼此獨立典奉。
(2)通常分詞是為了創(chuàng)建索引问潭。
(3)不存儲這個域文本內(nèi)容腥例,也可以對這個域先分詞、創(chuàng)建索引子姜。
?
域的常用類型有很多祟绊,每一個類都有自己默認的三大屬性。如下:
Field類數(shù)據(jù)類型Analyzed
是否分詞
Indexed
是否索引
Stored
是否存儲
StringField(FieldName, FieldValue,Store.YES))字符串NYY或N
LongField(FieldName, FieldValue,Store.YES)Long型YYY或N
FloatField(FieldName, FieldValue,Store.YES)Float型YYY或N
StoredField(FieldName, FieldValue)?重載方法哥捕,支持多種類型NNY
TextField(FieldName, FieldValue, Store.NO)字符串YYY或N
(1)圖書id:
是否分詞:不用分詞牧抽,因為不會根據(jù)商品id來搜索商品?
是否索引:不索引,因為不需要根據(jù)圖書ID進行搜索
是否存儲:要存儲遥赚,因為查詢結果頁面需要使用id這個值扬舒。
(2)圖書名稱:
是否分詞:要分詞,因為要將圖書的名稱內(nèi)容分詞索引凫佛,根據(jù)關鍵搜索圖書名稱抽取的詞讲坎。
是否索引:要索引。
是否存儲:要存儲愧薛。
(3)圖書價格:
是否分詞:要分詞衣赶,lucene對數(shù)字型的值只要有搜索需求的都要分詞和索引,因為lucene對數(shù)字型的內(nèi)容要特殊分詞處理厚满,本例子可能要根據(jù)價格范圍搜索,需要分詞和索引碧磅。
是否索引:要索引
是否存儲:要存儲
(4)圖書圖片地址:
是否分詞:不分詞
是否索引:不索引
是否存儲:要存儲
(5)圖書描述:
是否分詞:要分詞
是否索引:要索引
是否存儲:因為圖書描述內(nèi)容量大碘箍,不在查詢結果頁面直接顯示,不存儲鲸郊。
不存儲是來不在lucene的索引文件中記錄丰榴,節(jié)省lucene的索引文件空間,如果要在詳情頁面顯示描述秆撮,思路:
從lucene中取出圖書的id四濒,根據(jù)圖書的id查詢關系數(shù)據(jù)庫中book表得到描述信息。
修改BookDao的getDocument方法
public?List<Document> getDocuments(List<Book> books){
// Document對象集合
List<Document> docList?= new?ArrayList<Document>();
// Document對象
Document doc?= null;
for?(Book book?: books) {
//創(chuàng)建Document對象,同時要創(chuàng)建field對象
doc?= new?Document();
//圖書ID
//參數(shù):域名盗蟆、域中存儲的內(nèi)容戈二、是否存儲
//不分詞、索引喳资、要存儲
// Field id = new TextField("id", book.getId().toString(),Store.YES);
Field id = new?StoredField("id", book.getBookId().toString());
//圖書名稱
//分詞觉吭、索引、存儲
Field name = new?TextField("name", book.getName(),Store.YES);
//圖書價格
//分詞仆邓、索引鲜滩、存儲
Field price = new?FloatField("price", book.getPrice(), Store.YES);
//圖書圖片
//不分詞、不索引节值、要存儲
Field pic = new?StoredField("pic", book.getPic());
//圖書描述
//分詞徙硅、索引、不存儲
Field desc = new?TextField("description",book.getDescription(), Store.NO);
//把域(Field)添加到文檔(Document)中
doc.add(id);
doc.add(name);
doc.add(price);
doc.add(pic);
doc.add(desc);
docList.add(doc);
}
return?docList;
}
(1)去索引庫目錄中搞疗,手動清空索引庫嗓蘑。
(2)重新創(chuàng)建索引庫。
(3)使用Luke驗證分詞贴汪、索引效果脐往。
改造成功!0夤 业簿!
在第4節(jié),我們需要重新創(chuàng)建索引的時候阳懂,是去索引庫目錄下梅尤,手動刪除的。
而在實際的開發(fā)中岩调,我們可能壓根就不知道索引庫在哪巷燥,就算知道,我們也不可能每次都去手動刪除号枕,非常之麻煩g志尽!葱淳!
所以钝腺,我們必須學習如何維護索引庫,使用程序來操作索引庫赞厕。
需要注意的是艳狐,索引是與文檔緊密相連的,因此對索引的維護皿桑,實際上就是對文檔的增刪改毫目。
數(shù)據(jù)庫中新上架了圖書蔬啡,必須把這些圖書也添加到索引庫中,不然就搜不到該新上架的圖書了镀虐。
調(diào)用indexWriter.addDocument(doc)添加索引箱蟆。
參考入門示例中的創(chuàng)建索引。
某些圖書不再出版銷售了粉私,我們需要從索引庫中移除該圖書顽腾。
@Test
public?void?deleteIndex() throws?Exception {
// 1、指定索引庫目錄
Directory directory?= FSDirectory.open(new?File("F:\\lucene\\0719"));
// 2诺核、創(chuàng)建IndexWriterConfig
IndexWriterConfig cfg?= new?IndexWriterConfig(Version.LATEST,
new?StandardAnalyzer());
// 3抄肖、 創(chuàng)建IndexWriter
IndexWriter writer?= new?IndexWriter(directory, cfg);
// 4、通過IndexWriter來刪除索引
//刪除指定索引
writer.deleteDocuments(new?Term("name", "apache"));
// 5窖杀、關閉IndexWriter
writer.close();
System.out.println("刪除成功");
}
@Test
public?void?deleteIndex() throws?Exception {
// 1漓摩、指定索引庫目錄
Directory directory?= FSDirectory.open(new?File("F:\\lucene\\0719"));
// 2、創(chuàng)建IndexWriterConfig
IndexWriterConfig cfg?= new?IndexWriterConfig(Version.LATEST,
new?StandardAnalyzer());
// 3入客、 創(chuàng)建IndexWriter
IndexWriter writer?= new?IndexWriter(directory, cfg);
// 4管毙、通過IndexWriter來刪除索引
//刪除指定索引
writer.deleteAll();
// 5、關閉IndexWriter
writer.close();
System.out.println("清空索引庫成功");
}
?說明
Lucene更新索引比較特殊桌硫,是先刪除滿足條件的索引夭咬,再添加新的索引。
//修改索引
@Test
public?void?updateIndex() throws?Exception {
// 1铆隘、指定索引庫目錄
Directory directory?= FSDirectory.open(new?File("F:\\lucene\\0719"));
// 2卓舵、創(chuàng)建IndexWriterConfig
IndexWriterConfig cfg?= new?IndexWriterConfig(Version.LATEST,
new?StandardAnalyzer());
// 3、 創(chuàng)建IndexWriter
IndexWriter writer?= new?IndexWriter(directory, cfg);
// 4膀钠、通過IndexWriter來修改索引
// a)掏湾、創(chuàng)建修改后的文檔對象
Document document?= new?Document();
//文件名稱
Field filenameField?= new?StringField("name", "updateIndex", Store.YES);
document.add(filenameField);
//修改指定索引為新的索引
writer.updateDocument(new?Term("name", "apache"), document);
// 5、關閉IndexWriter
writer.close();
System.out.println("更新成功");
}
問題:我們在入門示例中肿嘲,已經(jīng)知道Lucene是通過IndexSearcher對象融击,來執(zhí)行搜索的。那我們?yōu)槭裁催€要繼續(xù)學習Lucene呢雳窟?
答:因為在實際的開發(fā)中尊浪,我們的查詢的業(yè)務是相對復雜的,比如我們在通過關鍵詞查找的時候封救,往往進行價格拇涤、商品類別的過濾。
而Lucene提供了一套查詢方案兴泥,供我們實現(xiàn)復雜的查詢。
執(zhí)行查詢之前虾宇,必須創(chuàng)建一個查詢Query查詢對象搓彻。
Query自身是一個抽象類,不能實例化,必須通過其它的方式來實現(xiàn)初始化旭贬。
在這里怔接,Lucene提供了兩種初始化Query查詢對象的方式。
Query是一個抽象類稀轨,lucene提供了很多查詢對象扼脐,比如TermQuery項精確查詢,NumericRangeQuery數(shù)字范圍查詢等奋刽。
使用TermQuery實例化
Query query = new?TermQuery(new?Term("name", "lucene"));
QueryParser會將用戶輸入的查詢表達式解析成Query對象實例瓦侮。如下代碼:
QueryParser queryParser = new?QueryParser("name", new?IKAnalyzer());
Query query?= queryParser.parse("name:lucene");
特點:查詢的關鍵詞不會再做分詞處理,作為整體來搜索佣谐。代碼如下:
/**
* Query子類查詢之 TermQuery
?* ?
*特點:不會再對查詢的關鍵詞做分詞處理肚吏。
?*
*需要:查詢書名與java教程相關書。
?*/
@Test
public?void?queryByTermQuery(){
//1狭魂、獲取一個查詢對象
Query query?= new?TermQuery(new?Term("name", "編程思想"));
doSearch(query);
}
???private?void?doSearch(Query query) {
try?{
//2罚攀、創(chuàng)建一個查詢的執(zhí)行對象
//指定索引庫的目錄
Directory d?= FSDirectory.open(new?File("F:\\lucene\\0719"));
//創(chuàng)建流對象
IndexReader reader?= DirectoryReader.open(d);
//創(chuàng)建搜索執(zhí)行對象
IndexSearcher searcher?= new?IndexSearcher(reader);
//3、執(zhí)行搜索
TopDocs result?= searcher.search(query, 10);
//4雌澄、提出結果集斋泄,獲取圖書的信息
int?totalHits?= result.totalHits;
System.out.println("共查詢到"+totalHits+"條滿足條件的數(shù)據(jù)!");
System.out.println("-----------------------------------------");
//提取圖書信息。
//score即相關度镐牺。即搜索的關鍵詞和 圖書名稱的相關度炫掐,用來做排序處理
ScoreDoc[] scoreDocs?= result.scoreDocs;
for?(ScoreDoc scoreDoc?: scoreDocs) {
/**
* scoreDoc.doc的返回值,是文檔的id任柜, 即 將文檔寫入索引庫的時候卒废,lucene自動給這份文檔做的一個編號。
?*
*獲取到這個文檔id之后宙地,即可以根據(jù)這個id摔认,找到這份文檔。
?*/
int?docId?= scoreDoc.doc;
System.out.println("文檔在索引庫中的編號:"+docId);
//從文檔中提取圖書的信息
Document doc?= searcher.doc(docId);
System.out.println("圖書id:"+doc.get("id"));
System.out.println("圖書name:"+doc.get("name"));
System.out.println("圖書price:"+doc.get("price"));
System.out.println("圖書pic:"+doc.get("pic"));
System.out.println("圖書description:"+doc.get("description"));
System.out.println();
System.out.println("------------------------------------");
}
//關閉連接宅粥,釋放資源
if(null!=reader){
reader.close();
}
} catch?(Exception e) {
e.printStackTrace();
}
}
指定數(shù)字范圍查詢.(創(chuàng)建field類型時参袱,注意與之對應)
/**
* Query子類查詢 ?之 ?NumricRangeQuery
*需求:查詢所有價格在[60,80)之間的書
?* @param?query
?*/
@Test
????public?void?queryByNumricRangeQuery(){
???? /**
???? *第一個參數(shù):要搜索的域
???? *第二個參數(shù):最小值
???? *第三個參數(shù):最大值
???? *第四個參數(shù):是否包含最小值
???? *第五個參數(shù):是否包含最大值
???? ?*/
???? Query query?= NumericRangeQuery.newFloatRange("price", 60.0f, 80.0f, true, false);
???? doSearch(query);
????}
BooleanQuery,布爾查詢秽梅,實現(xiàn)組合條件查詢抹蚀。
/**
* Query子類查詢 ?之 ?BooelanQuery查詢 ??組合條件查詢
?????*
*需求:查詢書名包含java,并且價格區(qū)間在[60,80)之間的書企垦。
?????*/
????@Test
????public?void?queryBooleanQuery(){
???? //1环壤、要使用BooelanQuery查詢,首先要把單個創(chuàng)建出來钞诡,然后再通過BooelanQuery組合
???? Query price?= NumericRangeQuery.newFloatRange("price", 60.0f, 80.0f, true, false);
???? Query name?= new?TermQuery(new?Term("name", "java"));
???? //2郑现、創(chuàng)建BooleanQuery實例對象
???? BooleanQuery query?= new?BooleanQuery();
???? query.add(name, Occur.MUST_NOT);
???? query.add(price, Occur.MUST);
???? /**
???? * MSUT表示必須滿足 ?????????????????????????對應的是 ?+
???? * MSUT_NOT必須不滿足 ??????????????????應對的是 ?-
???? * SHOULD可以滿足也可以不滿足 ????沒有符號
???? ?*
???? * SHOULD與MUST湃崩、MUST_NOT組合的時候,SHOULD就沒有意義了接箫。
???? ?*/
???? doSearch(query);
????}
對搜索的關鍵詞攒读,做分詞處理。
基礎語法
域名:關鍵字
實例:name:java
組合條件語法
條件1 AND 條件2 ?
條件1 OR 條件2
條件1 NOT 條件2
代碼實現(xiàn)
/**
*查詢解析器查詢 ?之 ?QueryParser查詢
?*/
@Test
public?void?queryByQueryParser(){
try?{
//1辛友、加載分詞器
Analyzer analyzer?= new?StandardAnalyzer();
/**
* 2薄扁、創(chuàng)建查詢解析器實例對象
*第一個參數(shù):默認搜索的域。
*如果在搜索的時候废累,沒有特別指定搜索的域邓梅,則按照默認的域進行搜索
*如何在搜索的時候指定搜索域呢?
*答:格式 ?域名:關鍵詞 ???????即 ??name:java教程
?*
*第二個參數(shù):分詞器 ??九默,對關鍵詞做分詞處理
?*/
QueryParser parser?= new?QueryParser("description", analyzer);
Query query?= parser.parse("name:java教程");
doSearch(query);
} catch?(Exception e) {
e.printStackTrace();
}
}
通過MulitFieldQueryParse對多個域查詢震放。
/**
*查詢解析器查詢 ?之 ?MultiFieldQueryParser查詢
?* ?
*特點:同時指定多個搜索域,并且對關鍵做分詞處理
?*/
@Test
public?void?queryByMultiFieldQueryParser(){
try?{
//1驼修、定義多個搜索的 ?name殿遂、description
String[] fields?= {"name","description"};
//2、加載分詞器
Analyzer analyzer?= new?StandardAnalyzer();
//3乙各、創(chuàng)建 MultiFieldQueryParser實例對象
MultiFieldQueryParser mParser?= new?MultiFieldQueryParser(fields, analyzer);
Query query?= mParser.parse("lucene教程");
doSearch(query);
} catch?(Exception e) {
e.printStackTrace();
}
}