欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
lucene搜索引擎技術(shù)的分析與整理
lucene搜索引擎技術(shù)的分析與整理  
  

1.   <!--[endif]-->引言

<!--[if !supportLists]-->1.1.   <!--[endif]-->編寫(xiě)目的

介紹開(kāi)源軟件搜索引擎——lucene的各個(gè)實(shí)現的功能,性能,以及代碼分析

<!--[if !supportLists]-->1.2.   <!--[endif]-->背景

分析的系統名稱(chēng)

Lucene

該開(kāi)源主頁(yè)

http://lucene.apache.org/

開(kāi)發(fā)語(yǔ)言

JAVA

該系統的分析者

zzpchina

該系統作者簡(jiǎn)介

Lucene的貢獻者Doug Cutting是一位資深全文索引/檢索專(zhuān)家,曾經(jīng)是V-Twin搜索引擎(AppleCopland操作系統的成就之一)的主要開(kāi)發(fā)者,后在Excite擔任高級系統架構設計師,目前從事于一些INTERNET底層架構的研究。他貢獻出的Lucene的目標是為各種中小型應用程序加入全文檢索功能。

該系統簡(jiǎn)介

Lucene不是一個(gè)完整的全文索引應用,而是是一個(gè)用Java寫(xiě)的全文索引引擎工具包,它可以方便的嵌入到各種應用中實(shí)現針對應用的全文索引/檢索功能。

<!--[if !supportLists]-->1.3.   <!--[endif]-->該項目的使用現狀

 經(jīng)過(guò)多年的發(fā)展,Lucene在全文檢索領(lǐng)域已經(jīng)有了很多的成功案例,并積累了良好的聲譽(yù)。

 基于Lucene的全文檢索產(chǎn)品和應用Lucene的項目在世界各地已經(jīng)非常之多,比較知名的有:

<!--[if !supportLists]-->1.      <!--[endif]--> Eclipse:主流Java開(kāi)發(fā)工具,其幫助文檔采用Lucene作為檢索引擎

<!--[if !supportLists]-->2.        <!--[endif]-->Jive:知名論壇系統,其檢索功能基于Lucene

<!--[if !supportLists]-->3.        <!--[endif]-->Ifinder:出自德國的網(wǎng)站檢索系統,基于Lucenehttp://ifinder.intrafind.org/

<!--[if !supportLists]-->4.        <!--[endif]-->MIT DSpace Federation:一個(gè)文檔管理系統(http://www.dspace.org/

國內外采用Lucene作為網(wǎng)站全文檢索引擎的也很多,比較知名的有:

<!--[if !supportLists]-->1.      <!--[endif]-->http://www.blogchina.com/weblucene/

<!--[if !supportLists]-->2.      <!--[endif]-->http://www.ioffer.com/

<!--[if !supportLists]-->3.      <!--[endif]-->http://search.soufun.com/

<!--[if !supportLists]-->4.      <!--[endif]-->http://www.taminn.com/

(更多案例,參見(jiàn)http://wiki.apache.org/jakarta-lucene/PoweredBy

在所有這些案例中,開(kāi)源應用占了很大一部分,但更多的還是商化業(yè)產(chǎn)品和網(wǎng)站。

2.   <!--[endif]-->功能分析

<!--[if !supportLists]-->2.1.   <!--[endif]-->Oracle數據庫對比

LuceneAPI接口設計的比較通用,輸入輸出結構都很像數據庫的表==>記錄==>字段,所以很多傳統的應用的文件、數據庫等都可以比較方便的映射到Lucene的存儲結構/接口中??傮w上看:可以先把Lucene當成一個(gè)支持全文索引的數據庫系統。

 

全文檢索庫對關(guān)系型數據庫對比

對比項

全文檢索庫(Lucene)

關(guān)系型數據庫(Oracle)

核心功能

以文本檢索為主,插入(insert)、刪除(delete)、修改(update)比較麻煩,適合于大文本塊的查詢(xún)。

插入(insert)、刪除(delete)、修改(update)十分方便,有專(zhuān)門(mén)的SQL命令,但對于大文本塊(如CLOB)類(lèi)型的檢索效率低下。

Oracle類(lèi)似,都可以建多個(gè)庫,且各個(gè)庫的存儲位置可以不同。

可以建多個(gè)庫,每個(gè)庫一般都有控制文件和數據文件等,比較復雜。

沒(méi)有嚴格的表的概念,比如Lucene的表只是由入庫時(shí)的定義字段松散組成。

有嚴格的表結構,有主鍵,有字段類(lèi)型等。

記錄

由于沒(méi)有嚴格表的概念,所以記錄體現為一個(gè)對象,在Lucene里記錄對應的類(lèi)是Document。

Record,與表結構對應。

字段

字段類(lèi)型只有文本和日期兩種,字段一般不支持運算,更無(wú)函數功能。

Lucene里字段的類(lèi)是Field,如document(field1,field2…)

字段類(lèi)型豐富,功能強大。

record(field1,field2…)

查詢(xún)結果集

Lucene里表示查詢(xún)結果集的類(lèi)是Hits,如hits(doc1,doc2,doc3…)

JDBC為例, Resultset(record1,record2,record3...)

<!--[if !supportLists]-->2.2.   <!--[endif]--> Lucene全文搜索相對數據庫搜索的優(yōu)點(diǎn)

全文檢索 ≠ like "%keyword%"

通常比較厚的書(shū)籍后面常常附關(guān)鍵詞索引表(比如:北京:12, 34頁(yè),上海:3,77頁(yè)……),它能夠幫助讀者比較快地找到相關(guān)內容的頁(yè)碼。而數據庫索引能夠大大提高查詢(xún)的速度原理也是一樣,想像一下通過(guò)書(shū)后面的索引查找的速度要比一頁(yè)一頁(yè)地翻內容高多少倍……而索引之所以效率高,另外一個(gè)原因是它是排好序的。對于檢索系統來(lái)說(shuō)核心是一個(gè)排序問(wèn)題。

由于數據庫索引不是為全文索引設計的,因此,使用like "%keyword%"時(shí),數據庫索引是不起作用的,在使用like查詢(xún)時(shí),搜索過(guò)程又變成類(lèi)似于一頁(yè)頁(yè)翻書(shū)的遍歷過(guò)程了,所以對于含有模糊查詢(xún)的數據庫服務(wù)來(lái)說(shuō),LIKE對性能的危害是極大的。如果是需要對多個(gè)關(guān)鍵詞進(jìn)行模糊匹配:like"%keyword1%" and like "%keyword2%" ...其效率也就可想而知了。

所以建立一個(gè)高效檢索系統的關(guān)鍵是建立一個(gè)類(lèi)似于科技索引一樣的反向索引機制,將數據源(比如多篇文章)排序順序存儲的同時(shí),有另外一個(gè)排好序的關(guān)鍵詞列表,用于存儲關(guān)鍵詞==>文章映射關(guān)系,利用這樣的映射關(guān)系索引:

關(guān)鍵詞==>出現關(guān)鍵詞的文章編號、出現次數、起始偏移量、結束偏移量,出現頻率

檢索過(guò)程就是把模糊查詢(xún)變成多個(gè)可以利用索引的精確查詢(xún)的邏輯組合的過(guò)程。從而大大提高了多關(guān)鍵詞查詢(xún)的效率,所以,全文檢索問(wèn)題歸結到最后是一個(gè)排序問(wèn)題。

由此可以看出,模糊查詢(xún)相對數據庫的精確查詢(xún)是一個(gè)非常不確定的問(wèn)題,這也是大部分數據庫對全文檢索支持有限的原因。Lucene最核心的特征是通過(guò)特殊的索引結構實(shí)現了傳統數據庫不擅長(cháng)的全文索引機制,并提供了擴展接口,以方便針對不同應用的定制。

可以通過(guò)一下表格對比一下數據庫的模糊查詢(xún):

 

Lucene全文索引引擎

數據庫

索引

將數據源中的數據都通過(guò)全文索引一一建立反向索引

對于LIKE查詢(xún)來(lái)說(shuō),數據傳統的索引是根本用不上的。數據需要逐個(gè)便利記錄進(jìn)行GREP式的模糊匹配,比有索引的搜索速度要有多個(gè)數量級的下降。

匹配效果

通過(guò)詞元(term)進(jìn)行匹配,通過(guò)語(yǔ)言分析接口的實(shí)現,可以實(shí)現對中文等非英語(yǔ)的支持。

使用:like "%net%" 會(huì )把netherlands也匹配出來(lái),
多個(gè)關(guān)鍵詞的模糊匹配:使用like "%com%net%":就不能匹配詞序顛倒的xxx.net..xxx.com

匹配度

有匹配度算法,將匹配程度(相似度)比較高的結果排在前面。

沒(méi)有匹配程度的控制:比如有記錄中net出現5詞和出現1次的,結果是一樣的。

結果輸出

通過(guò)特別的算法,將最匹配度最高的頭100條結果輸出,結果集是緩沖式的小批量讀取的。

返回所有的結果集,在匹配條目非常多的時(shí)候(比如上萬(wàn)條)需要大量的內存存放這些臨時(shí)結果集。

可定制性

通過(guò)不同的語(yǔ)言分析接口實(shí)現,可以方便的定制出符合應用需要的索引規則(包括對中文的支持)

沒(méi)有接口或接口復雜,無(wú)法定制

結論

高負載的模糊查詢(xún)應用,需要負責的模糊查詢(xún)的規則,索引的資料量比較大

使用率低,模糊匹配規則簡(jiǎn)單或者需要模糊查詢(xún)的資料量少

全文檢索和數據庫應用最大的不同在于:讓最相關(guān)的頭100條結果滿(mǎn)足98%以上用戶(hù)的需求

<!--[if !supportLists]-->2.3.   <!--[endif]-->相對一般的搜索引擎優(yōu)點(diǎn)

大部分的搜索(數據庫)引擎都是用B樹(shù)結構來(lái)維護索引,索引的更新會(huì )導致大量的IO操作,Lucene在實(shí)現中,對此稍微有所改進(jìn):不是維護一個(gè)索引文件,而是在擴展索引的時(shí)候不斷創(chuàng )建新的索引文件,然后定期的把這些新的小索引文件合并到原先的大索引中(針對不同的更新策略,批次的大小可以調整),這樣在不影響檢索的效率的前提下,提高了索引的效率。

Lucene和其他一些全文檢索系統/應用的比較:

 

 

 

 

Lucene

其他開(kāi)源全文檢索系統

增量索引和批量索引

可以進(jìn)行增量的索引(Append),可以對于大量數據進(jìn)行批量索引,并且接口設計用于優(yōu)化批量索引和小批量的增量索引。

很多系統只支持批量的索引,有時(shí)數據源有一點(diǎn)增加也需要重建索引。

數據源

Lucene沒(méi)有定義具體的數據源,而是一個(gè)文檔的結構,因此可以非常靈活的適應各種應用(只要前端有合適的轉換器把數據源轉換成相應結構),

很多系統只針對網(wǎng)頁(yè),缺乏其他格式文檔的靈活性。

索引內容抓取

Lucene的文檔是由多個(gè)字段組成的,甚至可以控制那些字段需要進(jìn)行索引,那些字段不需要索引,近一步索引的字段也分為需要分詞和不需要分詞的類(lèi)型:
  
需要進(jìn)行分詞的索引,比如:標題,文章內容字段
  
不需要進(jìn)行分詞的索引,比如:作者/日期字段

缺乏通用性,往往將文檔整個(gè)索引了

語(yǔ)言分析

通過(guò)語(yǔ)言分析器的不同擴展實(shí)現:
可以過(guò)濾掉不需要的詞:an the of 等,
西文語(yǔ)法分析:將jumps jumped jumper都歸結成jump進(jìn)行索引/檢索
非英文支持:對亞洲語(yǔ)言,阿拉伯語(yǔ)言的索引支持

缺乏通用接口實(shí)現

查詢(xún)分析

通過(guò)查詢(xún)分析接口的實(shí)現,可以定制自己的查詢(xún)語(yǔ)法規則:
比如: 多個(gè)關(guān)鍵詞之間的 + - and or關(guān)系等

 

并發(fā)訪(fǎng)問(wèn)

能夠支持多用戶(hù)的使用

 

 

<!--[if !supportLists]-->2.4.   <!--[endif]-->對索引過(guò)程優(yōu)化

索引一般分2種情況,一種是小批量的索引擴展,一種是大批量的索引重建。在索引過(guò)程中,并不是每次新的DOC加入進(jìn)去索引都重新進(jìn)行一次索引文件的寫(xiě)入操作(文件I/O是一件非常消耗資源的事情)。

Lucene先在內存中進(jìn)行索引操作,并根據一定的批量進(jìn)行文件的寫(xiě)入。這個(gè)批次的間隔越大,文件的寫(xiě)入次數越少,但占用內存會(huì )很多。反之占用內存少,但文件IO操作頻繁,索引速度會(huì )很慢。在IndexWriter中有一個(gè)MERGE_FACTOR參數可以幫助你在構造索引器后根據應用環(huán)境的情況充分利用內存減少文件的操作。根據我的使用經(jīng)驗:缺省Indexer是每20條記錄索引后寫(xiě)入一次,每將MERGE_FACTOR增加50倍,索引速度可以提高1倍左右。

<!--[if !supportLists]-->2.5.   <!--[endif]-->對搜索過(guò)程優(yōu)化

lucene支持內存索引:這樣的搜索比基于文件的I/O有數量級的速度提升。
http://www.onjava.com/lpt/a/3273
,而盡可能減少IndexSearcher的創(chuàng )建和對搜索結果的前臺的緩存也是必要的。

Lucene面向全文檢索的優(yōu)化在于首次索引檢索后,并不把所有的記錄(Document)具體內容讀取出來(lái),而起只將所有結果中匹配度最高的頭100條結果(TopDocs)的ID放到結果集緩存中并返回,這里可以比較一下數據庫檢索:如果是一個(gè)10,000條的數據庫檢索結果集,數據庫是一定要把所有記錄內容都取得以后再開(kāi)始返回給應用結果集的。

所以即使檢索匹配總數很多,Lucene的結果集占用的內存空間也不會(huì )很多。對于一般的模糊檢索應用是用不到這么多的結果的,頭100條已經(jīng)可以滿(mǎn)足90%以上的檢索需求。

如果首批緩存結果數用完后還要讀取更后面的結果時(shí)Searcher會(huì )再次檢索并生成一個(gè)上次的搜索緩存數大1倍的緩存,并再重新向后抓取。所以如果構造一個(gè)Searcher去查1120條結果,Searcher其實(shí)是進(jìn)行了2次搜索過(guò)程:頭100條取完后,緩存結果用完,Searcher重新檢索再構造一個(gè)200條的結果緩存,依此類(lèi)推,400條緩存,800條緩存。由于每次Searcher對象消失后,這些緩存也訪(fǎng)問(wèn)那不到了,你有可能想將結果記錄緩存下來(lái),緩存數盡量保證在100以下以充分利用首次的結果緩存,不讓Lucene浪費多次檢索,而且可以分級進(jìn)行結果緩存。

Lucene的另外一個(gè)特點(diǎn)是在收集結果的過(guò)程中將匹配度低的結果自動(dòng)過(guò)濾掉了,過(guò)濾過(guò)程我們可以通過(guò)設置最低的匹配度來(lái)進(jìn)行過(guò)濾。這也是和數據庫應用需要將搜索的結果全部返回不同之處。

 

3.   <!--[endif]-->Lucene的特性分析

<!--[if !supportLists]-->3.1.   <!--[endif]-->Lucene核心部分——索引排序

Lucene 的索引排序是使用了倒排序原理。

該結構及相應的生成算法如下:
   
設有兩篇文章12
   
文章1的內容為:Tom lives in Guangzhou,I live in Guangzhou too.
    文章2的內容為:He once lived in Shanghai.

<!--[if !supportLists]-->1.         <!--[endif]-->由于lucene是基于關(guān)鍵詞索引和查詢(xún)的,首先我們要取得這兩篇文章的關(guān)鍵詞,通常我們需要如下處理措施

<!--[if !supportLists]-->a.       <!--[endif]-->我們現在有的是文章內容,即一個(gè)字符串,我們先要找出字符串中的所有單詞,即分詞。英文單詞由于用空格分隔,比較好處理。中文單詞間是連在一起的需要特殊的分詞處理。

<!--[if !supportLists]-->b.       <!--[endif]-->文章中的”in”, “once” “too”等詞沒(méi)有什么實(shí)際意義,中文中的”“等字通常也無(wú)具體含義,  這些不代表概念的詞可以過(guò)濾掉,這個(gè)也就是在《Lucene詳細分析》中所講的StopTokens

<!--[if !supportLists]-->c.       <!--[endif]-->用戶(hù)通常希望查“He”時(shí)能把含“he”,“HE”的文章也找出來(lái),所以所有單詞需要統一大小寫(xiě)。

<!--[if !supportLists]-->d.       <!--[endif]-->用戶(hù)通常希望查“live”時(shí)能把含“lives”,“lived”的文章也找出來(lái),所以需要把“lives”,“lived”還原成“live”

<!--[if !supportLists]-->e.       <!--[endif]-->文章中的標點(diǎn)符號通常不表示某種概念,也可以過(guò)濾掉,lucene中以上措施由Analyzer類(lèi)完成,經(jīng)過(guò)上面處理后:

           文章1的所有關(guān)鍵詞為:[tom] [live] [guangzhou] [i] [live] [guangzhou]
            文章2的所有關(guān)鍵詞為:[he] [live] [shanghai]

<!--[if !supportLists]-->2.        <!--[endif]-->有了關(guān)鍵詞后,我們就可以建立倒排索引了

上面的對應關(guān)系是:文章號文章中所有關(guān)鍵詞。倒排索引把這個(gè)關(guān)系倒過(guò)來(lái),變成:關(guān)鍵詞擁有該關(guān)鍵詞的所有文章號。文章1,2經(jīng)過(guò)倒排后變成
<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

關(guān)鍵詞   

文章號

guangzhou  

1

he      

   2

i         

  1

live     

  1,2

shanghai  

 2

tom     

    1

 

通常僅知道關(guān)鍵詞在哪些文章中出現還不夠,我們還需要知道關(guān)鍵詞在文章中出現次數和出現的位置,通常有兩種位置:a)字符位置,即記錄該詞是文章中第幾個(gè)字符(優(yōu)點(diǎn)是關(guān)鍵詞亮顯時(shí)定位快);b)關(guān)鍵詞位置,即記錄該詞是文章中第幾個(gè)關(guān)鍵詞(優(yōu)點(diǎn)是節約索引空間、詞組(phase)查詢(xún)快),lucene中記錄的就是這種位置。
   
加上出現頻率出現位置信息后,我們的索引結構變?yōu)椋?/span>

 

關(guān)鍵詞   

文章號[出現頻率]   

出現位置

guangzhou

 1[2]             

  3,6

he     

  2[1]            

   1

i        

 1[1]           

    4

live     

 1[2],2[1]        

   2,5,2

shanghai  

2[1]            

   3

tom    

  1[1]               

1

 

live 這行為例我們說(shuō)明一下該結構:live在文章1中出現了2次,文章2中出現了一次,它的出現位置為“2,5,2”這表示什么呢?我們需要結合文章號和出現頻率來(lái)分析,文章1中出現了2次,那么“2,5”就表示live在文章1中出現的兩個(gè)位置,文章2中出現了一次,剩下的“2”就表示live是文章2中第 2個(gè)關(guān)鍵字。
    
以上就是lucene索引結構中最核心的部分。我們注意到關(guān)鍵字是按字符順序排列的(lucene沒(méi)有使用B樹(shù)結構),因此lucene可以用二元搜索算法快速定位關(guān)鍵詞。
    
實(shí)現時(shí) lucene將上面三列分別作為詞典文件(Term Dictionary)、頻率文件(frequencies)、位置文件 (positions)保存。其中詞典文件不僅保存有每個(gè)關(guān)鍵詞,還保留了指向頻率文件和位置文件的指針,通過(guò)指針可以找到該關(guān)鍵字的頻率信息和位置信息。

      Lucene
中使用了field的概念,用于表達信息所在位置(如標題中,文章中,url中),在建索引中,該field信息也記錄在詞典文件中,每個(gè)關(guān)鍵詞都有一個(gè)field信息(因為每個(gè)關(guān)鍵字一定屬于一個(gè)或多個(gè)field)。
      
為了減小索引文件的大小,Lucene對索引還使用了壓縮技術(shù)。首先,對詞典文件中的關(guān)鍵詞進(jìn)行了壓縮,關(guān)鍵詞壓縮為<前綴長(cháng)度,后綴>,例如:當前詞為阿拉伯語(yǔ),上一個(gè)詞為阿拉伯,那么阿拉伯語(yǔ)壓縮為<3,語(yǔ)>。其次大量用到的是對數字的壓縮,數字只保存與上一個(gè)值的差值(這樣可以減小數字的長(cháng)度,進(jìn)而減少保存該數字需要的字節數)。例如當前文章號是16389(不壓縮要用3個(gè)字節保存),上一文章號是16382,壓縮后保存7(只用一個(gè)字節)。
     
下面我們可以通過(guò)對該索引的查詢(xún)來(lái)解釋一下為什么要建立索引。
   
假設要查詢(xún)單詞 “live”,lucene先對詞典二元查找、找到該詞,通過(guò)指向頻率文件的指針讀出所有文章號,然后返回結果。詞典通常非常小,因而,整個(gè)過(guò)程的時(shí)間是毫秒級的。
而用普通的順序匹配算法,不建索引,而是對所有文章的內容進(jìn)行字符串匹配,這個(gè)過(guò)程將會(huì )相當緩慢,當文章數目很大時(shí),時(shí)間往往是無(wú)法忍受的。

<!--[if !supportLists]-->3.2.   <!--[endif]-->Lucene的相關(guān)度積分公式

score_d = sum_t(tf_q * idf_t / norm_q * tf_d * idf_t / norm_d_t * boost_t) * coord_q_d

注解:

score_d : 該文檔d的得分

sum_t : 所有項得分的總和

tf_q : 查詢(xún)串q中,某個(gè)項出項的次數的平方根

tf_d : 文檔d ,出現某個(gè)項的次數的平方根

numDocs : 在這個(gè)索引里,找到分數大于0的文檔的總數

docFreq_t : 包含項t的文檔總數

idf_t : log(numDocs/docFreq+1)+1.0

norm_q : sqrt(sum_t((tf_q*idf_t)^2))

norm_d_t : 在文檔d中,與項t相同的域中,所有的項總數的平方根

boost_t : t的提升因子,一般為 1.0

coord_q_d : 在文檔d中,命中的項數量除以查詢(xún)q的項總數

<!--[if !supportLists]-->3.3.   <!--[endif]-->Lucene其他特性

<!--[if !supportLists]-->3.3.1.    <!--[endif]-->Boosting特性

lunceneDocumentField提供了一個(gè)可以設置的Boosting參數, 這個(gè)參數的用處是告訴lucene, 某些記錄更重要,在搜索的時(shí)候優(yōu)先考慮他們 比如在搜索的時(shí)候你可能覺(jué)得幾個(gè)門(mén)戶(hù)的網(wǎng)頁(yè)要比垃圾小站更優(yōu)先考慮

lucene默認的boosting參數是1.0,  如果你覺(jué)得這個(gè)field重要,你可以把boosting設置為1.5, 1.2...., Document設置boosting相當設定了它的每個(gè)Field的基準boosting,到時(shí)候實(shí)際Fieldboosting就是(Document-boosting*Field-boosting)設置了一遍相同的boosting.

似乎在lucene的記分公式里面有boosting參數,不過(guò)我估計一般人是不會(huì )去研究他的公式的(復雜),而且公式也無(wú)法給出最佳值,所以我們所能做的只能是一點(diǎn)一點(diǎn)的改變boosting, 然后在實(shí)際檢測中觀(guān)察它對搜索結果起到多大的作用來(lái)調整

一般的情況下是沒(méi)有必要使用boosting, 因為搞不好你就把搜索給搞亂了, 另外如果是單獨對Field來(lái)做Bossting, 也可以通過(guò)將這個(gè)Field提前來(lái)起到近似的效果

<!--[if !supportLists]-->3.3.2.    <!--[endif]-->Indexing Date

日期是lucene需要特殊考慮的地方之一, 因為我們可能需要對日期進(jìn)行范圍搜索, Field.keyword(string,Date)提供了這樣的方法,lucene會(huì )把這個(gè)日期轉換為string, 值得注意的是這里的日期是精確到毫秒的,可能會(huì )有不必要的性能損失, 所以我們也可以把日期自行轉化為YYYYMMDD這樣的形勢,就不用精確到具體時(shí)間了,通過(guò)File.keyword(Stirng,String) 來(lái)index, 使用PrefixQuery YYYY一樣能起到簡(jiǎn)化版的日期范圍搜索(小技巧), lucene提到他不能處理1970年以前的時(shí)間,似乎是上一代電腦系統遺留下來(lái)的毛病

<!--[if !supportLists]-->3.3.3.    <!--[endif]-->Indexing 數字

如果數字只是簡(jiǎn)單的數據, 比如中國有56個(gè)民族. 那么可以簡(jiǎn)單的把它當字符處理

如果數字還包含數值的意義,比如價(jià)格, 我們會(huì )有范圍搜索的需要(20元到30元之間的商品),那么我們必須做點(diǎn)小技巧, 比如把3,34,100 這三個(gè)數字轉化為003,034,100 ,因為這樣處理以后, 按照字符排序和按照數值排序是一樣的,lucene內部按照字符排序,003->034->100 NOT(100->3->34)

<!--[if !supportLists]-->3.3.4.    <!--[endif]-->排序

Lucene默認按照相關(guān)度(score)排序,為了能支持其他的排序方式,比如日期,我們在add Field的時(shí)候,必須保證fieldIndex且不能被tokenized(分詞),并且排序的只能是數字,日期,字符三種類(lèi)型之一

<!--[if !supportLists]-->3.3.5.    <!--[endif]-->LuceneIndexWriter調整

IndexWriter提供了一些參數可供設置,列表如下

 

屬性

默認值

說(shuō)明

mergeFactor

org.apache.lucene.mergeFactor

10

控制index的大小和頻率,兩個(gè)作用

maxMergeDocs

org.apache.lucene.maxMergeDocs

Integer.MAX_VALUE

限制一個(gè)段中的document數目

minMergeDocs

org.apache.lucene.minMergeDocs

10

緩存在內存中的document數目,超過(guò)他以后會(huì )寫(xiě)入到磁盤(pán)

maxFieldLength

 

1000

一個(gè)Field中最大Term數目,超過(guò)部分忽略,不會(huì )indexfield中,所以自然也就搜索不到


這些參數的的詳細說(shuō)明比較復雜:mergeFactor有雙重作用

設置每mergeFactor個(gè)document寫(xiě)入一個(gè)段,比如每10個(gè)document寫(xiě)入一個(gè)段

設置每mergeFacotr個(gè)小段合并到一個(gè)大段,比如10個(gè)document的時(shí)候合并為1小段,以后有10個(gè)小段以后合并到一個(gè)大段,有10個(gè)大段以后再合并,實(shí)際的document數目會(huì )是mergeFactor的指數

簡(jiǎn)單的來(lái)說(shuō)mergeFactor 越大,系統會(huì )用更多的內存,更少磁盤(pán)處理,如果要打批量的作index,那么把mergeFactor設置大沒(méi)錯, mergeFactor 小了以后, index數目也會(huì )增多,searhing的效率會(huì )降低, 但是mergeFactor增大一點(diǎn)一點(diǎn),內存消耗會(huì )增大很多(指數關(guān)系),所以要留意不要"out of memory"
maxMergeDocs設置小,可以強制讓達到一定數量的document寫(xiě)為一個(gè)段,這樣可以抵消部分mergeFactor的作用.
minMergeDocs
相當于設置一個(gè)小的cache,第一個(gè)這個(gè)數目的document會(huì )留在內存里面,不寫(xiě)入磁盤(pán)。這些參數同樣是沒(méi)有最佳值的, 必須根據實(shí)際情況一點(diǎn)點(diǎn)調整。
maxFieldLength
可以在任何時(shí)刻設置, 設置后,接下來(lái)的indexField會(huì )按照新的length截取,之前已經(jīng)index的部分不會(huì )改變??梢栽O置為Integer.MAX_VALUE

<!--[if !supportLists]-->3.3.6.    <!--[endif]-->RAMDirectory FSDirectory 轉化

RAMDirectory(RAMD)在效率上比FSDirectyr(FSD)高不少, 所以我們可以手動(dòng)的把RAMD當作FSDbuffer,這樣就不用去很費勁的調優(yōu)FSD那么多參數了,完全可以先用RAM跑好了index, 周期性(或者是別的什么算法)來(lái)回寫(xiě)道FSD中。 RAMD完全可以做FSDbuffer。

<!--[if !supportLists]-->3.3.7.    <!--[endif]-->為查詢(xún)優(yōu)化索引(index)

Indexwriter.optimize()方法可以為查詢(xún)優(yōu)化索引(index),之前提到的參數調優(yōu)是為indexing過(guò)程本身優(yōu)化,而這里是為查詢(xún)優(yōu)化,優(yōu)化主要是減少index文件數,這樣讓查詢(xún)的時(shí)候少打開(kāi)文件,優(yōu)化過(guò)程中,lucene會(huì )拷貝舊的index再合并,合并完成以后刪除舊的index,所以在此期間,磁盤(pán)占用增加, IO符合也會(huì )增加,在優(yōu)化完成瞬間,磁盤(pán)占用會(huì )是優(yōu)化前的2,optimize過(guò)程中可以同時(shí)作search。

<!--[if !supportLists]-->3.3.8.    <!--[endif]-->并發(fā)操作Lucenelocking機制

<!--[if !supportLists]-->v         <!--[endif]-->所有只讀操作都可以并發(fā)

<!--[if !supportLists]-->v         <!--[endif]-->index被修改期間,所有只讀操作都可以并發(fā)

<!--[if !supportLists]-->v         <!--[endif]-->index修改操作不能并發(fā),一個(gè)index只能被一個(gè)線(xiàn)程占用

<!--[if !supportLists]-->v         <!--[endif]-->index的優(yōu)化,合并,添加都是修改操作

<!--[if !supportLists]-->v         <!--[endif]-->IndexWriterIndexReader的實(shí)例可以被多線(xiàn)程共享,他們內部是實(shí)現了同步,所以外面使用不需要同步

<!--[if !supportLists]-->3.3.9.    <!--[endif]-->Locing

   lucence內部使用文件來(lái)locking, 默認的locking文件放在java.io.tmpdir,可以通過(guò)-Dorg.apache.lucene.lockDir=xxx指定新的dir,有write.lock commit.lock兩個(gè)文件,lock文件用來(lái)防止并行操作index,如果并行操作, lucene會(huì )拋出異常,可以通過(guò)設置-DdisableLuceneLocks=true來(lái)禁止locking,這樣做一般來(lái)說(shuō)很危險,除非你有操作系統或者物理級別的只讀保證,比如把index文件刻盤(pán)到CDROM上。

4.   <!--[endif]-->Lucene文檔結構 

Lucene中最基礎的概念是索引(index),文檔(document.,域(field)和項(term)。
索引包含了一個(gè)文檔的序列。
·   文檔是一些域的序列。
·   域是一些項的序列。
·   項就是一個(gè)字串。
存在于不同域中的同一個(gè)字串被認為是不同的項。因此項實(shí)際是用一對字串表示的,第一個(gè)字串是域名,第二個(gè)是域中的字串。

<!--[if !supportLists]-->4.1.   <!--[endif]-->Lucene概念詳細介紹

<!--[if !supportLists]-->4.1.1.    <!--[endif]-->域的類(lèi)型 

Lucene中,域的文本可能以逐字的非倒排的方式存儲在索引中。而倒排過(guò)的域稱(chēng)為被索引過(guò)了。域也可能同時(shí)被存儲和被索引。
   
域的文本可能被分解許多項目而被索引,或者就被用作一個(gè)項目而被索引。大多數的域是被分解過(guò)的,但是有些時(shí)候某些標識符域被當做一個(gè)項目索引是很有用的。

<!--[if !supportLists]-->4.1.2.    <!--[endif]--> 段(Segment 

Lucene索引可能由多個(gè)子索引組成,這些子索引成為段。每一段都是完整獨立的索引,能被搜索。索引是這樣作成的:
1.    
為新加入的文檔創(chuàng )建新段。
2.    
合并已經(jīng)存在的段。
搜索時(shí)需要涉及到多個(gè)段和/或者多個(gè)索引,每一個(gè)索引又可能由一些段組成。

<!--[if !supportLists]-->4.1.3.    <!--[endif]--> 文檔號(documentnbspNumber 

內部的來(lái)說(shuō),Lucene用一個(gè)整形(interger)的文檔號來(lái)指示文檔。第一個(gè)被加入到索引中的文檔就是0號,順序加入的文檔將得到一個(gè)由前一個(gè)號碼遞增而來(lái)的號碼。

注意文檔號是可能改變的,所以在Lucene外部存儲這些號碼時(shí)必須小心。特別的,號碼的改變的情況如下:

·   只 有段內的號碼是相同的,不同段之間不同,因而在一個(gè)比段廣泛的上下文環(huán)境中使用這些號碼時(shí),就必須改變它們。標準的技術(shù)是根據每一段號碼多少為每一段分配 一個(gè)段號。將段內文檔號轉換到段外時(shí),加上段號。將某段外的文檔號轉換到段內時(shí),根據每段中可能的轉換后號碼范圍來(lái)判斷文檔屬于那一段,并減調這一段的段 號。例如有兩個(gè)含5個(gè)文檔的段合并,那么第一段的段號就是0,第二段段號5。第二段中的第三個(gè)文檔,在段外的號碼就是8。

·   文檔刪除后,連續的號碼就出現了間斷。這可以通過(guò)合并索引來(lái)解決,段合并時(shí)刪除的文檔相應也刪掉了,新合并而成的段并沒(méi)有號碼間斷。

<!--[if !supportLists]-->4.1.4.    <!--[endif]--> 索引信息

索引段維護著(zhù)以下的信息:
·   域集合。包含了索引中用到的所有的域。
·   域值存儲表。每一個(gè)文檔都含有一個(gè)“屬性-值”對的列表,屬性即為域名。這個(gè)列表用來(lái)存儲文檔的一些附加信息,如標題,url或者訪(fǎng)問(wèn)數據庫的一個(gè)ID。在搜索時(shí)存儲域的集合可以被返回。這個(gè)表以文檔號標識。
·   項字典。這個(gè)字典含有所有文檔的所有域中使用過(guò)的的項,同時(shí)含有使用過(guò)它的文檔的文檔號,以及指向使用頻數信息和位置信息的指針。
·   項頻數信息。對于項字典中的每個(gè)項,這些信息包含含有這個(gè)項的文檔的總數,以及每個(gè)文檔中使用的次數。
·   項位置信息。對于項字典中的每個(gè)項,都存有在每個(gè)文檔中出現的各個(gè)位置。
·  標準化因子。對于文檔中的每一個(gè)域,存有一個(gè)值,用來(lái)以后乘以這個(gè)這個(gè)域的命中數(hits)。
·   被刪除的文檔信息。這是一個(gè)可選文件,用來(lái)表明那些文檔已經(jīng)刪除了。
接下來(lái)的各部分部分詳細描述這些信息。

<!--[if !supportLists]-->4.1.5.    <!--[endif]--> 文件的命名(File Naming

 同屬于一個(gè)段的文件擁有相同的文件名,不同的擴展名。擴展名由以下討論的各種文件格式確定。
    
一般來(lái)說(shuō),一個(gè)索引存放一個(gè)目錄,其所有段都存放在這個(gè)目錄里,不這樣作,也是可以的,在性能方面較低。

<!--[if !supportLists]-->4.2.   <!--[endif]-->Lucene基本數據類(lèi)型(Primitive Types

<!--[if !supportLists]-->4.2.1.    <!--[endif]-->字節Byte 

 最基本的數據類(lèi)型就是字節(byte,8位)。文件就是按字節順序訪(fǎng)問(wèn)的。其它的一些數據類(lèi)型也定義為字節的序列,文件的格式具有字節意義上的獨立性。
UInt32 
32位無(wú)符號整數,由四個(gè)字節組成,高位優(yōu)先。UInt32 --> <Byte>4 
Uint64 
 64位無(wú)符號整數,由八字節組成,高位優(yōu)先。UInt64 --> <Byte>8 
VInt 
 可變長(cháng)的正整數類(lèi)型,每字節的最高位表明還剩多少字節。每字節的低七位表明整數的值。因此單字節的值從0127,兩字節值從12816,383,等等。
VInt 
編碼示例
value 
 First byte 
 Second byte 
 Third byte 
 0 
 00000000 
  1 
 00000001 
  2 
 00000010 
  ... 
  127 
 01111111 
  128 
 10000000 
 00000001 
  129 
 10000001 
 00000001 
  130 
 10000010 
 00000001 
  ... 
  16,383 
 11111111 
 01111111 
  16,384 
 10000000 
 10000000 
 00000001 
 16,385 
 10000001 
 10000000 
 00000001 
 ... 
這種編碼提供了一種在高效率解碼時(shí)壓縮數據的方法。 

<!--[if !supportLists]-->4.2.2.       <!--[endif]-->字符串Chars  

Lucene輸出UNICODE字符序列,使用標準UTF-8編碼。
String 
Lucene輸出由VINT和字符串組成的字串,VINT表示字串長(cháng),字符串緊接其后。
String --> VInt, Chars 
 

<!--[if !supportLists]-->4.3.       <!--[endif]-->索引包含的文件(Per-Index Files 

<!--[if !supportLists]-->4.3.1.    <!--[endif]-->Segments文件 

 索引中活動(dòng)的段存儲在Segments文件中。每個(gè)索引只能含有一個(gè)這樣的文件,名為"segments".這個(gè)文件依次列出每個(gè)段的名字和每個(gè)段的大小。
Segments --> SegCount, <SegName, SegSize>SegCount 
SegCount, SegSize --> UInt32 
SegName --> String 
SegName
表示該segment的名字,同時(shí)作為索引其他文件的前綴。
SegSize
是段索引中含有的文檔數。

<!--[if !supportLists]-->4.3.2.    <!--[endif]--> Lock文件

 有一些文件用來(lái)表示另一個(gè)進(jìn)程在使用索引。
·   如果存在"commit.lock"文件,表示有進(jìn)程在寫(xiě)"segments"文件和刪除無(wú)用的段索引文件,或者表示有進(jìn)程在讀"segments"文件和打開(kāi)某些段的文件。在一個(gè)進(jìn)程在讀取"segments"文件段信息后,還沒(méi)來(lái)得及打開(kāi)所有該段的文件前,這個(gè)Lock文件可以防止另一個(gè)進(jìn)程刪除這些文件。
·   如果存在"index.lock"文件,表示有進(jìn)程在向索引中加入文檔,或者是從索引中刪除文檔。這個(gè)文件防止很多文件同時(shí)修改一個(gè)索引。

<!--[if !supportLists]-->4.3.3.    <!--[endif]--> Deleteable文件

名為"deletetable"的文件包含了索引不再使用的文件的名字,這些文件可能并沒(méi)有被實(shí)際的刪除。這種情況只存在與Win32平臺下,因為Win32下文件仍打開(kāi)時(shí)并不能刪除。
Deleteable --> DelableCount, <DelableName>DelableCount 
DelableCount --> UInt32 
DelableName --> String 

<!--[if !supportLists]-->4.3.4.    <!--[endif]--> 段包含的文件(Per-Segment Files

剩下的文件是每段中包含的文件,因此由后綴來(lái)區分。
域(Field
域集合信息(Field Info 
所有域名都存儲在這個(gè)文件的域集合信息中,這個(gè)文件以后綴.fnm結尾。

FieldInfos (.fnm) --> FieldsCount, <FieldName, FieldBits>FieldsCount 
FieldsCount --> VInt 
FieldName --> String 
FieldBits --> Byte 
目前情況下,FieldBits只有使用低位,對于已索引的域值為1,對未索引的域值為0。
文件中的域根據它們的次序編號。因此域0是文件中的第一個(gè)域,域1是接下來(lái)的,等等。這個(gè)和文檔號的編號方式相同。 

<!--[if !supportLists]-->4.3.5.    <!--[endif]-->域值存儲表(Stored Fields

域值存儲表使用兩個(gè)文件表示:
1.    
域索引(.fdx文件)。
如下,對于每個(gè)文檔這個(gè)文件包含指向域值的指針:
FieldIndex (.fdx) --> <FieldvaluesPosition>SegSize 
FieldvaluesPosition --> Uint64 
FieldvaluesPosition
指示的是某一文檔的某域的域值在域值文件中的位置。因為域值文件含有定長(cháng)的數據信息,因而很容易隨機訪(fǎng)問(wèn)。在域值文件中,文檔n的域值信息就存在n*8位置處(The position of documentnbspn‘s field data is the Uint64 at n*8 in this file.)。
2.    
域值(.fdt文件)。
如下,每個(gè)文檔的域值信息包含:
FieldData (.fdt) --> <DocFieldData>SegSize 
DocFieldData --> FieldCount, <FieldNum, Bits, value>FieldCount 
FieldCount --> VInt 
FieldNum --> VInt 
Bits --> Byte 
value --> String 
目前情況下,Bits只有低位被使用,值為1表示域名被分解過(guò),值為0表示未分解過(guò)。÷

<!--[if !supportLists]-->4.3.6.    <!--[endif]--> 項字典(Term Dictionary

 項字典用以下兩個(gè)文件表示:
1.    
項信息(.tis文件)。
TermInfoFile (.tis)--> TermCount, TermInfos 
TermCount --> UInt32 
TermInfos --> <TermInfo>TermCount 
TermInfo --> <Term, DocFreq, FreqDelta, ProxDelta> 
Term --> <PrefixLength, Suffix, FieldNum> 
Suffix --> String 
PrefixLength, DocFreq, FreqDelta, ProxDelta
--> VInt 

項信息按項排序。項信息排序時(shí)先按項所屬的域的文字順序排序,然后按照項的字串的文字順序排序。

項的字前綴往往是共同的,與字的后綴組成字。PrefixLength變量就是表示與前一項相同的前綴的字數。因此,如果前一個(gè)項的字是"bone",后一個(gè)是"boy"的話(huà),PrefixLength值為2,Suffix值為"y"。

FieldNum指明了項屬于的域號,而域名存儲在.fdt文件中。

DocFreg表示的是含有該項的文檔的數量。

FreqDelta指明了項所屬TermFreq變量在.frq文件中的位置。詳細的說(shuō),就是指相對于前一個(gè)項的數據的位置偏移量(或者是0,表示文件中第一個(gè)項)。

ProxDelta指明了項所屬的TermPosition變量在.prx文件中的位置。詳細的說(shuō),就是指相對于前一個(gè)項的數據的位置偏移量(或者是0,表示文件中第一個(gè)項)。

2.    項信息索引(.tii文件)。

每個(gè)項信息索引文件包含.tis文件中的128個(gè)條目,依照條目在.tis文件中的順序。這樣設計是為了一次將索引信息讀入內存能,然后使用它來(lái)隨機的訪(fǎng)問(wèn).tis文件。
這個(gè)文件的結構和.tis文件非常類(lèi)似,只在每個(gè)條目記錄上增加了一個(gè)變量IndexDelta。
TermInfoIndex (.tii)--> IndexTermCount, TermIndices 
IndexTermCount --> UInt32 
TermIndices --> <TermInfo, IndexDelta>IndexTermCount 
IndexDelta --> VInt 
IndexDelta
表示該項的TermInfo變量值在.tis文件中的位置。詳細的講,就是指相對于前一個(gè)條目的偏移量(或者是0,對于文件中第一個(gè)項)。 

<!--[if !supportLists]-->4.3.7.    <!--[endif]-->項頻數(Frequencies

  .frq文件包含每一項的文檔的列表,還有該項在對應文檔中出現的頻數。
FreqFile (.frq) --> <TermFreqs>TermCount 
TermFreqs --> <TermFreq>DocFreq 
TermFreq --> DocDelta, Freq? 
DocDelta,Freq --> VInt 
TermFreqs
序列按照項來(lái)排序(依據于.tis文件中的項,即項是隱含存在的)。
TermFreq
元組按照文檔號升序排列。
DocDelta
決定了文檔號和頻數。詳細的說(shuō),DocDelta/2表示相對于前一文檔號的偏移量(或者是0,表示這是TermFreqs里面的第一項)。當DocDelta是奇數時(shí)表示在該文檔中頻數為1,當DocDelta是偶數時(shí),另一個(gè)VIntFreq)就表示在該文檔中出現的頻數。
例如,假設某一項在文檔7中出現一次,在文檔11中出現了3次,在TermFreqs中就存在如下的VInts序列:
15, 22, 3 

<!--[if !supportLists]-->4.3.8.    <!--[endif]--> 項位置(Position 

.prx文件包含了某文檔中某項出現的位置信息的列表。
ProxFile (.prx) --> <TermPositions>TermCount 
TermPositions --> <Positions>DocFreq 
Positions --> <PositionDelta>Freq 
PositionDelta --> VInt 
TermPositions
按照項來(lái)排序(依據于.tis文件中的項,即項是隱含存在的)。
Positions
元組按照文檔號升序排列。
PositionDelta
是相對于前一個(gè)出現位置的偏移位置(或者為0,表示這是第一次在這個(gè)文檔中出現)。
例如,假設某一項在某文檔第4項出現,在另一個(gè)文檔中第5項和第9項出現,將存在如下的VInt序列:
4, 5, 4 

<!--[if !supportLists]-->4.3.9.    <!--[endif]--> 標準化因子(Normalization Factor 

 .nrm文件包含了每個(gè)文檔的標準化因子,標準化因子用來(lái)以后乘以這個(gè)這個(gè)域的命中數。
Norms (.nrm) --> <Byte>SegSize 
每個(gè)字節記錄一個(gè)浮點(diǎn)數。位0-2包含了3位的尾數部分,位3-8包含了5位的指數部分。
按如下規則可將這些字節轉換為IEEE標準單精度浮點(diǎn)數:
1.    
如果該字節是0,就是浮點(diǎn)0;
2.    
否則,設置新浮點(diǎn)數的標志位為0;
3.    
將字節中的指數加上48后作為新的浮點(diǎn)數的指數;
4.    
將字節中的尾數映射到新浮點(diǎn)數尾數的高3位;并且
5.    
設置新浮點(diǎn)數尾數的低21位為0。

<!--[if !supportLists]-->4.3.10.             <!--[endif]--> 被刪除的文檔(Deleted document

.del文件是可選的,只有在某段中存在刪除操作后才存在:
Deletions (.del) --> ByteCount,BitCount,Bits 
ByteSize,BitCount --> Uint32 
Bits --> <Byte>ByteCount 
ByteCount
表示的是Bits列表中Byte的數量。典型的,它等于(SegSize/8+1。
BitCount
表示Bits列表中多少個(gè)已經(jīng)被設置過(guò)了。
Bits
列表包含了一些位(bit),順序表示一個(gè)文檔。當對應于文檔號的位被設置了,就標志著(zhù)這個(gè)文檔已經(jīng)被刪除了。位的順序是從低到高。因此,如果Bits包含兩個(gè)字節,0x000x02,那么表示文檔9已經(jīng)刪除了。

<!--[if !supportLists]-->4.3.11.             <!--[endif]--> 局限性(Limitations

在以上的文件格式中,好幾處都有限制項和文檔的最大個(gè)數為32位數的極限,即接近于40億。今天看來(lái),這不會(huì )造成問(wèn)題,但是,長(cháng)遠的看,可能造成問(wèn)題。因此,這些極限應該或者換為UInt64類(lèi)型的值,或者更好的,換為VInt類(lèi)型的值(VInt值沒(méi)有上限)。

有兩處地方的代碼要求必須是定長(cháng)的值,他們是:

1.    FieldvaluesPosition變量(存儲于域索引文件中,.fdx文件)。它已經(jīng)是一個(gè)UInt64型,所以不會(huì )有問(wèn)題。

2.    TermCount
變量(存儲于項信息文件中,.tis文件)。這是最后輸出到文件中的,但是最先被讀取,因此是存儲于文件的最前端 。索引代碼先在這里寫(xiě)入一個(gè)0值,然后在其他文件輸出完畢后覆蓋這個(gè)值。所以無(wú)論它存儲在什么地方,它都必須是一個(gè)定長(cháng)的值,它應該被變成UInt64型。
   
除此之外,所有的UInt值都可以換成VInt型以去掉限制。

5.   <!--[endif]-->Lucene代碼分析

應用情景分析

Query query = parser.parse(queries[j]);

獲得布爾查詢(xún)

hits = searcher.search(query);

 

 return new Hits(this, query, filter);

  getMoreDocs(50)

    TopDocs topDocs = searcher.search(query, filter, n)

      IndexSearcher:public TopDocs search(Query query, Filter filter, final int nDocs)

<!--[if !supportLists]-->²         <!--[endif]-->IndexSearcher 開(kāi)始時(shí)已經(jīng)打開(kāi)了該目錄

<!--[if !supportLists]-->²         <!--[endif]-->IndexSearcher 中初始化了IndexReader

<!--[if !supportLists]-->²         <!--[endif]-->IndexReader中讀取了SegmentInfos

<!--[if !supportLists]-->²         <!--[endif]-->IndexReader SegmentReader

<!--[if !supportLists]-->²         <!--[endif]-->SegmentReader ::initialize(SegmentInfo si)

<!--[if !supportLists]-->n          <!--[endif]-->1。讀入域信息,只有域的名字

<!--[if !supportLists]-->n          <!--[endif]-->2. 打開(kāi)保存域、保存域索引的文件

       Scorer scorer = query.weight(this).scorer(reader)

<!--[if !supportLists]-->u        <!--[endif]-->這里query = PhraseQuery

<!--[if !supportLists]-->u        <!--[endif]-->query.weight(this) 獲得PhraseWeightIndexSearcher

<!--[if !supportLists]-->u        <!--[endif]-->PhraseWeight::scorer(IndexReader reader)

<!--[if !supportLists]-->u        <!--[endif]-->PhraseQuery::TermPositions p = reader.termPositions((Term)terms.elementAt(i));

<!--[if !supportLists]-->u        <!--[endif]-->public TermPositions termPositions(Term term) throws IOException {

IndexReader::TermPositions termPositions = termPositions();

SegmentTermDocs::SegmentTermDocs(SegmentReader parent)

          throws IOException {

    this.parent = parent;

    this.freqStream = (InputStream) parent.freqStream.clone();//頻率文件

    this.deletedDocs = parent.deletedDocs;

    this.skipInterval = parent.tis.getSkipInterval();

  }

SegmentTermPositions::SegmentTermPositions(SegmentReader p) throws IOException {

    super(p);

    this.proxStream = (InputStream)parent.proxStream.clone();//位置文件

  }

IndexReader  = SegmentReader, IndexSearcher

termPositions.seek(term);

 

SegmentTermDocs::public void seek(Term term) throws IOException {

TermInfo ti = parent.tis.get(term);// parent SegmentReader

// tis TermInfosReader

// 在初始化SegmentTermDocs的時(shí)候讀取文件并創(chuàng )建了

//  tis = new TermInfosReader(cfsDir, segment, fieldInfos);

/**

   * 1。從.tis文件中讀取相關(guān)的信息 項的迭代對象

   * 2。得到項的迭代對象

   * 3。該項讀取器 size 該項迭代對象的 size

   * 4。讀取索引,初始化了 索引指針,索引

   * */

    seek(ti);

  }

 

 

return termPositions;

<!--[if !supportLists]-->²         <!--[endif]-->SegmentReader. termPositions()::return SegmentTermPositions(this)`

 

 

 

<p>一個(gè)權重由query創(chuàng )建,并給查詢(xún)器({@link Query#createWeight(Searcher)})使用,方法 {@link #sumOfSquaredWeights()},然后被最高級的查詢(xún)api調用

用來(lái)計算查詢(xún)規范化因子 (@link Similarity#queryNorm(float)}),然后該因子傳給{@link #normalize(float)} 然后被{@link #scorer(IndexReader)}調用

 

應用情景分析

6.   <!--[endif]-->測試的主程序

規則:

加粗體的黑色代碼,表示將作深入分析

try {

      Directory directory = new RAMDirectory();

      Analyzer analyzer = new SimpleAnalyzer();

      IndexWriter writer = new IndexWriter(directory, analyzer, true);

      String[] docs = {

        "a b c d e",

        "a b c d e a b c d e",

        "a b c d e f g h i j",

        "a c e",

        "e c a",

        "a c e a c e",

        "a c e a b c"

      };

      for (int j = 0; j < docs.length; j++) {

        Document d = new Document();

        d.add(Field.Text("contents", docs[j]));

        writer.addDocument(d);

      }

      writer.close();

      以上代碼是準備工作,生成索引

      Searcher searcher = new IndexSearcher(directory);

      以上代碼,初始化查詢(xún),分析編號1。1

      String[] queries = {"\"a c e\"",

      };

      Hits hits = null;

      QueryParser parser = new QueryParser("contents", analyzer);

      parser.setPhraseSlop(0);

      for (int j = 0; j < queries.length; j++) {

        Query query = parser.parse(queries[j]);

        Query PhraseQuery

        System.out.println("Query: " + query.toString("contents"));

        hits = searcher.search(query);

        以上代碼,初始化查詢(xún),分析編號1。2

        System.out.println(hits.length() + " total results");

        for (int i = 0 ; i < hits.length() && i < 10; i++) {

          Document d = hits.doc(i);

          System.out.println(i + " " + hits.score(i)

//                                                                           + " " + DateField.stringToDate(d.get("modified"))

            + " " + d.get("contents"));

        }

      }

      searcher.close();

    } catch (Exception e) {

      System.out.println(" caught a " + e.getClass() +

                                                                            "\n with message: " + e.getMessage());

}

 

查詢(xún)結果:

Query: "a c e"

3 total results

0 1.0 a c e a c e

1 0.9428091 a c e

2 0.7071068 a c e a b c

<!--[if !supportLists]-->1.1.   <!--[endif]-->Searcher searcher = new IndexSearcher(directory)

<!--[if !supportLists]-->1.1.1.    <!--[endif]-->初始化

通過(guò)目錄,創(chuàng )建一個(gè)索引搜索器,

調用類(lèi)

IndexSearcher ::public IndexSearcher(Directory directory) throws IOException {

    this(IndexReader.open(directory), true);

  }

調用

private IndexSearcher(IndexReader r, boolean closeReader) {

    reader = r;

    this.closeReader = closeReader;

  }

調用

private static IndexReader open(final Directory directory, final boolean closeDirectory) throws IOException {

    synchronized (directory) {                          // in- & inter-process sync

      return (IndexReader)new Lock.With(

          directory.makeLock(IndexWriter.COMMIT_LOCK_NAME),

          IndexWriter.COMMIT_LOCK_TIMEOUT) {

          public Object doBody() throws IOException {

            SegmentInfos infos = new SegmentInfos();

                            從目錄中讀取SegmentInfos

            infos.read(directory);

            if (infos.size() == 1) {                 // index is optimized

              return new SegmentReader(infos, infos.info(0), closeDirectory);

            } else {

              IndexReader[] readers = new IndexReader[infos.size()];

              for (int i = 0; i < infos.size(); i++)

                readers[i] = new SegmentReader(infos.info(i));

              return new MultiReader(directory, infos, closeDirectory, readers);

            }

          }

        }.run();

    }

  }

代碼到這里,已經(jīng)讀取了文件segments文件,獲得段信息,該測試只有一個(gè)段,所以執行了return new SegmentReader(infos, infos.info(0), closeDirectory);,記住IndexReader = SegmentReader

 

infos.read(directory):

/** 讀取輸入參數的目錄,下的segments文件

   * 代碼分析:

   * 1。讀取格式,小于0表示該文件有隱含的格式信息,小于-1就表示該格式是未知的,因為最小的格式是-1

   * 2。小于0時(shí),再讀取版本信息以及段的計數

   * 3。大于0,表示segments文件開(kāi)頭部分沒(méi)有版本信息,只有段的計數

   * 4。讀取段的數量

   * 5。循環(huán)讀取段信息,然后構建段信息對象,最后把這些對象都加入到段集合中

   * 6。大于0時(shí),判斷是否文件最后有版本信息,有的話(huà)就賦值version,沒(méi)有的話(huà),version 0 */,該段代碼比較簡(jiǎn)單,讀者可以從看src中代碼

return new SegmentReader(infos, infos.info(0), closeDirectory);

SegmentReader(SegmentInfos sis, SegmentInfo si, boolean closeDir)

          throws IOException {

    super(si.dir, sis, closeDir);

    initialize(si);

  }

super(si.dir, sis, closeDir);

IndexReader ::IndexReader(Directory directory, SegmentInfos segmentInfos, boolean closeDirectory) {

    this.directory = directory;

    this.segmentInfos = segmentInfos;

    directoryOwner = true;

    this.closeDirectory = closeDirectory;

    stale = false;

    hasChanges = false;

    writeLock = null;

  }

SegmentReader ::initialize(si);

/** 初始化這個(gè)段信息

   該段代碼是初始化了

   * 1。讀入域信息,只有域的名字

   * 2. 打開(kāi)保存域、保存域索引的文件

   */

private void initialize(SegmentInfo si) throws IOException

   {

    segment = si.name;

    // Use compound file directory for some files, if it exists

    Directory cfsDir = directory();// 就是保存該段的目錄

    // CompoundFileReader(組合文件讀取器)也是(目錄)的子類(lèi)

    if (directory().fileExists(segment + ".cfs")) {

      cfsReader = new CompoundFileReader(directory(), segment + ".cfs");

      cfsDir = cfsReader;

    }

    // 1。讀入域信息,只有域的名字

    fieldInfos = new FieldInfos(cfsDir, segment + ".fnm"); // 這個(gè)過(guò)程讀入所有的域信息了

    // 2。打開(kāi)保存域、保存域索引的文件

    fieldsReader = new FieldsReader(cfsDir, segment, fieldInfos);

    tis = new TermInfosReader(cfsDir, segment, fieldInfos);

    if (hasDeletions(si))

      deletedDocs = new BitVector(directory(), segment + ".del");// 讀入刪除表

    freqStream = cfsDir.openFile(segment + ".frq");// 讀入頻率文件

    proxStream = cfsDir.openFile(segment + ".prx");// 讀入位置文件

    openNorms(cfsDir);// 讀入文件segment.f1,segment.f2……,建立hashtable

    if (fieldInfos.hasVectors()) { // open term vector files only as needed

      termVectorsReader = new TermVectorsReader(cfsDir, segment, fieldInfos);

    }

  }

<!--[if !supportLists]-->1.2.   <!--[endif]-->hits = searcher.search(query);

這時(shí),searcher IndexSearcher,對該代碼的跟蹤如下:

調用:return search(query, (Filter)null)

調用:return new Hits(this, query, filter);

調用:Hit::Hits(Searcher s, Query q, Filter f) throws IOException {

    query = q;

    searcher = s;

    filter = f;

    getMoreDocs(50); // retrieve 100 initially

  }

getMoreDocs(int min)調用::TopDocs topDocs =  searcher.search(query, filter, n)

searcher.search(query, filter, n) 調用Scorer scorer = query.weight(this).scorer(reader);

 

IndexSearcher::public TopDocs search(Query query, Filter filter, final int nDocs)

       throws IOException {

    Scorer scorer = query.weight(this).scorer(reader);

    if (scorer == null)

      return new TopDocs(0, new ScoreDoc[0]);

    final BitSet bits = filter != null ? filter.bits(reader) : null;

    final HitQueue hq = new HitQueue(nDocs);

    final int[] totalHits = new int[1];

    scorer.score(new HitCollector() {

        private float minScore = 0.0f;

         public final void collect(int doc, float score) {

           if (score > 0.0f &&                       // ignore zeroed buckets

               (bits==null || bits.get(doc))) {        // skip docs not in bits

             totalHits[0]++;

            if (hq.size() < nDocs || score >= minScore) {

              hq.insert(new ScoreDoc(doc, score));

              minScore = ((ScoreDoc)hq.top()).score; // maintain minScore

            }

           }

         }

      });

    ScoreDoc[] scoreDocs = new ScoreDoc[hq.size()];

    for (int i = hq.size()-1; i >= 0; i--)      // put docs in array

      scoreDocs[i] = (ScoreDoc)hq.pop();

    return new TopDocs(totalHits[0], scoreDocs);

  }

<!--[if !supportLists]-->1.2.1.    <!--[endif]-->Scorer scorer = query.weight(this).scorer(reader);

參數分析:query PhraseQuery (該參數由主測試程序中的Query query = parser.parse(queries[j]);初始化)

this IndexSearcher(該參數初始化,已經(jīng)初始化了主要的文件,具體可參考1.1

由代碼

1  PhraseQuery::

protected Weight createWeight(Searcher searcher) {

    if (terms.size() == 1) {                       // optimize one-term case

      Term term = (Term)terms.elementAt(0);

      Query termQuery = new TermQuery(term);

      termQuery.setBoost(getBoost());

      return termQuery.createWeight(searcher);

    }

    return new PhraseWeight(searcher);

  }

query.weight(this) 創(chuàng )建了PhraseWeight(searcher)

Scorer scorer = query.weight(this).scorer(reader) 就相當于PhraseWeight(searcher). .scorer(reader),即調用以下代碼:

2  PhraseQuery::

public Scorer scorer(IndexReader reader) throws IOException {

      if (terms.size() == 0)                      // optimize zero-term case

        return null;

      // 讀取項的 位置信息

      TermPositions[] tps = new TermPositions[terms.size()];

      for (int i = 0; i < terms.size(); i++) {

        TermPositions p = reader.termPositions((Term)terms.elementAt(i));

        if (p == null)

          return null;

        tps[i] = p;

      }

      得到所有項的項信息,TermPositions[ ] SegmentTermPositions[ ]

      if (slop == 0)                                  // optimize exact case

        return new ExactPhraseScorer(this, tps, getPositions(), getSimilarity(searcher),

                                     reader.norms(field));

    }

 

<!--[if !supportLists]-->ü          <!--[endif]-->TermPositions p = reader.termPositions((Term)terms.elementAt(i));

這時(shí)Term文本為查詢(xún)里的項

public TermPositions termPositions(Term term) throws IOException {

    TermPositions termPositions = termPositions();

    termPositions.seek(term);

    return termPositions;

  }

termPositions()::

SegmentReader ::public final TermPositions termPositions() throws IOException {

    return new SegmentTermPositions(this);

  }

parent SegmentReader,即剛才的段讀取器

tis = new TermInfosReader(cfsDir, segment, fieldInfos); 即項信息讀取器

SegmentTermPositions(this)::

SegmentTermPositions ::SegmentTermPositions(SegmentReader p) throws IOException {

    super(p);

    this.proxStream = (InputStream)parent.proxStream.clone();

  }

super(p)::

SegmentTermDocs(SegmentReader parent)

          throws IOException {

    this.parent = parent;

    this.freqStream = (InputStream) parent.freqStream.clone();

    this.deletedDocs = parent.deletedDocs;

    this.skipInterval = parent.tis.getSkipInterval();

  }

 

termPositions.seek(term);

public void seek(Term term) throws IOException {

      根據項,從項信息讀取器中讀取對應的項信息,該方法是線(xiàn)程安全的

    TermInfo ti = parent.tis.get(term);

    seek(ti);

  }

seek(TermInfo ti)

SegmentTermDocs的項信息轉變?yōu)楝F在讀入的項的信息

void seek(TermInfo ti) throws IOException {

    count = 0;

    if (ti == null) {

      df = 0;

    } else {

      df = ti.docFreq;

      doc = 0;

      skipDoc = 0;

      skipCount = 0;

      numSkips = df / skipInterval;

      freqPointer = ti.freqPointer;

      proxPointer = ti.proxPointer;

      skipPointer = freqPointer + ti.skipOffset;

      freqStream.seek(freqPointer);

      haveSkipped = false;

    }

  }

 

new ExactPhraseScorer(this, tps, getPositions(), getSimilarity(searcher),reader.norms(field));

調用構造器

ExactPhraseScorer(Weight weight, TermPositions[] tps, int[] positions, Similarity similarity,

                    byte[] norms) throws IOException {

    super(weight, tps, positions, similarity, norms);

調用超類(lèi)構造器,獲得短語(yǔ)位置的頻繁度信息和位置信息,并構造一個(gè)優(yōu)先隊列

PhraseScorer(Weight weight, TermPositions[] tps, int[] positions, Similarity similarity,

               byte[] norms) {

           super(similarity);

    this.norms = norms;

    this.weight = weight;

    this.value = weight.getValue();

 

         // convert tps to a list

         // PhrasePositions 放在一個(gè)一般的隊列里面(以鏈表形式)

         for (int i = 0; i < tps.length; i++) {

           PhrasePositions pp = new PhrasePositions(tps[i], positions[i]);

           if (last != null) {                        // add next to end of list

           last.next = pp;

         } else

        first = pp;

         last = pp;

         }

         pq = new PhraseQueue(tps.length);             // construct empty pq

        }

使用該記分器記分,并收集

scorer.score(new HitCollector()

public void score(HitCollector hc) throws IOException {

    while (next()) {

      hc.collect(doc(), score());

    }

  }

hc.collect(doc(), score());

score()調用,value為權值

PhraseScorer::public float score() throws IOException {

    //System.out.println("scoring " + first.doc);

    float raw = getSimilarity().tf(freq) * value; // raw score

    return raw * Similarity.decodeNorm(norms[first.doc]); // normalize

  }

把各個(gè)位置的文檔和得分收集

public final void collect(int doc, float score) {

                    if (score > 0.0f &&                       // ignore zeroed buckets

                        (bits==null || bits.get(doc))) {        // skip docs not in bits

                      totalHits[0]++;

            if (hq.size() < nDocs || score >= minScore) {

                       hq.insert(new ScoreDoc(doc, score));

                        minScore = ((ScoreDoc)hq.top()).score; // maintain minScore

                 }

                          }

                  }

到這里就出來(lái)了查詢(xún)的文檔和分數,并且這些文檔和分數經(jīng)過(guò)了指定的排序和過(guò)濾



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1454992


[收藏到我的網(wǎng)摘]   [發(fā)送Trackback]  Johnny.Deng發(fā)表于 2006年12月23日 07:42:00
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
Lucene學(xué)習總結之二:Lucene的總體架構
Lucene里經(jīng)常被聊到的幾個(gè)話(huà)題【基礎】
Lucence的結構 - neuron - 計世博客
使用 Apache Lucene 搜索文本 - - JavaEye技術(shù)網(wǎng)站
Web-第二十八天 Lucene&solr使用一【悟空教程】
如何讀取Lucene索引數據 - lewutian@126的日志 - 網(wǎng)易博客
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久