2019-07-16

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í)行搜索--->渲染搜索結果矛紫。

入門示例

1.1?需求

使用Lucene實現(xiàn)電商項目中圖書類商品的索引和搜索功能。

1.2?配置步驟說明

(1)搭建環(huán)境(先下載Lucene)

(2)創(chuàng)建索引庫

(3)搜索索引庫


1.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

第二部分:創(chuàng)建索引

步驟說明:

(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客戶端連接索引庫

Luke作為Lucene工具包中的一個工具(http://www.getopt.org/luke/)蜜笤,可以通過可視化界面,連接操作索引庫盐碱。


啟動方法

(1)雙擊start.bat啟動把兔!



(2)連接索引庫



驗證分詞效果



Field域

問題:我們已經(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常用類型

域的常用類型有很多祟绊,每一個類都有自己默認的三大屬性。如下:

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ù)庫中新上架了圖書蔬啡,必須把這些圖書也添加到索引庫中,不然就搜不到該新上架的圖書了镀虐。


代碼實現(xiàn)

調(diào)用indexWriter.addDocument(doc)添加索引箱蟆。

參考入門示例中的創(chuàng)建索引。


刪除索引(文檔)

需求

某些圖書不再出版銷售了粉私,我們需要從索引庫中移除該圖書顽腾。

代碼實現(xiàn)

@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更新索引比較特殊桌硫,是先刪除滿足條件的索引夭咬,再添加新的索引。

代碼實現(xiàn)

//修改索引

@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)復雜的查詢。


創(chuàng)建查詢的兩種方法

執(zhí)行查詢之前虾宇,必須創(chuàng)建一個查詢Query查詢對象搓彻。


Query自身是一個抽象類,不能實例化,必須通過其它的方式來實現(xiàn)初始化旭贬。


在這里怔接,Lucene提供了兩種初始化Query查詢對象的方式。

使用Lucene提供Query子類

Query是一個抽象類稀轨,lucene提供了很多查詢對象扼脐,比如TermQuery項精確查詢,NumericRangeQuery數(shù)字范圍查詢等奋刽。


使用TermQuery實例化

Query query = new?TermQuery(new?Term("name", "lucene"));


使用QueryParse解析查詢表達式

QueryParser會將用戶輸入的查詢表達式解析成Query對象實例瓦侮。如下代碼:

QueryParser queryParser = new?QueryParser("name", new?IKAnalyzer());

Query query?= queryParser.parse("name:lucene");


常用的Query子類搜索

TermQuery

特點:查詢的關鍵詞不會再做分詞處理,作為整體來搜索佣谐。代碼如下:

/**

* 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();

}

}


NumericRangeQuery

指定數(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

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);

????}


通過QueryParser搜索

特點

對搜索的關鍵詞攒读,做分詞處理。

語法

基礎語法

域名:關鍵字

實例:name:java


組合條件語法

條件1 AND 條件2 ?

條件1 OR 條件2

條件1 NOT 條件2


QueryParser

代碼實現(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();

}

}


MultiFieldQueryParser

通過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();

}

}

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末墨礁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子耳峦,更是在濱河造成了極大的恐慌恩静,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹲坷,死亡現(xiàn)場離奇詭異驶乾,居然都是意外死亡,警方通過查閱死者的電腦和手機循签,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門级乐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人县匠,你說我怎么就攤上這事风科。” “怎么了乞旦?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵贼穆,是天一觀的道長。 經(jīng)常有香客問我兰粉,道長故痊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任玖姑,我火速辦了婚禮愕秫,結果婚禮上浊仆,老公的妹妹穿的比我還像新娘。我一直安慰自己豫领,他們只是感情好,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布舔琅。 她就那樣靜靜地躺著等恐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪备蚓。 梳的紋絲不亂的頭發(fā)上课蔬,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音郊尝,去河邊找鬼二跋。 笑死,一個胖子當著我的面吹牛流昏,可吹牛的內(nèi)容都是我干的扎即。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼况凉,長吁一口氣:“原來是場噩夢啊……” “哼谚鄙!你這毒婦竟也來了?” 一聲冷哼從身側響起刁绒,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤闷营,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后知市,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體傻盟,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年嫂丙,在試婚紗的時候發(fā)現(xiàn)自己被綠了娘赴。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡奢入,死狀恐怖筝闹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腥光,我是刑警寧澤关顷,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站武福,受9級特大地震影響议双,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捉片,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一平痰、第九天 我趴在偏房一處隱蔽的房頂上張望汞舱。 院中可真熱鬧,春花似錦宗雇、人聲如沸昂芜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泌神。三九已至,卻和暖如春舞虱,著一層夾襖步出監(jiān)牢的瞬間欢际,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工矾兜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留损趋,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓椅寺,卻偏偏與公主長得像浑槽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子返帕,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容