您好,登錄后才能下訂單哦!
這篇文章主要講解了“Lucene基礎原理是什么”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Lucene基礎原理是什么”吧!
一、總論
根據 lucene.apache.org/java/docs/i…定義:
Lucene是一個高效的,基于Java的全文檢索庫。
所以在了解Lucene之前要費一番工夫了解一下全文檢索。
那么什么叫做全文檢索呢?這要從我們生活中的數據說起。
我們生活中的數據總體分為兩種:結構化數據和非結構化數據。
結構化數據:指具有固定格式或有限長度的數據,如數據庫,元數據等。
非結構化數據:指不定長或無固定格式的數據,如郵件,word文檔等。
當然有的地方還會提到第三種,半結構化數據,如XML,HTML等,當根據需要可按結構化數據來處理,也可抽取出純文本按非結構化數據來處理。
非結構化數據又一種叫法叫全文數據。
按照數據的分類,搜索也分為兩種:
對結構化數據的搜索:如對數據庫的搜索,用SQL語句。再如對元數據的搜索,如利用windows搜索對文件名,類型,修改時間進行搜索等。
對非結構化數據的搜索:如利用windows的搜索也可以搜索文件內容,Linux下的grep命令,再如用Google和百度可以搜索大量內容數據。
對非結構化數據也即對全文數據的搜索主要有兩種方法:
一種是順序掃描法(Serial Scanning):所謂順序掃描,比如要找內容包含某一個字符串的文件,就是一個文檔一個文檔的看,對于每一個文檔,從頭看到尾,如果此文檔包含此字符串,則此文檔為我們要找的文件,接著看下一個文件,直到掃描完所有的文件。如利用windows的搜索也可以搜索文件內容,只是相當的慢。如果你有一個80G硬盤,如果想在上面找到一個內容包含某字符串的文件,不花他幾個小時,怕是做不到。Linux下的grep命令也是這一種方式。大家可能覺得這種方法比較原始,但對于小數據量的文件,這種方法還是最直接,最方便的。但是對于大量的文件,這種方法就很慢了。
有人可能會說,對非結構化數據順序掃描很慢,對結構化數據的搜索卻相對較快(由于結構化數據有一定的結構可以采取一定的搜索算法加快速度),那么把我們的非結構化數據想辦法弄得有一定結構不就行了嗎?
這種想法很天然,卻構成了全文檢索的基本思路,也即將非結構化數據中的一部分信息提取出來,重新組織,使其變得有一定結構,然后對此有一定結構的數據進行搜索,從而達到搜索相對較快的目的。
這部分從非結構化數據中提取出的然后重新組織的信息,我們稱之索引。
這種說法比較抽象,舉幾個例子就很容易明白,比如字典,字典的拼音表和部首檢字表就相當于字典的索引,對每一個字的解釋是非結構化的,如果字典沒有音節表和部首檢字表,在茫茫辭海中找一個字只能順序掃描。然而字的某些信息可以提取出來進行結構化處理,比如讀音,就比較結構化,分聲母和韻母,分別只有幾種可以一一列舉,于是將讀音拿出來按一定的順序排列,每一項讀音都指向此字的詳細解釋的頁數。我們搜索時按結構化的拼音搜到讀音,然后按其指向的頁數,便可找到我們的非結構化數據——也即對字的解釋。
這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)。
下面這幅圖來自《Lucene in action》,但卻不僅僅描述了Lucene的檢索過程,而是描述了全文檢索的一般過程。
cdn.xitu.io/2016/11/29/a3cea9bc799d798340ded4e405204c4b.jpg?imageView2/0/w/1280/h/960/format/webp/ignore-error/1">
全文檢索大體分兩個過程,索引創建(Indexing)和搜索索引(Search)。
索引創建:將現實世界中所有的結構化和非結構化數據提取信息,創建索引的過程。
搜索索引:就是得到用戶的查詢請求,搜索創建的索引,然后返回結果的過程。
于是全文檢索就存在三個重要問題:
索引里面究竟存些什么?(Index)
如何創建索引?(Indexing)
如何對索引進行搜索?(Search)
下面我們順序對每個個問題進行研究。
索引里面究竟需要存些什么呢?
首先我們來看為什么順序掃描的速度慢:
其實是由于我們想要搜索的信息和非結構化數據中所存儲的信息不一致造成的。
非結構化數據中所存儲的信息是每個文件包含哪些字符串,也即已知文件,欲求字符串相對容易,也即是從文件到字符串的映射。而我們想搜索的信息是哪些文件包含此字符串,也即已知字符串,欲求文件,也即從字符串到文件的映射。兩者恰恰相反。于是如果索引總能夠保存從字符串到文件的映射,則會大大提高搜索速度。
由于從字符串到文件的映射是文件到字符串映射的反向過程,于是保存這種信息的索引稱為反向索引。
反向索引的所保存的信息一般如下:
假設我的文檔集合里面有100篇文檔,為了方便表示,我們為文檔編號從1到100,得到下面的結構
左邊保存的是一系列字符串,稱為詞典。
每個字符串都指向包含此字符串的文檔(Document)鏈表,此文檔鏈表稱為倒排表(Posting List)。
有了索引,便使保存的信息和要搜索的信息一致,可以大大加快搜索的速度。
比如說,我們要尋找既包含字符串“lucene”又包含字符串“solr”的文檔,我們只需要以下幾步:
1. 取出包含字符串“lucene”的文檔鏈表。
2. 取出包含字符串“solr”的文檔鏈表。
3. 通過合并鏈表,找出既包含“lucene”又包含“solr”的文件。
看到這個地方,有人可能會說,全文檢索的確加快了搜索的速度,但是多了索引的過程,兩者加起來不一定比順序掃描快多少。的確,加上索引的過程,全文檢索不一定比順序掃描快,尤其是在數據量小的時候更是如此。而對一個很大量的數據創建索引也是一個很慢的過程。
然而兩者還是有區別的,順序掃描是每次都要掃描,而創建索引的過程僅僅需要一次,以后便是一勞永逸的了,每次搜索,創建索引的過程不必經過,僅僅搜索創建好的索引就可以了。
這也是全文搜索相對于順序掃描的優勢之一:一次索引,多次使用。
全文檢索的索引創建過程一般有以下幾步:
為了方便說明索引創建過程,這里特意用兩個文件為例:
文件一:Students should be allowed to go out with their friends, but not allowed to drink beer.
文件二:My friend Jerry went to school to see his students but found them drunk which is not allowed.
分詞組件(Tokenizer)會做以下幾件事情(此過程稱為Tokenize):
將文檔分成一個一個單獨的單詞。
去除標點符號。
去除停詞(Stop word)。
所謂停詞(Stop word)就是一種語言中最普通的一些單詞,由于沒有特別的意義,因而大多數情況下不能成為搜索的關鍵詞,因而創建索引時,這種詞會被去掉而減少索引的大小。
英語中挺詞(Stop word)如:“the”,“a”,“this”等。
對于每一種語言的分詞組件(Tokenizer),都有一個停詞(stop word)集合。
經過分詞(Tokenizer)后得到的結果稱為詞元(Token)。
在我們的例子中,便得到以下詞元(Token):
“Students”,“allowed”,“go”,“their”,“friends”,“allowed”,“drink”,“beer”,“My”,“friend”,“Jerry”,“went”,“school”,“see”,“his”,“students”,“found”,“them”,“drunk”,“allowed”。
語言處理組件(linguistic processor)主要是對得到的詞元(Token)做一些同語言相關的處理。
對于英語,語言處理組件(Linguistic Processor)一般做以下幾點:
變為小寫(Lowercase)。
將單詞縮減為詞根形式,如“cars”到“car”等。這種操作稱為:stemming。
將單詞轉變為詞根形式,如“drove”到“drive”等。這種操作稱為:lemmatization。
Stemming 和 lemmatization的異同:
相同之處:Stemming和lemmatization都要使詞匯成為詞根形式。
兩者的方式不同:
Stemming采用的是“縮減”的方式:“cars”到“car”,“driving”到“drive”。
Lemmatization采用的是“轉變”的方式:“drove”到“drove”,“driving”到“drive”。
兩者的算法不同:
Stemming主要是采取某種固定的算法來做這種縮減,如去除“s”,去除“ing”加“e”,將“ational”變為“ate”,將“tional”變為“tion”。
Lemmatization主要是采用保存某種字典的方式做這種轉變。比如字典中有“driving”到“drive”,“drove”到“drive”,“am, is, are”到“be”的映射,做轉變時,只要查字典就可以了。
Stemming和lemmatization不是互斥關系,是有交集的,有的詞利用這兩種方式都能達到相同的轉換。
語言處理組件(linguistic processor)的結果稱為詞(Term)。
在我們的例子中,經過語言處理,得到的詞(Term)如下:
“student”,“allow”,“go”,“their”,“friend”,“allow”,“drink”,“beer”,“my”,“friend”,“jerry”,“go”,“school”,“see”,“his”,“student”,“find”,“them”,“drink”,“allow”。
也正是因為有語言處理的步驟,才能使搜索drove,而drive也能被搜索出來。
索引組件(Indexer)主要做以下幾件事情:
1. 利用得到的詞(Term)創建一個字典。
在我們的例子中字典如下:
Term | Document ID |
---|---|
student | 1 |
allow | 1 |
go | 1 |
their | 1 |
friend | 1 |
allow | 1 |
drink | 1 |
beer | 1 |
my | 2 |
friend | 2 |
jerry | 2 |
go | 2 |
school | 2 |
see | 2 |
his | 2 |
student | 2 |
find | 2 |
them | 2 |
drink | 2 |
allow | 2 |
對字典按字母順序進行排序。
Term | Document ID |
---|---|
allow | 1 |
allow | 1 |
allow | 2 |
beer | 1 |
drink | 1 |
drink | 2 |
find | 2 |
friend | 1 |
friend | 2 |
go | 1 |
go | 2 |
his | 2 |
jerry | 2 |
my | 2 |
school | 2 |
see | 2 |
student | 1 |
student | 2 |
their | 1 |
them | 2 |
合并相同的詞(Term)成為文檔倒排(Posting List)鏈表。
在此表中,有幾個定義:
Document Frequency 即文檔頻次,表示總共有多少文件包含此詞(Term)。
Frequency 即詞頻率,表示此文件中包含了幾個此詞(Term)。
所以對詞(Term) “allow”來講,總共有兩篇文檔包含此詞(Term),從而詞(Term)后面的文檔鏈表總共有兩項,第一項表示包含“allow”的第一篇文檔,即1號文檔,此文檔中,“allow”出現了2次,第二項表示包含“allow”的第二個文檔,是2號文檔,此文檔中,“allow”出現了1次。
到此為止,索引已經創建好了,我們可以通過它很快的找到我們想要的文檔。
而且在此過程中,我們驚喜地發現,搜索“drive”,“driving”,“drove”,“driven”也能夠被搜到。因為在我們的索引中,“driving”,“drove”,“driven”都會經過語言處理而變成“drive”,在搜索時,如果您輸入“driving”,輸入的查詢語句同樣經過我們這里的一到三步,從而變為查詢“drive”,從而可以搜索到想要的文檔。
到這里似乎我們可以宣布“我們找到想要的文檔了”。
然而事情并沒有結束,找到了僅僅是全文檢索的一個方面。不是嗎?如果僅僅只有一個或十個文檔包含我們查詢的字符串,我們的確找到了。然而如果結果有一千個,甚至成千上萬個呢?那個又是您最想要的文件呢?
打開Google吧,比如說您想在微軟找份工作,于是您輸入“Microsoft job”,您卻發現總共有22600000個結果返回。好大的數字呀,突然發現找不到是一個問題,找到的太多也是一個問題。在如此多的結果中,如何將最相關的放在最前面呢?
當然Google做的很不錯,您一下就找到了jobs at Microsoft。想象一下,如果前幾個全部是“Microsoft does a good job at software industry…”將是多么可怕的事情呀。
如何像Google一樣,在成千上萬的搜索結果中,找到和查詢語句最相關的呢?
如何判斷搜索出的文檔和查詢語句的相關性呢?
這要回到我們第三個問題:如何對索引進行搜索?
搜索主要分為以下幾步:
查詢語句同我們普通的語言一樣,也是有一定語法的。
不同的查詢語句有不同的語法,如SQL語句就有一定的語法。
查詢語句的語法根據全文檢索系統的實現而不同。最基本的有比如:AND, OR, NOT等。
舉個例子,用戶輸入語句:lucene AND learned NOT hadoop。
說明用戶想找一個包含lucene和learned然而不包括hadoop的文檔。
由于查詢語句有語法,因而也要進行語法分析,語法分析及語言處理。
1. 詞法分析主要用來識別單詞和關鍵字。
如上述例子中,經過詞法分析,得到單詞有lucene,learned,hadoop, 關鍵字有AND, NOT。
如果在詞法分析中發現不合法的關鍵字,則會出現錯誤。如lucene AMD learned,其中由于AND拼錯,導致AMD作為一個普通的單詞參與查詢。
2. 語法分析主要是根據查詢語句的語法規則來形成一棵語法樹。
如果發現查詢語句不滿足語法規則,則會報錯。如lucene NOT AND learned,則會出錯。
如上述例子,lucene AND learned NOT hadoop形成的語法樹如下:
3. 語言處理同索引過程中的語言處理幾乎相同。
如learned變成learn等。
經過第二步,我們得到一棵經過語言處理的語法樹。
此步驟有分幾小步:
首先,在反向索引表中,分別找出包含lucene,learn,hadoop的文檔鏈表。
其次,對包含lucene,learn的鏈表進行合并操作,得到既包含lucene又包含learn的文檔鏈表。
然后,將此鏈表與hadoop的文檔鏈表進行差操作,去除包含hadoop的文檔,從而得到既包含lucene又包含learn而且不包含hadoop的文檔鏈表。
此文檔鏈表就是我們要找的文檔。
雖然在上一步,我們得到了想要的文檔,然而對于查詢結果應該按照與查詢語句的相關性進行排序,越相關者越靠前。
如何計算文檔和查詢語句的相關性呢?
不如我們把查詢語句看作一片短小的文檔,對文檔與文檔之間的相關性(relevance)進行打分(scoring),分數高的相關性好,就應該排在前面。
那么又怎么對文檔之間的關系進行打分呢?
這可不是一件容易的事情,首先我們看一看判斷人之間的關系吧。
首先看一個人,往往有很多要素,如性格,信仰,愛好,衣著,高矮,胖瘦等等。
其次對于人與人之間的關系,不同的要素重要性不同,性格,信仰,愛好可能重要些,衣著,高矮,胖瘦可能就不那么重要了,所以具有相同或相似性格,信仰,愛好的人比較容易成為好的朋友,然而衣著,高矮,胖瘦不同的人,也可以成為好的朋友。
因而判斷人與人之間的關系,首先要找出哪些要素對人與人之間的關系最重要,比如性格,信仰,愛好。其次要判斷兩個人的這些要素之間的關系,比如一個人性格開朗,另一個人性格外向,一個人信仰佛教,另一個信仰上帝,一個人愛好打籃球,另一個愛好踢足球。我們發現,兩個人在性格方面都很積極,信仰方面都很善良,愛好方面都愛運動,因而兩個人關系應該會很好。
我們再來看看公司之間的關系吧。
首先看一個公司,有很多人組成,如總經理,經理,首席技術官,普通員工,保安,門衛等。
其次對于公司與公司之間的關系,不同的人重要性不同,總經理,經理,首席技術官可能更重要一些,普通員工,保安,門衛可能較不重要一點。所以如果兩個公司總經理,經理,首席技術官之間關系比較好,兩個公司容易有比較好的關系。然而一位普通員工就算與另一家公司的一位普通員工有血海深仇,怕也難影響兩個公司之間的關系。
因而判斷公司與公司之間的關系,首先要找出哪些人對公司與公司之間的關系最重要,比如總經理,經理,首席技術官。其次要判斷這些人之間的關系,不如兩家公司的總經理曾經是同學,經理是老鄉,首席技術官曾是創業伙伴。我們發現,兩家公司無論總經理,經理,首席技術官,關系都很好,因而兩家公司關系應該會很好。
分析了兩種關系,下面看一下如何判斷文檔之間的關系了。
首先,一個文檔有很多詞(Term)組成,如search, lucene, full-text, this, a, what等。
其次對于文檔之間的關系,不同的Term重要性不同,比如對于本篇文檔,search, Lucene, full-text就相對重要一些,this, a , what可能相對不重要一些。所以如果兩篇文檔都包含search, Lucene,fulltext,這兩篇文檔的相關性好一些,然而就算一篇文檔包含this, a, what,另一篇文檔不包含this, a, what,也不能影響兩篇文檔的相關性。
因而判斷文檔之間的關系,首先找出哪些詞(Term)對文檔之間的關系最重要,如search, Lucene, fulltext。然后判斷這些詞(Term)之間的關系。
找出詞(Term)對文檔的重要性的過程稱為計算詞的權重(Term weight)的過程。
計算詞的權重(term weight)有兩個參數,第一個是詞(Term),第二個是文檔(Document)。
詞的權重(Term weight)表示此詞(Term)在此文檔中的重要程度,越重要的詞(Term)有越大的權重(Term weight),因而在計算文檔之間的相關性中將發揮更大的作用。
判斷詞(Term)之間的關系從而得到文檔相關性的過程應用一種叫做向量空間模型的算法(Vector Space Model)。
下面仔細分析一下這兩個過程:
影響一個詞(Term)在一篇文檔中的重要性主要有兩個因素:
Term Frequency (tf):即此Term在此文檔中出現了多少次。tf 越大說明越重要。
Document Frequency (df):即有多少文檔包含次Term。df 越大說明越不重要。
容易理解嗎?詞(Term)在文檔中出現的次數越多,說明此詞(Term)對該文檔越重要,如“搜索”這個詞,在本文檔中出現的次數很多,說明本文檔主要就是講這方面的事的。然而在一篇英語文檔中,this出現的次數更多,就說明越重要嗎?不是的,這是由第二個因素進行調整,第二個因素說明,有越多的文檔包含此詞(Term), 說明此詞(Term)太普通,不足以區分這些文檔,因而重要性越低。
這也如我們程序員所學的技術,對于程序員本身來說,這項技術掌握越深越好(掌握越深說明花時間看的越多,tf越大),找工作時越有競爭力。然而對于所有程序員來說,這項技術懂得的人越少越好(懂得的人少df小),找工作越有競爭力。人的價值在于不可替代性就是這個道理。
道理明白了,我們來看看公式:
這僅僅只term weight計算公式的簡單典型實現。實現全文檢索系統的人會有自己的實現,Lucene就與此稍有不同。
我們把文檔看作一系列詞(Term),每一個詞(Term)都有一個權重(Term weight),不同的詞(Term)根據自己在文檔中的權重來影響文檔相關性的打分計算。
于是我們把所有此文檔中詞(term)的權重(term weight) 看作一個向量。
Document = {term1, term2, …… ,term N}
Document Vector = {weight1, weight2, …… ,weight N}
同樣我們把查詢語句看作一個簡單的文檔,也用向量來表示。
Query = {term1, term 2, …… , term N}
Query Vector = {weight1, weight2, …… , weight N}
我們把所有搜索出的文檔向量及查詢向量放到一個N維空間中,每個詞(term)是一維。
如圖:
我們認為兩個向量之間的夾角越小,相關性越大。
所以我們計算夾角的余弦值作為相關性的打分,夾角越小,余弦值越大,打分越高,相關性越大。
有人可能會問,查詢語句一般是很短的,包含的詞(Term)是很少的,因而查詢向量的維數很小,而文檔很長,包含詞(Term)很多,文檔向量維數很大。你的圖中兩者維數怎么都是N呢?
在這里,既然要放到相同的向量空間,自然維數是相同的,不同時,取二者的并集,如果不含某個詞(Term)時,則權重(Term Weight)為0。
相關性打分公式如下:
舉個例子,查詢語句有11個Term,共有三篇文檔搜索出來。其中各自的權重(Term weight),如下表格。
t10 | t11 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
.477 | .477 | .176 | .176 | ||||||||
.176 | .477 | .954 | .176 | ||||||||
.176 | .176 | .176 | .176 | ||||||||
.176 | .477 | .176 |
于是計算,三篇文檔同查詢語句的相關性打分分別為:
于是文檔二相關性最高,先返回,其次是文檔一,最后是文檔三。
到此為止,我們可以找到我們最想要的文檔了。
說了這么多,其實還沒有進入到Lucene,而僅僅是信息檢索技術(Information retrieval)中的基本理論,然而當我們看過Lucene后我們會發現,Lucene是對這種基本理論的一種基本的的實踐。所以在以后分析Lucene的文章中,會常常看到以上理論在Lucene中的應用。
在進入Lucene之前,對上述索引創建和搜索過程所一個總結,如圖:
此圖參照 www.lucene.com.cn/about.htm中文章《開放源代碼的全文檢索引擎Lucene》
1. 索引過程:
1) 有一系列被索引文件
2) 被索引文件經過語法分析和語言處理形成一系列詞(Term)。
3) 經過索引創建形成詞典和反向索引表。
4) 通過索引存儲將索引寫入硬盤。
2. 搜索過程:
a) 用戶輸入查詢語句。
b) 對查詢語句經過語法分析和語言分析得到一系列詞(Term)。
c) 通過語法分析得到一個查詢樹。
d) 通過索引存儲將索引讀入到內存。
e) 利用查詢樹搜索索引,從而得到每個詞(Term)的文檔鏈表,對文檔鏈表進行交,差,并得到結果文檔。
f) 將搜索到的結果文檔對查詢的相關性進行排序。
g) 返回查詢結果給用戶。
下面我們可以進入Lucene的世界了。
CSDN中此文章鏈接為 blog.csdn.net/forfuture19…
Javaeye中此文章鏈接為 forfuture1978.javaeye.com/blog/546771
代碼我已放到 Github ,導入spring-boot-lucene-demo
項目
github spring-boot-lucene-demo
<!--對分詞索引查詢解析--><dependency> <groupId>org.apache.lucene</groupId> lucene-queryparser <version>7.1.0</version></dependency> <!--高亮 --><dependency> <groupId>org.apache.lucene</groupId> lucene-highlighter <version>7.1.0</version></dependency> <!--smartcn 中文分詞器 SmartChineseAnalyzer smartcn分詞器 需要lucene依賴 且和lucene版本同步--><dependency> <groupId>org.apache.lucene</groupId> lucene-analyzers-smartcn <version>7.1.0</version></dependency> <!--ik-analyzer 中文分詞器--><dependency> <groupId>cn.bestwu</groupId> ik-analyzers <version>5.1.0</version></dependency> <!--MMSeg4j 分詞器--><dependency> <groupId>com.chenlb.mmseg4j</groupId> mmseg4j-solr <version>2.4.0</version> <exclusions> <exclusion> <groupId>org.apache.solr</groupId> solr-core </exclusion> </exclusions></dependency>
private Directory directory; private IndexReader indexReader; private IndexSearcher indexSearcher; @Beforepublic void setUp() throws IOException { //索引存放的位置,設置在當前目錄中 directory = FSDirectory.open(Paths.get("indexDir/")); //創建索引的讀取器 indexReader = DirectoryReader.open(directory); //創建一個索引的查找器,來檢索索引庫 indexSearcher = new IndexSearcher(indexReader);} @Afterpublic void tearDown() throws Exception { indexReader.close();} ** * 執行查詢,并打印查詢到的記錄數 * * @param query * @throws IOException */public void executeQuery(Query query) throws IOException { TopDocs topDocs = indexSearcher.search(query, 100); //打印查詢到的記錄數 System.out.println("總共查詢到" + topDocs.totalHits + "個文檔"); for (ScoreDoc scoreDoc : topDocs.scoreDocs) { //取得對應的文檔對象 Document document = indexSearcher.doc(scoreDoc.doc); System.out.println("id:" + document.get("id")); System.out.println("title:" + document.get("title")); System.out.println("content:" + document.get("content")); }} /** * 分詞打印 * * @param analyzer * @param text * @throws IOException */public void printAnalyzerDoc(Analyzer analyzer, String text) throws IOException { TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(text)); CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class); try { tokenStream.reset(); while (tokenStream.incrementToken()) { System.out.println(charTermAttribute.toString()); } tokenStream.end(); } finally { tokenStream.close(); analyzer.close(); }}
@Testpublic void indexWriterTest() throws IOException { long start = System.currentTimeMillis(); //索引存放的位置,設置在當前目錄中 Directory directory = FSDirectory.open(Paths.get("indexDir/")); //在 6.6 以上版本中 version 不再是必要的,并且,存在無參構造方法,可以直接使用默認的 StandardAnalyzer 分詞器。 Version version = Version.LUCENE_7_1_0; //Analyzer analyzer = new StandardAnalyzer(); // 標準分詞器,適用于英文 //Analyzer analyzer = new SmartChineseAnalyzer();//中文分詞 //Analyzer analyzer = new ComplexAnalyzer();//中文分詞 //Analyzer analyzer = new IKAnalyzer();//中文分詞 Analyzer analyzer = new IKAnalyzer();//中文分詞 //創建索引寫入配置 IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer); //創建索引寫入對象 IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig); //創建Document對象,存儲索引 Document doc = new Document(); int id = 1; //將字段加入到doc中 doc.add(new IntPoint("id", id)); doc.add(new StringField("title", "Spark", Field.Store.YES)); doc.add(new TextField("content", "Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎", Field.Store.YES)); doc.add(new StoredField("id", id)); //將doc對象保存到索引庫中 indexWriter.addDocument(doc); indexWriter.commit(); //關閉流 indexWriter.close(); long end = System.currentTimeMillis(); System.out.println("索引花費了" + (end - start) + " 毫秒");}
響應
17:58:14.655 [main] DEBUG org.wltea.analyzer.dic.Dictionary - 加載擴展詞典:ext.dic17:58:14.660 [main] DEBUG org.wltea.analyzer.dic.Dictionary - 加載擴展停止詞典:stopword.dic索引花費了879 毫秒
@Testpublic void deleteDocumentsTest() throws IOException { //Analyzer analyzer = new StandardAnalyzer(); // 標準分詞器,適用于英文 //Analyzer analyzer = new SmartChineseAnalyzer();//中文分詞 //Analyzer analyzer = new ComplexAnalyzer();//中文分詞 //Analyzer analyzer = new IKAnalyzer();//中文分詞 Analyzer analyzer = new IKAnalyzer();//中文分詞 //創建索引寫入配置 IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer); //創建索引寫入對象 IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig); // 刪除title中含有關鍵詞“Spark”的文檔 long count = indexWriter.deleteDocuments(new Term("title", "Spark")); // 除此之外IndexWriter還提供了以下方法: // DeleteDocuments(Query query):根據Query條件來刪除單個或多個Document // DeleteDocuments(Query[] queries):根據Query條件來刪除單個或多個Document // DeleteDocuments(Term term):根據Term來刪除單個或多個Document // DeleteDocuments(Term[] terms):根據Term來刪除單個或多個Document // DeleteAll():刪除所有的Document //使用IndexWriter進行Document刪除操作時,文檔并不會立即被刪除,而是把這個刪除動作緩存起來,當IndexWriter.Commit()或IndexWriter.Close()時,刪除操作才會被真正執行。 indexWriter.commit(); indexWriter.close(); System.out.println("刪除完成:" + count);}
響應
刪除完成:1
/** * 測試更新 * 實際上就是刪除后新增一條 * * @throws IOException */@Testpublic void updateDocumentTest() throws IOException { //Analyzer analyzer = new StandardAnalyzer(); // 標準分詞器,適用于英文 //Analyzer analyzer = new SmartChineseAnalyzer();//中文分詞 //Analyzer analyzer = new ComplexAnalyzer();//中文分詞 //Analyzer analyzer = new IKAnalyzer();//中文分詞 Analyzer analyzer = new IKAnalyzer();//中文分詞 //創建索引寫入配置 IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer); //創建索引寫入對象 IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig); Document doc = new Document(); int id = 1; doc.add(new IntPoint("id", id)); doc.add(new StringField("title", "Spark", Field.Store.YES)); doc.add(new TextField("content", "Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎", Field.Store.YES)); doc.add(new StoredField("id", id)); long count = indexWriter.updateDocument(new Term("id", "1"), doc); System.out.println("更新文檔:" + count); indexWriter.close();}
響應
更新文檔:1
/** * 按詞條搜索 * <p> * TermQuery是最簡單、也是最常用的Query。TermQuery可以理解成為“詞條搜索”, * 在搜索引擎中最基本的搜索就是在索引中搜索某一詞條,而TermQuery就是用來完成這項工作的。 * 在Lucene中詞條是最基本的搜索單位,從本質上來講一個詞條其實就是一個名/值對。 * 只不過這個“名”是字段名,而“值”則表示字段中所包含的某個關鍵字。 * * @throws IOException */@Testpublic void termQueryTest() throws IOException { String searchField = "title"; //這是一個條件查詢的api,用于添加條件 TermQuery query = new TermQuery(new Term(searchField, "Spark")); //執行查詢,并打印查詢到的記錄數 executeQuery(query);}
響應
總共查詢到1個文檔id:1title:Sparkcontent:Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
/** * 多條件查詢 * * BooleanQuery也是實際開發過程中經常使用的一種Query。 * 它其實是一個組合的Query,在使用時可以把各種Query對象添加進去并標明它們之間的邏輯關系。 * BooleanQuery本身來講是一個布爾子句的容器,它提供了專門的API方法往其中添加子句, * 并標明它們之間的關系,以下代碼為BooleanQuery提供的用于添加子句的API接口: * * @throws IOException */@Testpublic void BooleanQueryTest() throws IOException { String searchField1 = "title"; String searchField2 = "content"; Query query1 = new TermQuery(new Term(searchField1, "Spark")); Query query2 = new TermQuery(new Term(searchField2, "Apache")); BooleanQuery.Builder builder = new BooleanQuery.Builder(); // BooleanClause用于表示布爾查詢子句關系的類, // 包 括: // BooleanClause.Occur.MUST, // BooleanClause.Occur.MUST_NOT, // BooleanClause.Occur.SHOULD。 // 必須包含,不能包含,可以包含三種.有以下6種組合: // // 1.MUST和MUST:取得連個查詢子句的交集。 // 2.MUST和MUST_NOT:表示查詢結果中不能包含MUST_NOT所對應得查詢子句的檢索結果。 // 3.SHOULD與MUST_NOT:連用時,功能同MUST和MUST_NOT。 // 4.SHOULD與MUST連用時,結果為MUST子句的檢索結果,但是SHOULD可影響排序。 // 5.SHOULD與SHOULD:表示“或”關系,最終檢索結果為所有檢索子句的并集。 // 6.MUST_NOT和MUST_NOT:無意義,檢索無結果。 builder.add(query1, BooleanClause.Occur.SHOULD); builder.add(query2, BooleanClause.Occur.SHOULD); BooleanQuery query = builder.build(); //執行查詢,并打印查詢到的記錄數 executeQuery(query);}
響應
總共查詢到1個文檔id:1title:Sparkcontent:Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
/** * 匹配前綴 * <p> * PrefixQuery用于匹配其索引開始以指定的字符串的文檔。就是文檔中存在xxx% * <p> * * @throws IOException */@Testpublic void prefixQueryTest() throws IOException { String searchField = "title"; Term term = new Term(searchField, "Spar"); Query query = new PrefixQuery(term); //執行查詢,并打印查詢到的記錄數 executeQuery(query);}
響應
總共查詢到1個文檔id:1title:Sparkcontent:Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
/** * 短語搜索 * <p> * 所謂PhraseQuery,就是通過短語來檢索,比如我想查“big car”這個短語, * 那么如果待匹配的document的指定項里包含了"big car"這個短語, * 這個document就算匹配成功。可如果待匹配的句子里包含的是“big black car”, * 那么就無法匹配成功了,如果也想讓這個匹配,就需要設定slop, * 先給出slop的概念:slop是指兩個項的位置之間允許的最大間隔距離 * * @throws IOException */@Testpublic void phraseQueryTest() throws IOException { String searchField = "content"; String query1 = "apache"; String query2 = "spark"; PhraseQuery.Builder builder = new PhraseQuery.Builder(); builder.add(new Term(searchField, query1)); builder.add(new Term(searchField, query2)); builder.setSlop(0); PhraseQuery phraseQuery = builder.build(); //執行查詢,并打印查詢到的記錄數 executeQuery(phraseQuery);}
響應
總共查詢到1個文檔id:1title:Sparkcontent:Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
/** * 相近詞語搜索 * <p> * FuzzyQuery是一種模糊查詢,它可以簡單地識別兩個相近的詞語。 * * @throws IOException */@Testpublic void fuzzyQueryTest() throws IOException { String searchField = "content"; Term t = new Term(searchField, "大規模"); Query query = new FuzzyQuery(t); //執行查詢,并打印查詢到的記錄數 executeQuery(query);}
響應
總共查詢到1個文檔id:1title:Sparkcontent:Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
/** * 通配符搜索 * <p> * Lucene也提供了通配符的查詢,這就是WildcardQuery。 * 通配符“?”代表1個字符,而“*”則代表0至多個字符。 * * @throws IOException */@Testpublic void wildcardQueryTest() throws IOException { String searchField = "content"; Term term = new Term(searchField, "大*規模"); Query query = new WildcardQuery(term); //執行查詢,并打印查詢到的記錄數 executeQuery(query);}
響應
總共查詢到1個文檔id:1title:Sparkcontent:Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
/** * 分詞查詢 * * @throws IOException * @throws ParseException */@Testpublic void queryParserTest() throws IOException, ParseException { //Analyzer analyzer = new StandardAnalyzer(); // 標準分詞器,適用于英文 //Analyzer analyzer = new SmartChineseAnalyzer();//中文分詞 //Analyzer analyzer = new ComplexAnalyzer();//中文分詞 //Analyzer analyzer = new IKAnalyzer();//中文分詞 Analyzer analyzer = new IKAnalyzer();//中文分詞 String searchField = "content"; //指定搜索字段和分析器 QueryParser parser = new QueryParser(searchField, analyzer); //用戶輸入內容 Query query = parser.parse("計算引擎"); //執行查詢,并打印查詢到的記錄數 executeQuery(query);}
響應
總共查詢到1個文檔id:1title:Sparkcontent:Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
/** * 多個 Field 分詞查詢 * * @throws IOException * @throws ParseException */@Testpublic void multiFieldQueryParserTest() throws IOException, ParseException { //Analyzer analyzer = new StandardAnalyzer(); // 標準分詞器,適用于英文 //Analyzer analyzer = new SmartChineseAnalyzer();//中文分詞 //Analyzer analyzer = new ComplexAnalyzer();//中文分詞 //Analyzer analyzer = new IKAnalyzer();//中文分詞 Analyzer analyzer = new IKAnalyzer();//中文分詞 String[] filedStr = new String[]{"title", "content"}; //指定搜索字段和分析器 QueryParser queryParser = new MultiFieldQueryParser(filedStr, analyzer); //用戶輸入內容 Query query = queryParser.parse("Spark"); //執行查詢,并打印查詢到的記錄數 executeQuery(query);}
響應
總共查詢到1個文檔id:1title:Sparkcontent:Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
/** * IKAnalyzer 中文分詞器 * SmartChineseAnalyzer smartcn分詞器 需要lucene依賴 且和lucene版本同步 * * @throws IOException */@Testpublic void AnalyzerTest() throws IOException { //Analyzer analyzer = new StandardAnalyzer(); // 標準分詞器,適用于英文 //Analyzer analyzer = new SmartChineseAnalyzer();//中文分詞 //Analyzer analyzer = new ComplexAnalyzer();//中文分詞 //Analyzer analyzer = new IKAnalyzer();//中文分詞 Analyzer analyzer = null; String text = "Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎"; analyzer = new IKAnalyzer();//IKAnalyzer 中文分詞 printAnalyzerDoc(analyzer, text); System.out.println(); analyzer = new ComplexAnalyzer();//MMSeg4j 中文分詞 printAnalyzerDoc(analyzer, text); System.out.println(); analyzer = new SmartChineseAnalyzer();//Lucene 中文分詞器 printAnalyzerDoc(analyzer, text);}
三種分詞響應
apachespark專為大規模規模模數數據處理數據處理而設設計快速通用計算引擎
apachespark是專為大規模數據處理而設計的快速通用的計算引擎
apachspark是專為大規模數據處理而設計的快速通用的計算引擎
/** * 高亮處理 * * @throws IOException */@Testpublic void HighlighterTest() throws IOException, ParseException, InvalidTokenOffsetsException { //Analyzer analyzer = new StandardAnalyzer(); // 標準分詞器,適用于英文 //Analyzer analyzer = new SmartChineseAnalyzer();//中文分詞 //Analyzer analyzer = new ComplexAnalyzer();//中文分詞 //Analyzer analyzer = new IKAnalyzer();//中文分詞 Analyzer analyzer = new IKAnalyzer();//中文分詞 String searchField = "content"; String text = "Apache Spark 大規模數據處理"; //指定搜索字段和分析器 QueryParser parser = new QueryParser(searchField, analyzer); //用戶輸入內容 Query query = parser.parse(text); TopDocs topDocs = indexSearcher.search(query, 100); // 關鍵字高亮顯示的html標簽,需要導入lucene-highlighter-xxx.jar SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("", ""); Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query)); for (ScoreDoc scoreDoc : topDocs.scoreDocs) { //取得對應的文檔對象 Document document = indexSearcher.doc(scoreDoc.doc); // 內容增加高亮顯示 TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(document.get("content"))); String content = highlighter.getBestFragment(tokenStream, document.get("content")); System.out.println(content); } }
響應
Apache Spark 是專為大規模數據處理而設計的快速通用的計算引擎!
感謝各位的閱讀,以上就是“Lucene基礎原理是什么”的內容了,經過本文的學習后,相信大家對Lucene基礎原理是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。