lucene 第一天
?
?
?
Lucene/Solr
?
第一天
?
1.?課程計(jì)劃
a)?索引流程
b)?搜索流程
a)?索引實(shí)現(xiàn)
b)?搜索實(shí)現(xiàn)
a)?分詞介紹
b)?IK分詞器
?
?
?
?
?
2.?搜索介紹
2.1.?實(shí)現(xiàn)搜索的方案
原來的方式實(shí)現(xiàn)搜索功能,我們的搜索流程如下圖:
?
上圖就是原始搜索引擎技術(shù),如果用戶比較少而且數(shù)據(jù)庫的數(shù)據(jù)量比較小,那么這種方式實(shí)現(xiàn)搜索功能在企業(yè)中是比較常見的。
?
但是數(shù)據(jù)量過多時(shí),數(shù)據(jù)庫的壓力就會(huì)變得很大,查詢速度會(huì)變得非常慢。我們需要使用更好的解決方案來分擔(dān)數(shù)據(jù)庫的壓力。
?
現(xiàn)在的方案(使用Lucene),如下圖
?
為了解決數(shù)據(jù)庫壓力和速度的問題,我們的數(shù)據(jù)庫就變成了索引庫,我們使用Lucene的API的來操作服務(wù)器上的索引庫。這樣完全和數(shù)據(jù)庫進(jìn)行了隔離。
?
?
?
2.2.?數(shù)據(jù)查詢方法
2.2.1.?順序掃描法
所謂順序掃描,例如要找內(nèi)容包含一個(gè)字符串的文件,就是一個(gè)文檔一個(gè)文檔的看,對(duì)于每一個(gè)文檔,從頭看到尾,如果此文檔包含此字符串,則此文檔為我們要找的文件,接著看下一個(gè)文件,直到掃描完所有的文件。
這種方法是順序掃描方法,數(shù)據(jù)量大就搜索慢。
?
2.2.2.?倒排索引
先舉一個(gè)栗子:
例如我們使用新華字典查詢漢字,新華字典有偏旁部首的目錄(索引),我們查字首先查這個(gè)目錄,找到這個(gè)目錄中對(duì)應(yīng)的偏旁部首,就可以通過這個(gè)目錄中的偏旁部首找到這個(gè)字所在的位置(文檔)。
?
?
現(xiàn)在有兩篇文檔:
Doc1: When in Rome, do as the Romans do.
Doc2: When do you come back from Rome?
?
Lucene會(huì)對(duì)以上兩篇文檔建立倒排索引
索引結(jié)構(gòu)如下圖:
?
1、?提取資源中關(guān)鍵信息, 建立索引 (目錄)
2、?搜索時(shí),根據(jù)關(guān)鍵字(目錄),找到資源的位置
?
?
1.1.?搜索技術(shù)應(yīng)用場景
應(yīng)用場景 :
1、 單機(jī)軟件的搜索(word中的搜索)
2、 站內(nèi)搜索 (baidu貼吧、論壇、 京東、 taobao)
3、 垂直領(lǐng)域的搜索 (818工作網(wǎng))
4、 專業(yè)搜索引擎公司 (google、baidu)
?
?
?
?
?
?
?
?
?
?
?
3.?Lucene介紹
3.1.?什么是全文索引
計(jì)算機(jī)索引程序通過掃描文章中的每一個(gè)詞,對(duì)每一個(gè)詞建立一個(gè)索引,指明該詞在文章中出現(xiàn)的次數(shù)和位置,當(dāng)用戶查詢時(shí),檢索程序就根據(jù)事先建立的索引進(jìn)行查找,并將查找的結(jié)果反饋給用戶的檢索方式
?
3.2.?什么是Lucene
Lucene是apache軟件基金會(huì)4 jakarta項(xiàng)目組的一個(gè)子項(xiàng)目,是一個(gè)開放源代碼的全文檢索引擎工具包,但它不是一個(gè)完整的全文檢索引擎,而是一個(gè)全文檢索引擎的架構(gòu),提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。
?
Lucene的目的是為軟件開發(fā)人員提供一個(gè)簡單易用的工具包,以方便的在目標(biāo)系統(tǒng)中實(shí)現(xiàn)全文檢索的功能,或者是以此為基礎(chǔ)建立起完整的全文檢索引擎。
?
目前已經(jīng)有很多應(yīng)用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的幫助系統(tǒng)的搜索功能。Lucene 能夠?yàn)槲谋绢愋偷臄?shù)據(jù)建立索引,所以你只要能把你要索引的數(shù)據(jù)格式轉(zhuǎn)化的文本的,Lucene 就能對(duì)你的文檔進(jìn)行索引和搜索。比如你要對(duì)一些 HTML 文檔,PDF 文檔進(jìn)行索引的話你就首先需要把 HTML 文檔和 PDF 文檔轉(zhuǎn)化成文本格式的,然后將轉(zhuǎn)化后的內(nèi)容交給 Lucene 進(jìn)行索引,然后把創(chuàng)建好的索引文件保存到磁盤或者內(nèi)存中,最后根據(jù)用戶輸入的查詢條件在索引文件上進(jìn)行查詢。不指定要索引的文檔的格式也使 Lucene 能夠幾乎適用于所有的搜索應(yīng)用程序。
?
l?Lucene是一套用于全文檢索和搜尋的開源程式庫,由Apache軟件基金會(huì)支 持和提供
l?Lucene提供了一個(gè)簡單卻強(qiáng)大的應(yīng)用程式接口,能夠做全文索引和搜尋, 在Java開發(fā)環(huán)境里L(fēng)ucene是一個(gè)成熟的免費(fèi)開放源代碼工具
l?Lucene并不是現(xiàn)成的搜索引擎產(chǎn)品,但可以用來制作搜索引擎產(chǎn)品
?
?
3.3.?Lucene與搜索引擎的區(qū)別
全文檢索系統(tǒng)是按照全文檢索理論建立起來的用于提供全文檢索服務(wù)的軟件系統(tǒng),包括建立索引、處理查詢返回結(jié)果集、增加索引、優(yōu)化索引結(jié)構(gòu)等功能。例如:百度搜索、eclipse幫助搜索、淘寶網(wǎng)商品搜索等。
?
搜索引擎是全文檢索技術(shù)最主要的一個(gè)應(yīng)用,例如百度。搜索引擎起源于傳統(tǒng)的信息全文檢索理論,即計(jì)算機(jī)程序通過掃描每一篇文章中的每一個(gè)詞,建立以詞為單位的倒排文件,檢索程序根據(jù)檢索詞在每一篇文章中出現(xiàn)的頻率和每一個(gè)檢索詞在一篇文章中出現(xiàn)的概率,對(duì)包含這些檢索詞的文章進(jìn)行排序,最后輸出排序的結(jié)果。全文檢索技術(shù)是搜索引擎的核心支撐技術(shù)。
?
Lucene和搜索引擎不同,Lucene是一套用java或其它語言寫的全文檢索的工具包,為應(yīng)用程序提供了很多個(gè)api接口去調(diào)用,可以簡單理解為是一套實(shí)現(xiàn)全文檢索的類庫,搜索引擎是一個(gè)全文檢索系統(tǒng),它是一個(gè)單獨(dú)運(yùn)行的軟件系統(tǒng)
?
3.4.?Lucene官網(wǎng)
官網(wǎng): http://lucene.apache.org/?
?
4.?Lucene全文檢索的流程
4.1.?索引和搜索流程圖
?
?
1、綠色表示索引過程,對(duì)要搜索的原始內(nèi)容進(jìn)行索引構(gòu)建一個(gè)索引庫,索引過程包括:
確定原始內(nèi)容即要搜索的內(nèi)容à獲得文檔à創(chuàng)建文檔à分析文檔à索引文檔
?
2、紅色表示搜索過程,從索引庫中搜索內(nèi)容,搜索過程包括:
用戶通過搜索界面à創(chuàng)建查詢à執(zhí)行搜索,從索引庫搜索à渲染搜索結(jié)果
4.2.?索引流程
對(duì)文檔索引的過程,將用戶要搜索的文檔內(nèi)容進(jìn)行索引,索引存儲(chǔ)在索引庫(index)中。
?
4.2.1.?原始內(nèi)容
原始內(nèi)容是指要索引和搜索的內(nèi)容。
原始內(nèi)容包括互聯(lián)網(wǎng)上的網(wǎng)頁、數(shù)據(jù)庫中的數(shù)據(jù)、磁盤上的文件等。
?
4.2.2.?獲得文檔(采集數(shù)據(jù))
從互聯(lián)網(wǎng)上、數(shù)據(jù)庫、文件系統(tǒng)中等獲取需要搜索的原始信息,這個(gè)過程就是信息采集,采集數(shù)據(jù)的目的是為了對(duì)原始內(nèi)容進(jìn)行索引。
?
采集數(shù)據(jù)分類:
1、對(duì)于互聯(lián)網(wǎng)上網(wǎng)頁,可以使用工具將網(wǎng)頁抓取到本地生成html文件。
2、數(shù)據(jù)庫中的數(shù)據(jù),可以直接連接數(shù)據(jù)庫讀取表中的數(shù)據(jù)。
3、文件系統(tǒng)中的某個(gè)文件,可以通過I/O操作讀取文件的內(nèi)容。
?
在Internet上采集信息的軟件通常稱為爬蟲或蜘蛛,也稱為網(wǎng)絡(luò)機(jī)器人,爬蟲訪問互聯(lián)網(wǎng)上的每一個(gè)網(wǎng)頁,將獲取到的網(wǎng)頁內(nèi)容存儲(chǔ)起來。
Lucene不提供信息采集的類庫,需要自己編寫一個(gè)爬蟲程序?qū)崿F(xiàn)信息采集,也可以通過一些開源軟件實(shí)現(xiàn)信息采集,如下:
Solr(http://lucene.apache.org/solr) ,solr是apache的一個(gè)子項(xiàng)目,支持從關(guān)系數(shù)據(jù)庫、xml文檔中提取原始數(shù)據(jù)。
Nutch(http://lucene.apache.org/nutch), Nutch是apache的一個(gè)子項(xiàng)目,包括大規(guī)模爬蟲工具,能夠抓取和分辨web網(wǎng)站數(shù)據(jù)。
jsoup(http://jsoup.org/?),jsoup 是一款Java 的HTML解析器,可直接解析某個(gè)URL地址、HTML文本內(nèi)容。它提供了一套非常省力的API,可通過DOM,CSS以及類似于jQuery的操作方法來取出和操作數(shù)據(jù)。
?
4.2.3.?創(chuàng)建文檔
獲取原始內(nèi)容的目的是為了索引,在索引前需要將原始內(nèi)容創(chuàng)建成文檔(Document),文檔中包括一個(gè)一個(gè)的域(Field),域中存儲(chǔ)內(nèi)容。
這里我們可以將磁盤上的一個(gè)文件當(dāng)成一個(gè)document,Document中包括一些Field,如下圖:
?
?
注意:每個(gè)Document可以有多個(gè)Field,不同的Document可以有不同的Field,同一個(gè)Document可以有相同的Field(域名和域值都相同)
4.2.4.?分析文檔
將原始內(nèi)容創(chuàng)建為包含域(Field)的文檔(document),需要再對(duì)域中的內(nèi)容進(jìn)行分析,分析成為一個(gè)一個(gè)的單詞。
?
比如下邊的文檔經(jīng)過分析如下:
原文檔內(nèi)容:
Lucene is a Java full-text search engine. ?Lucene is not a complete
application, but rather a code library and API that can easily be used
to add search capabilities to applications.
?
分析后得到的詞:
lucene、java、full、search、engine。。。。
?
4.2.5.?索引文檔
對(duì)所有文檔分析得出的語匯單元進(jìn)行索引,索引的目的是為了搜索,最終要實(shí)現(xiàn)只搜索被索引的語匯單元從而找到Document(文檔)。
?
創(chuàng)建索引是對(duì)語匯單元索引,通過詞語找文檔,這種索引的結(jié)構(gòu)叫倒排索引結(jié)構(gòu)。
?
倒排索引結(jié)構(gòu)是根據(jù)內(nèi)容(詞匯)找文檔,如下圖:
?
?
?
倒排索引結(jié)構(gòu)也叫反向索引結(jié)構(gòu),包括索引和文檔兩部分,索引即詞匯表,它的規(guī)模較小,而文檔集合較大。
?
4.3.?搜索流程
搜索就是用戶輸入關(guān)鍵字,從索引中進(jìn)行搜索的過程。根據(jù)關(guān)鍵字搜索索引,根據(jù)索引找到對(duì)應(yīng)的文檔,從而找到要搜索的內(nèi)容。
?
4.3.1.?用戶
就是使用搜索的角色,用戶可以是自然人,也可以是遠(yuǎn)程調(diào)用的程序。
?
4.3.2.?用戶搜索界面
全文檢索系統(tǒng)提供用戶搜索的界面供用戶提交搜索的關(guān)鍵字,搜索完成展示搜索結(jié)果。如下圖:
?
Lucene不提供制作用戶搜索界面的功能,需要根據(jù)自己的需求開發(fā)搜索界面。
?
4.3.3.?創(chuàng)建查詢
用戶輸入查詢關(guān)鍵字執(zhí)行搜索之前需要先構(gòu)建一個(gè)查詢對(duì)象,查詢對(duì)象中可以指定查詢要查詢關(guān)鍵字、要搜索的Field文檔域等,查詢對(duì)象會(huì)生成具體的查詢語法,比如:
name:lucene表示要搜索name這個(gè)Field域中,內(nèi)容為“lucene”的文檔。
desc:lucene AND desc:java 表示要搜索既包括關(guān)鍵字“lucene”也包括“java”的文檔。
?
4.3.4.?執(zhí)行搜索
搜索索引過程:
1.根據(jù)查詢語法在倒排索引詞典表中分別找出對(duì)應(yīng)搜索詞的索引,從而找到索引所鏈接的文檔鏈表。
例如搜索語法為“desc:lucene AND desc:java”表示搜索出的文檔中既要包括lucene也要包括java。
?
?
2、由于是AND,所以要對(duì)包含lucene或java詞語的鏈表進(jìn)行交集,得到文檔鏈表應(yīng)該包括每一個(gè)搜索詞語
?
3、獲取文檔中的Field域數(shù)據(jù)。
?
4.3.5.?渲染結(jié)果
以一個(gè)友好的界面將查詢結(jié)果展示給用戶,用戶根據(jù)搜索結(jié)果找自己想要的信息,為了幫助用戶很快找到自己的結(jié)果,提供了很多展示的效果,比如搜索結(jié)果中將關(guān)鍵字高亮顯示,百度提供的快照等。
?
5.?Lucene入門
5.1.?Lucene準(zhǔn)備
Lucene可以在官網(wǎng)上下載。課程已經(jīng)準(zhǔn)備好了Lucene的文件,我們使用的是4.10.3版本,文件位置如下圖:
?
解壓后的效果:
?
使用這三個(gè)文件的jar包,就可以實(shí)現(xiàn)lucene功能
?
本教程使用的數(shù)據(jù)是MySQL數(shù)據(jù)庫的數(shù)據(jù),所以還需要MySQL的連接包
學(xué)員編寫的時(shí)候,也可以直接復(fù)制準(zhǔn)備好的jar包,位置如下圖:
?
5.2.?開發(fā)環(huán)境
JDK: 1.7 (Lucene4.8以上,必須使用JDK1.7及以上版本)
IDE: eclipse Mars2
數(shù)據(jù)庫: MySQL
?
數(shù)據(jù)庫腳本位置如下圖:
?
?
?
導(dǎo)入到MySQL效果如下圖:
?
?
5.3.?創(chuàng)建Java工程
創(chuàng)建java工程測試即可,效果如下:
?
?
?
5.4.?索引流程
5.4.1.?數(shù)據(jù)采集
在電商網(wǎng)站中,全文檢索的數(shù)據(jù)源在數(shù)據(jù)庫中,需要通過jdbc訪問數(shù)據(jù)庫中book表的內(nèi)容。
5.4.1.1.?創(chuàng)建pojo
public?class?Book {
// 圖書ID
private?Integer id;
// 圖書名稱
private?String name;
// 圖書價(jià)格
private?Float price;
// 圖書圖片
private?String pic;
// 圖書描述
private?String desc;
get/set。。。
}
?
5.4.1.2.?創(chuàng)建DAO接口
public?interface?BookDao {
?
/**
?* 查詢所有的book數(shù)據(jù)
?*
?* @return
?*/
List<Book> queryBookList();
}
?
5.4.1.3.?創(chuàng)建DAO接口實(shí)現(xiàn)類
使用jdbc實(shí)現(xiàn)
public?class?BookDaoImpl implements?BookDao {
?
@Override
public?List<Book> queryBookList() {
// 數(shù)據(jù)庫鏈接
Connection connection?= null;
// 預(yù)編譯statement
PreparedStatement preparedStatement?= null;
// 結(jié)果集
ResultSet resultSet?= null;
// 圖書列表
List<Book> list?= new?ArrayList<Book>();
?
try?{
// 加載數(shù)據(jù)庫驅(qū)動(dòng)
Class.forName("com.mysql.jdbc.Driver");
// 連接數(shù)據(jù)庫
connection?= DriverManager.getConnection("jdbc:mysql://localhost:3306/solr", "root", "root");
?
// SQL語句
String sql?= "SELECT * FROM book";
// 創(chuàng)建preparedStatement
preparedStatement?= connection.prepareStatement(sql);
// 獲取結(jié)果集
resultSet?= preparedStatement.executeQuery();
// 結(jié)果集解析
while?(resultSet.next()) {
Book book?= new?Book();
book.setId(resultSet.getInt("id"));
book.setName(resultSet.getString("name"));
book.setPrice(resultSet.getFloat("price"));
book.setPic(resultSet.getString("pic"));
book.setDesc(resultSet.getString("desc"));
list.add(book);
}
} catch?(Exception e) {
e.printStackTrace();
}
?
return?list;
}
}
?
5.4.2.?實(shí)現(xiàn)索引流程
public?class?CreateIndexTest {
@Test
public?void?testCreateIndex() throws?Exception {
// 1. 采集數(shù)據(jù)
BookDao bookDao?= new?BookDaoImpl();
List<Book> bookList?= bookDao.queryBookList();
?
// 2. 創(chuàng)建Document文檔對(duì)象
List<Document> documents?= new?ArrayList<>();
for?(Book book?: bookList) {
Document document?= new?Document();
?
// Document文檔中添加Field域
// 圖書Id
// Store.YES:表示存儲(chǔ)到文檔域中
document.add(new?TextField("id", book.getId().toString(), Store.YES));
// 圖書名稱
document.add(new?TextField("name", book.getName().toString(), Store.YES));
// 圖書價(jià)格
document.add(new?TextField("price", book.getPrice().toString(), Store.YES));
// 圖書圖片地址
document.add(new?TextField("pic", book.getPic().toString(), Store.YES));
// 圖書描述
document.add(new?TextField("desc", book.getDesc().toString(), Store.YES));
?
// 把Document放到list中
documents.add(document);
}
?
// 3. 創(chuàng)建Analyzer分詞器,分析文檔,對(duì)文檔進(jìn)行分詞
Analyzer analyzer?= new?StandardAnalyzer();
?
// 4. 創(chuàng)建Directory對(duì)象,聲明索引庫的位置
Directory directory?= FSDirectory.open(new?File("C:/itcast/lucene/index"));
?
// 5. 創(chuàng)建IndexWriteConfig對(duì)象,寫入索引需要的配置
IndexWriterConfig config?= new?IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
?
// 6.創(chuàng)建IndexWriter寫入對(duì)象
IndexWriter indexWriter?= new?IndexWriter(directory, config);
?
// 7.寫入到索引庫,通過IndexWriter添加文檔對(duì)象document
for?(Document doc?: documents) {
indexWriter.addDocument(doc);
}
?
// 8.釋放資源
indexWriter.close();
}
}
?
執(zhí)行效果:
在文件夾中出現(xiàn)了以下文件,表示創(chuàng)建索引成功
?
?
?
5.4.3.?使用Luke查看索引
Luke作為Lucene工具包中的一個(gè)工具(http://www.getopt.org/luke/),可以通過界面來進(jìn)行索引文件的查詢、修改
luke所在位置如下圖:
?
?
?
打開Luke方法:打開cmd命令行運(yùn)行命令:java -jar lukeall-4.10.3.jar
打開后,使用如下圖:
?
?
下圖是索引域的展示效果:
?
?
下圖是文檔域展示效果
?
?
?
5.5.?搜索流程
5.5.1.?輸入查詢語句
Lucene可以通過query對(duì)象輸入查詢語句。同數(shù)據(jù)庫的sql一樣,lucene也有固定的查詢語法:
最基本的有比如:AND, OR, NOT 等(必須大寫)
?
舉個(gè)栗子:
用戶想找一個(gè)desc中包括java關(guān)鍵字和lucene關(guān)鍵字的文檔。
它對(duì)應(yīng)的查詢語句:desc:java?AND desc:lucene
?
?
?
?
?
?
?
?
如下圖是使用luke搜索的例子:
?
?
?
5.5.1.1.?搜索分詞
和索引過程的分詞一樣,這里要對(duì)用戶輸入的關(guān)鍵字進(jìn)行分詞,一般情況索引和搜索使用的分詞器一致。
比如:輸入搜索關(guān)鍵字“java學(xué)習(xí)”,分詞后為java和學(xué)習(xí)兩個(gè)詞,與java和學(xué)習(xí)有關(guān)的內(nèi)容都搜索出來了,如下:
?
?
?
5.5.2.?代碼實(shí)現(xiàn)
?1. 創(chuàng)建Query搜索對(duì)象
?2. 創(chuàng)建Directory流對(duì)象,聲明索引庫位置
?3. 創(chuàng)建索引讀取對(duì)象IndexReader
?4. 創(chuàng)建索引搜索對(duì)象IndexSearcher
?5. 使用索引搜索對(duì)象,執(zhí)行搜索,返回結(jié)果集TopDocs
?6. 解析結(jié)果集
?7. 釋放資源
?
IndexSearcher搜索方法如下:
| 方法 | 說明 |
| indexSearcher.search(query, n) | 根據(jù)Query搜索,返回評(píng)分最高的n條記錄 |
| indexSearcher.search(query, filter, n) | 根據(jù)Query搜索,添加過濾策略,返回評(píng)分最高的n條記錄 |
| indexSearcher.search(query, n, sort) | 根據(jù)Query搜索,添加排序策略,返回評(píng)分最高的n條記錄 |
| indexSearcher.search(booleanQuery, filter, n, sort) | 根據(jù)Query搜索,添加過濾策略,添加排序策略,返回評(píng)分最高的n條記錄 |
?
代碼實(shí)現(xiàn)
public?class?SearchIndexTest {
@Test
public?void?testSearchIndex() throws?Exception {
// 1. 創(chuàng)建Query搜索對(duì)象
// 創(chuàng)建分詞器
Analyzer analyzer?= new?StandardAnalyzer();
// 創(chuàng)建搜索解析器,第一個(gè)參數(shù):默認(rèn)Field域,第二個(gè)參數(shù):分詞器
QueryParser queryParser?= new?QueryParser("desc", analyzer);
?
// 創(chuàng)建搜索對(duì)象
Query query?= queryParser.parse("desc:java AND lucene");
?
// 2. 創(chuàng)建Directory流對(duì)象,聲明索引庫位置
Directory directory?= FSDirectory.open(new?File("C:/itcast/lucene/index"));
?
// 3. 創(chuàng)建索引讀取對(duì)象IndexReader
IndexReader reader?= DirectoryReader.open(directory);
?
// 4. 創(chuàng)建索引搜索對(duì)象
IndexSearcher searcher?= new?IndexSearcher(reader);
?
// 5. 使用索引搜索對(duì)象,執(zhí)行搜索,返回結(jié)果集TopDocs
// 第一個(gè)參數(shù):搜索對(duì)象,第二個(gè)參數(shù):返回的數(shù)據(jù)條數(shù),指定查詢結(jié)果最頂部的n條數(shù)據(jù)返回
TopDocs topDocs?= searcher.search(query, 10);
System.out.println("查詢到的數(shù)據(jù)總條數(shù)是:"?+ topDocs.totalHits);
// 獲取查詢結(jié)果集
ScoreDoc[] docs?= topDocs.scoreDocs;
?
// 6. 解析結(jié)果集
for?(ScoreDoc scoreDoc?: docs) {
// 獲取文檔
int?docID?= scoreDoc.doc;
Document doc?= searcher.doc(docID);
?
System.out.println("=============================");
System.out.println("docID:"?+ docID);
System.out.println("bookId:"?+ 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("desc:" + doc.get("desc"));
}
// 7. 釋放資源
reader.close();
}
}
?
?
?
?
?
6.?分詞器
6.1.?分詞理解
在對(duì)Docuemnt中的內(nèi)容進(jìn)行索引之前,需要使用分詞器進(jìn)行分詞 ,分詞的目的是為了搜索。分詞的主要過程就是先分詞后過濾。
?
l?分詞:采集到的數(shù)據(jù)會(huì)存儲(chǔ)到document對(duì)象的Field域中,分詞就是將Document中Field的value值切分成一個(gè)一個(gè)的詞。
l?過濾:包括去除標(biāo)點(diǎn)符號(hào)過濾、去除停用詞過濾(的、是、a、an、the等)、大寫轉(zhuǎn)小寫、詞的形還原(復(fù)數(shù)形式轉(zhuǎn)成單數(shù)形參、過去式轉(zhuǎn)成現(xiàn)在式。。。)等。?
?
什么是停用詞?停用詞是為節(jié)省存儲(chǔ)空間和提高搜索效率,搜索引擎在索引頁面或處理搜索請(qǐng)求時(shí)會(huì)自動(dòng)忽略某些字或詞,這些字或詞即被稱為Stop Words(停用詞)。比如語氣助詞、副詞、介詞、連接詞等,通常自身并無明確的意義,只有將其放入一個(gè)完整的句子中才有一定作用,如常見的“的”、“在”、“是”、“啊”等。
?
對(duì)于分詞來說,不同的語言,分詞規(guī)則不同。Lucene作為一個(gè)工具包提供不同國家的分詞器,本例子使用StandardAnalyzer,它可以對(duì)用英文進(jìn)行分詞。
?
如下是org.apache.lucene.analysis.standard.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);
??????}
????};
??}
?
Tokenizer就是分詞器,負(fù)責(zé)將reader轉(zhuǎn)換為語匯單元即進(jìn)行分詞處理,Lucene提供了很多的分詞器,也可以使用第三方的分詞,比如IKAnalyzer一個(gè)中文分詞器。
?
TokenFilter是分詞過濾器,負(fù)責(zé)對(duì)語匯單元進(jìn)行過濾,TokenFilter可以是一個(gè)過濾器鏈兒,Lucene提供了很多的分詞器過濾器,比如大小寫轉(zhuǎn)換、去除停用詞等。
?
如下圖是語匯單元的生成過程:
?
?
?
從一個(gè)Reader字符流開始,創(chuàng)建一個(gè)基于Reader的Tokenizer分詞器,經(jīng)過三個(gè)TokenFilter生成語匯單元Token。
?
比如下邊的文檔經(jīng)過分析器分析如下:
- 原文檔內(nèi)容:
?
?
?
?
- 分析后得到的多個(gè)語匯單元:
?
?
?
?
?
6.2.?Analyzer使用時(shí)機(jī)
6.2.1.?索引時(shí)使用Analyzer
輸入關(guān)鍵字進(jìn)行搜索,當(dāng)需要讓該關(guān)鍵字與文檔域內(nèi)容所包含的詞進(jìn)行匹配時(shí)需要對(duì)文檔域內(nèi)容進(jìn)行分析,需要經(jīng)過Analyzer分析器處理生成語匯單元(Token)。分析器分析的對(duì)象是文檔中的Field域。當(dāng)Field的屬性tokenized(是否分詞)為true時(shí)會(huì)對(duì)Field值進(jìn)行分析,如下圖:
?
?
?
對(duì)于一些Field可以不用分析:
1、不作為查詢條件的內(nèi)容,比如文件路徑
2、不是匹配內(nèi)容中的詞而匹配Field的整體內(nèi)容,比如訂單號(hào)、身份證號(hào)等。
?
?
6.2.2.?搜索時(shí)使用Analyzer
對(duì)搜索關(guān)鍵字進(jìn)行分析和索引分析一樣,使用Analyzer對(duì)搜索關(guān)鍵字進(jìn)行分析、分詞處理,使用分析后每個(gè)詞語進(jìn)行搜索。比如:搜索關(guān)鍵字:spring web ,經(jīng)過分析器進(jìn)行分詞,得出:spring??web拿詞去索引詞典表查找 ,找到索引鏈接到Document,解析Document內(nèi)容。
對(duì)于匹配整體Field域的查詢可以在搜索時(shí)不分析,比如根據(jù)訂單號(hào)、身份證號(hào)查詢等。
注意:搜索使用的分析器要和索引使用的分析器一致。
?
6.3.?中文分詞器
6.3.1.?什么是中文分詞器
學(xué)過英文的都知道,英文是以單詞為單位的,單詞與單詞之間以空格或者逗號(hào)句號(hào)隔開。所以對(duì)于英文,我們可以簡單以空格判斷某個(gè)字符串是否為一個(gè)單詞,比如I love China,love 和 China很容易被程序區(qū)分開來。
?
而中文則以字為單位,字又組成詞,字和詞再組成句子。中文“我愛中國”就不一樣了,電腦不知道“中國”是一個(gè)詞語還是“愛中”是一個(gè)詞語。
?
把中文的句子切分成有意義的詞,就是中文分詞,也稱切詞。我愛中國,分詞的結(jié)果是:我、愛、中國。
?
6.3.2.?Lucene自帶中文分詞器
l?StandardAnalyzer:
單字分詞:就是按照中文一個(gè)字一個(gè)字地進(jìn)行分詞。如:“我愛中國”,
效果:“我”、“愛”、“中”、“國”。
?
l?CJKAnalyzer
二分法分詞:按兩個(gè)字進(jìn)行切分。如:“我是中國人”,效果:“我是”、“是中”、“中國”“國人”。
?
上邊兩個(gè)分詞器無法滿足需求。
l?SmartChineseAnalyzer
對(duì)中文支持較好,但擴(kuò)展性差,擴(kuò)展詞庫,禁用詞庫和同義詞庫等不好處理
?
6.4.?第三方中文分詞器
l?paoding: 庖丁解牛最新版在?https://code.google.com/p/paoding/?中最多支持Lucene 3.0,且最新提交的代碼在 2008-06-03,在svn中最新也是2010年提交,已經(jīng)過時(shí),不予考慮。
?
l?mmseg4j:最新版已從?https://code.google.com/p/mmseg4j/?移至?https://github.com/chenlb/mmseg4j-solr,支持Lucene 4.10,且在github中最新提交代碼是2014年6月,從09年~14年一共有:18個(gè)版本,也就是一年幾乎有3個(gè)大小版本,有較大的活躍度,用了mmseg算法。
?
l?IK-analyzer: 最新版在https://code.google.com/p/ik-analyzer/上,支持Lucene 4.10從2006年12月推出1.0版開始, IKAnalyzer已經(jīng)推出了4個(gè)大版本。最初,它是以開源項(xiàng)目Luence為應(yīng)用主體的,結(jié)合詞典分詞和文法分析算法的中文分詞組件。從3.0版本開 始,IK發(fā)展為面向Java的公用分詞組件,獨(dú)立于Lucene項(xiàng)目,同時(shí)提供了對(duì)Lucene的默認(rèn)優(yōu)化實(shí)現(xiàn)。在2012版本中,IK實(shí)現(xiàn)了簡單的分詞 歧義排除算法,標(biāo)志著IK分詞器從單純的詞典分詞向模擬語義分詞衍化。 但是也就是2012年12月后沒有在更新。
?
l?ansj_seg:最新版本在?https://github.com/NLPchina/ansj_seg?tags僅有1.1版本,從2012年到2014年更新了大小6次,但是作者本人在2014年10月10日說明:“可能我以后沒有精力來維護(hù)ansj_seg了”,現(xiàn)在由”nlp_china”管理。2014年11月有更新。并未說明是否支持Lucene,是一個(gè)由CRF(條件隨機(jī)場)算法所做的分詞算法。
?
l?imdict-chinese-analyzer:最新版在?https://code.google.com/p/imdict-chinese-analyzer/?, 最新更新也在2009年5月,下載源碼,不支持Lucene 4.10 。是利用HMM(隱馬爾科夫鏈)算法。
?
l?Jcseg:最新版本在git.oschina.net/lionsoul/jcseg,支持Lucene 4.10,作者有較高的活躍度。利用mmseg算法。
?
6.5.?使用中文分詞器IKAnalyzer
IKAnalyzer繼承Lucene的Analyzer抽象類,使用IKAnalyzer和Lucene自帶的分析器方法一樣,將Analyzer測試代碼改為IKAnalyzer測試中文分詞效果。
?
如果使用中文分詞器ik-analyzer,就需要在索引和搜索程序中使用一致的分詞器:IK-analyzer。
6.5.1.?添加jar包
?
?
?
6.5.2.?修改分詞器代碼
@Test
public?void?testCreateIndex() throws?Exception {
// 1. 采集數(shù)據(jù)
BookDao bookDao?= new?BookDaoImpl();
List<Book> bookList?= bookDao.queryBookList();
?
// 2. 創(chuàng)建Document文檔對(duì)象
List<Document> documents?= new?ArrayList<>();
for?(Book book?: bookList) {
Document document?= new?Document();
?
// Document文檔中添加Field域
// 圖書Id
// Store.YES:表示存儲(chǔ)到文檔域中
document.add(new?TextField("id", book.getId().toString(), Store.YES));
// 圖書名稱
document.add(new?TextField("name", book.getName().toString(), Store.YES));
// 圖書價(jià)格
document.add(new?TextField("price", book.getPrice().toString(), Store.YES));
// 圖書圖片地址
document.add(new?TextField("pic", book.getPic().toString(), Store.YES));
// 圖書描述
document.add(new?TextField("desc", book.getDesc().toString(), Store.YES));
?
// 把Document放到list中
documents.add(document);
}
?
// 3. 創(chuàng)建Analyzer分詞器,分析文檔,對(duì)文檔進(jìn)行分詞
// Analyzer analyzer = new StandardAnalyzer();
Analyzer analyzer?= new?IKAnalyzer();
?
// 4. 創(chuàng)建Directory對(duì)象,聲明索引庫的位置
Directory directory?= FSDirectory.open(new?File("C:/itcast/lucene/index"));
?
// 5. 創(chuàng)建IndexWriteConfig對(duì)象,寫入索引需要的配置
IndexWriterConfig config?= new?IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
?
// 6.創(chuàng)建IndexWriter寫入對(duì)象
IndexWriter indexWriter?= new?IndexWriter(directory, config);
?
// 7.寫入到索引庫,通過IndexWriter添加文檔對(duì)象document
for?(Document doc?: documents) {
indexWriter.addDocument(doc);
}
?
// 8.釋放資源
indexWriter.close();
}
?
6.6.?擴(kuò)展中文詞庫
如果想配置擴(kuò)展詞和停用詞,就創(chuàng)建擴(kuò)展詞的文件和停用詞的文件。
注意:不要用window自帶的記事本保存擴(kuò)展詞文件和停用詞文件,那樣的話,格式中是含有bom的。
?
?
?
?
?
?
?
?
從ikanalyzer包中拷貝配置文件
?
?
?
拷貝到資源文件夾中
?
?
?
IKAnalyzer.cfg.xml配置文件
<?xml?version="1.0"?encoding="UTF-8"?>
<!DOCTYPE?properties?SYSTEM?"http://java.sun.com/dtd/properties.dtd">??
<properties>??
<comment>IK Analyzer 擴(kuò)展配置</comment>
<!--用戶可以在這里配置自己的擴(kuò)展字典 -->
<entry?key="ext_dict">ext.dic;</entry>?
?
<!--用戶可以在這里配置自己的擴(kuò)展停止詞字典-->
<entry?key="ext_stopwords">stopword.dic;</entry>?
?
</properties>
?
中文詞庫,添加新詞的地方
?
?
?
stopword.dic是存放停用詞的地方
?
?
?
最終分詞效果
?
轉(zhuǎn)載于:https://www.cnblogs.com/shan1393/p/9343630.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的lucene 第一天的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对事务的特性ACID的理解
- 下一篇: STL容器底层数据结构的实现