Lucene提供了方便您創建自建查詢的API,也通過QueryParser提供了強大的查詢語言。
本文講述Lucene的查詢語句解析器支持的語法,Lucene的查詢語句解析器是使用JavaCC工
具生成的詞法解析器,它將查詢字串解析為Lucene Query對象。
項(Term)
一條搜索語句被拆分為一些項(term)和操作符(operator)。項有兩種類型:單獨項和
短語。
單獨項就是一個單獨的單詞,例如"test" , "hello"。
短語是一組被雙引號包圍的單詞,例如"hello dolly"。
多個項可以用布爾操作符連接起來形成復雜的查詢語句(接下來您就會看到)。
注意:Analyzer建立索引時使用的解析器和解析單獨項和短語時的解析器相同,因此選擇
一個不會受查詢語句干擾的Analyzer非常重要。luence1.4的StandardAnalyzer的解析器已
經支持中文等亞洲國家的文字了,可以直接。標準的解析其不支持中文。
域(Field)
Lucene支持域。您可以指定在某一個域中搜索,或者就使用默認域。域名及默認域是具體
索引器實現決定的。(怎么定制默認域?)
您可以這樣搜索域:域名+":"+搜索的項名。
舉個例子,假設某一個Lucene索引包含兩個域,title和text,text是默認域。如果您想查
找標題為"The Right Way"且含有"don't go this way"的文章,您可以輸入:
title:"The Right Way" AND text:go
或者
title:"Do it right" AND right
因為text是默認域,所以這個域名可以不行。
注意:域名只對緊接于其后的項生效,所以
title:Do it right
只有"Do"屬于title域。"it"和"right"仍將在默認域中搜索(這里是text域)。
項修飾符(Term Modifiers)
Lucene支持項修飾符以支持更寬范圍的搜索選項。
用通配符搜索
Lucene支持單個與多個字符的通配搜索。
使用符號"?"表示單個任意字符的通配。
使用符號"*"表示多個任意字符的通配。
單個任意字符匹配的是所有可能單個字符。例如,搜索"text或者"test",可以這樣:
te?t
多個任意字符匹配的是0個及更多個可能字符。例如,搜索test, tests 或者 tester,可
以這樣: test*
您也可以在字符竄中間使用多個任意字符通配符。 te*t
注意:您不能在搜索的項開始使用*或者?符號。
模糊查詢
Lucene支持基于Levenshtein Distance與Edit Distance算法的模糊搜索。要使用模糊搜索
只需要在單獨項的最后加上符號"~"。例如搜索拼寫類似于"roam"的項這樣寫:
roam~
這次搜索將找到形如foam和roams的單詞。
注意:使用模糊查詢將自動得到增量因子(boost factor)為0.2的搜索結果.
鄰近搜索(Proximity Searches)
Lucene還支持查找相隔一定距離的單詞。鄰近搜索是在短語最后加上符號"~"。例如在文檔
中搜索相隔10個單詞的"apache"和"jakarta",這樣寫: "jakarta apache"~10
Boosting a Term
Lucene provides the relevance level of matching documents based on the terms
found. To boost a term use the caret, "^", symbol with a boost factor (a
number) at the end of the term you are searching. The higher the boost factor,
the more relevant the term will be.
Lucene可以設置在搜索時匹配項的相似度。在項的最后加上符號"^"緊接一個數字(增量值
),表示搜索時的相似度。增量值越高,搜索到的項相關度越好。
Boosting allows you to control the relevance of a document by boosting its
term. For example, if you are searching for jakarta apache and you want the
term "jakarta" to be more relevant boost it using the ^ symbol along with the
boost factor next to the term. You would type:
通過增量一個項可以控制搜索文檔時的相關度。例如如果您要搜索jakarta apache,同時
您想讓"jakarta"的相關度更加好,那么在其后加上"^"符號和增量值,也就是您輸入:
jakarta^4 apache
This will make documents with the term jakarta appear more relevant. You can
also boost Phrase Terms as in the example:
這將使得生成的doucment盡可能與jakarta相關度高。您也可以增量短語,象以下這個例子
一樣:
"jakarta apache"^4 "jakarta lucene"
By default, the boost factor is 1. Although, the boost factor must be positive,
it can be less than 1 (i.e. .2)
默認情況下,增量值是1。增量值也可以小于1(例如0.2),但必須是有效的。
布爾操作符
布爾操作符可將項通過邏輯操作連接起來。Lucene支持AND, "+", OR, NOT 和 "-"這些操
作符。(注意:布爾操作符必須全部大寫)
OR
OR操作符是默認的連接操作符。這意味著如果兩個項之間沒有布爾操作符,就是使用OR操
作符。OR操作符連接兩個項,意味著查找含有任意項的文檔。這與集合并運算相同。符號
||可以代替符號OR。
搜索含有"jakarta apache" 或者 "jakarta"的文檔,可以使用這樣的查詢:
"jakarta apache" jakarta 或者 "jakarta apache" OR jakarta
AND
AND操作符匹配的是兩項同時出現的文檔。這個與集合交操作相等。符號&&可以代替符號
AND。
搜索同時含有"jakarta apache" 與 "jakarta lucene"的文檔,使用查詢:
"jakarta apache" AND "jakarta lucene"
+
"+"操作符或者稱為存在操作符,要求符號"+"后的項必須在文檔相應的域中存在。
搜索必須含有"jakarta",可能含有"lucene"的文檔,使用查詢:
+jakarta apache
NOT
NOT操作符排除那些含有NOT符號后面項的文檔。這和集合的差運算相同。符號!可以代替
符號NOT。
搜索含有"jakarta apache",但是不含有"jakarta lucene"的文檔,使用查詢:
"jakarta apache" NOT "jakarta lucene"
注意:NOT操作符不能單獨與項使用構成查詢。例如,以下的查詢查不到任何結果:
NOT "jakarta apache"
-
"-"操作符或者禁止操作符排除含有"-"后面的相似項的文檔。
搜索含有"jakarta apache",但不是"jakarta lucene",使用查詢:
"jakarta apache" -"jakarta lucene"
分組(Grouping)
Lucene支持使用圓括號來組合字句形成子查詢。這對于想控制查詢布爾邏輯的人十分有用
。
搜索含有"jakarta"或者"apache",同時含有"website"的文檔,使用查詢:
(jakarta OR apache) AND website
這樣就消除了歧義,保證website必須存在,jakarta和apache中之一也存在。
轉義特殊字符(Escaping Special Characters)
Lucene支持轉義特殊字符,因為特殊字符是查詢語法用到的。現在,特殊字符包括
+ - && || ! ( ) { } [ ] ^ " ~ * ? : \
轉義特殊字符只需在字符前加上符號\,例如搜索(1+1):2,使用查詢
\(1\+1\)\:2??
(李宇翻譯,來自Lucene的幫助文檔)上面這段看了之后很有幫助,解除了使用中的不少
疑惑,謝謝翻譯者,同時應該看到,有的時候詳細查看使用幫助文檔是非常有用的。
------------------------------------------------------------------------------
索引文件格式
本文定義了Lucene(版本1.3)用到的索引文件的格式。
Jakarta Lucene是用Java寫成的,同時有很多團體正在默默的用其他的程序語言來改寫它
。如果這些新的版本想和Jakarta Lucene兼容,就需要一個與具體語言無關的Lucene索引
文件格式。本文正是試圖提供一個完整的與語言無關的Jakarta Lucene 1.3索引文件格式
的規格定義。
隨著Lucene不斷發展,本文也應該更新。不同語言寫成的Lucene實現版本應當盡力遵守文
件格式,也必須產生本文的新版本。
本文同時提供兼容性批注,描述文件格式上與前一版本不同的地方。
定義
Lucene中最基礎的概念是索引(index),文檔(document),域(field)和項(term)
。
索引包含了一個文檔的序列。
· 文檔是一些域的序列。
· 域是一些項的序列。
· 項就是一個字串。
存在于不同域中的同一個字串被認為是不同的項。因此項實際是用一對字串表示的,第一
個字串是域名,第二個是域中的字串。
倒排索引
為了使得基于項的搜索更有效率,索引中項是靜態存儲的。Lucene的索引屬于索引方式中
的倒排索引,因為對于一個項這種索引可以列出包含它的文檔。這剛好是文檔與項自然聯
系的倒置。
域的類型
Lucene中,域的文本可能以逐字的非倒排的方式存儲在索引中。而倒排過的域稱為被索引
過了。域也可能同時被存儲和被索引。
域的文本可能被分解許多項目而被索引,或者就被用作一個項目而被索引。大多數的域是
被分解過的,但是有些時候某些標識符域被當做一個項目索引是很有用的。
段(Segment)
Lucene索引可能由多個子索引組成,這些子索引成為段。每一段都是完整獨立的索引,能
被搜索。索引是這樣作成的:
1. 為新加入的文檔創建新段。
2. 合并已經存在的段。
搜索時需要涉及到多個段和/或者多個索引,每一個索引又可能由一些段組成。
文檔號(Document Number)
內部的來說,Lucene用一個整形(interger)的文檔號來指示文檔。第一個被加入到索引
中的文檔就是0號,順序加入的文檔將得到一個由前一個號碼遞增而來的號碼。
注意文檔號是可能改變的,所以在Lucene外部存儲這些號碼時必須小心。特別的,號碼的
改變的情況如下:
· 只有段內的號碼是相同的,不同段之間不同,因而在一個比段廣泛的上下文環境中使用
這些號碼時,就必須改變它們。標準的技術是根據每一段號碼多少為每一段分配一個段號
。將段內文檔號轉換到段外時,加上段號。將某段外的文檔號轉換到段內時,根據每段中
可能的轉換后號碼范圍來判斷文檔屬于那一段,并減調這一段的段號。例如有兩個含5個文
檔的段合并,那么第一段的段號就是0,第二段段號5。第二段中的第三個文檔,在段外的
號碼就是8。
· 文檔刪除后,連續的號碼就出現了間斷。這可以通過合并索引來解決,段合并時刪除的
文檔相應也刪掉了,新合并而成的段并沒有號碼間斷。
緒論
索引段維護著以下的信息:
· 域集合。包含了索引中用到的所有的域。
· 域值存儲表。每一個文檔都含有一個“屬性-值”對的列表,屬性即為域名。這個列表
用來存儲文檔的一些附加信息,如標題,url或者訪問數據庫的一個ID。在搜索時存儲域的
集合可以被返回。這個表以文檔號標識。
· 項字典。這個字典含有所有文檔的所有域中使用過的的項,同時含有使用過它的文檔的
文檔號,以及指向使用頻數信息和位置信息的指針。
· 項頻數信息。對于項字典中的每個項,這些信息包含含有這個項的文檔的總數,以及每
個文檔中使用的次數。
· 項位置信息。對于項字典中的每個項,都存有在每個文檔中出現的各個位置。
· Normalization factors. For each field in each document, a value is stored
that is multiplied into the score for hits on that field. 標準化因子。對于文檔
中的每一個域,存有一個值,用來以后乘以這個這個域的命中數(hits)。
· 被刪除的文檔信息。這是一個可選文件,用來表明那些文檔已經刪除了。
接下來的各部分部分詳細描述這些信息。
文件的命名(File Naming)
同屬于一個段的文件擁有相同的文件名,不同的擴展名。擴展名由以下討論的各種文件格
式確定。
一般來說,一個索引存放一個目錄,其所有段都存放在這個目錄里,盡管我們不要求您這
樣做。
基本數據類型(Primitive Types)
Byte
最基本的數據類型就是字節(byte,8位)。文件就是按字節順序訪問的。其它的一些數據
類型也定義為字節的序列,文件的格式具有字節意義上的獨立性。
UInt32
32位無符號整數,由四個字節組成,高位優先。
UInt32 --> <Byte>4
Uint64
64位無符號整數,由八字節組成,高位優先。
UInt64 --> <Byte>8
VInt
可變長的正整數類型,每字節的最高位表明還剩多少字節。每字節的低七位表明整數的值
。因此單字節的值從0到127,兩字節值從128到16,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
...
這種編碼提供了一種在高效率解碼時壓縮數據的方法。
Chars
Lucene輸出UNICODE字符序列,使用標準UTF-8編碼。
String
Lucene輸出由VINT和字符串組成的字串,VINT表示字串長,字符串緊接其后。
String --> VInt, Chars
索引包含的文件(Per-Index Files)
這部分介紹每個索引包含的文件。
Segments文件
索引中活動的段存儲在Segments文件中。每個索引只能含有一個這樣的文件,名
為"segments".這個文件依次列出每個段的名字和每個段的大小。
Segments --> SegCount, <SegName, SegSize>SegCount
SegCount, SegSize --> UInt32
SegName --> String
SegName表示該segment的名字,同時作為索引其他文件的前綴。
SegSize是段索引中含有的文檔數。
Lock文件
有一些文件用來表示另一個進程在使用索引。
· 如果存在"commit.lock"文件,表示有進程在寫"segments"文件和刪除無用的段索引文
件,或者表示有進程在讀"segments"文件和打開某些段的文件。在一個進程在讀
取"segments"文件段信息后,還沒來得及打開所有該段的文件前,這個Lock文件可以防止
另一個進程刪除這些文件。
· 如果存在"index.lock"文件,表示有進程在向索引中加入文檔,或者是從索引中刪除文
檔。這個文件防止很多文件同時修改一個索引。
Deleteable文件
名為"deletetable"的文件包含了索引不再使用的文件的名字,這些文件可能并沒有被實際
的刪除。這種情況只存在與Win32平臺下,因為Win32下文件仍打開時并不能刪除。
Deleteable --> DelableCount, <DelableName>DelableCount
DelableCount --> UInt32
DelableName --> String
段包含的文件(Per-Segment Files)
剩下的文件是每段中包含的文件,因此由后綴來區分。
域(Field)
域集合信息(Field Info)
所有域名都存儲在這個文件的域集合信息中,這個文件以后綴.fnm結尾。
FieldInfos (.fnm) --> FieldsCount, <FieldName, FieldBits>FieldsCount
FieldsCount --> VInt
FieldName --> String
FieldBits --> Byte
目前情況下,FieldBits只有使用低位,對于已索引的域值為1,對未索引的域值為0。
文件中的域根據它們的次序編號。因此域0是文件中的第一個域,域1是接下來的,等等。
這個和文檔號的編號方式相同。
域值存儲表(Stored Fields)
域值存儲表使用兩個文件表示:
1. 域索引(.fdx文件)。
如下,對于每個文檔這個文件包含指向域值的指針:
FieldIndex (.fdx) --> <FieldValuesPosition>SegSize
FieldValuesPosition --> Uint64
FieldValuesPosition指示的是某一文檔的某域的域值在域值文件中的位置。因為域值文件
含有定長的數據信息,因而很容易隨機訪問。在域值文件中,文檔n的域值信息就存在n*8
位置處(The position of document n's field data is the Uint64 at n*8 in this
file.)。
2. 域值(.fdt文件)。
如下,每個文檔的域值信息包含:
FieldData (.fdt) --> <DocFieldData>SegSize
DocFieldData --> FieldCount, <FieldNum, Bits, Value>FieldCount
FieldCount --> VInt
FieldNum --> VInt
Bits --> Byte
Value --> String
目前情況下,Bits只有低位被使用,值為1表示域名被分解過,值為0表示未分解過。
項字典(Term Dictionary)
項字典用以下兩個文件表示:
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
項信息按項排序。項信息排序時先按項所屬的域的文字順序排序,然后按照項的字串的文
字順序排序。
項的字前綴往往是共同的,與字的后綴組成字。PrefixLength變量就是表示與前一項相同
的前綴的字數。因此,如果前一個項的字是"bone",后一個是"boy"的話,PrefixLength值
為2,Suffix值為"y"。
FieldNum指明了項屬于的域號,而域名存儲在.fdt文件中。
DocFreg表示的是含有該項的文檔的數量。
FreqDelta指明了項所屬TermFreq變量在.frq文件中的位置。詳細的說,就是指相對于前一
個項的數據的位置偏移量(或者是0,表示文件中第一個項)。
ProxDelta指明了項所屬的TermPosition變量在.prx文件中的位置。詳細的說,就是指相對
于前一個項的數據的位置偏移量(或者是0,表示文件中第一個項)。
2. 項信息索引(.tii文件)。
每個項信息索引文件包含.tis文件中的128個條目,依照條目在.tis文件中的順序。這樣設
計是為了一次將索引信息讀入內存能,然后使用它來隨機的訪問.tis文件。
這個文件的結構和.tis文件非常類似,只在每個條目記錄上增加了一個變量IndexDelta。
TermInfoIndex (.tii)--> IndexTermCount, TermIndices
IndexTermCount --> UInt32
TermIndices --> <TermInfo, IndexDelta>IndexTermCount
IndexDelta --> VInt
IndexDelta表示該項的TermInfo變量值在.tis文件中的位置。詳細的講,就是指相對于前
一個條目的偏移量(或者是0,對于文件中第一個項)。
項頻數(Frequencies)
.frq文件包含每一項的文檔的列表,還有該項在對應文檔中出現的頻數。
FreqFile (.frq) --> <TermFreqs>TermCount
TermFreqs --> <TermFreq>DocFreq
TermFreq --> DocDelta, Freq?
DocDelta,Freq --> VInt
TermFreqs序列按照項來排序(依據于.tis文件中的項,即項是隱含存在的)。
TermFreq元組按照文檔號升序排列。
DocDelta決定了文檔號和頻數。詳細的說,DocDelta/2表示相對于前一文檔號的偏移量(
或者是0,表示這是TermFreqs里面的第一項)。當DocDelta是奇數時表示在該文檔中頻數
為1,當DocDelta是偶數時,另一個VInt(Freq)就表示在該文檔中出現的頻數。
例如,假設某一項在文檔7中出現一次,在文檔11中出現了3次,在TermFreqs中就存在如下
的VInts序列: 15, 22, 3
項位置(Position)
.prx文件包含了某文檔中某項出現的位置信息的列表。
ProxFile (.prx) --> <TermPositions>TermCount
TermPositions --> <Positions>DocFreq
Positions --> <PositionDelta>Freq
PositionDelta --> VInt
TermPositions按照項來排序(依據于.tis文件中的項,即項是隱含存在的)。
Positions元組按照文檔號升序排列。
PositionDelta是相對于前一個出現位置的偏移位置(或者為0,表示這是第一次在這個文
檔中出現)。
例如,假設某一項在某文檔第4項出現,在另一個文檔中第5項和第9項出現,將存在如下的
VInt序列: 4, 5, 4
標準化因子(Normalization Factor)
.nrm文件包含了每個文檔的標準化因子,標準化因子用來以后乘以這個這個域的命中數。
Norms (.nrm) --> <Byte>SegSize
每個字節記錄一個浮點數。位0-2包含了3位的尾數部分,位3-8包含了5位的指數部分。
按如下規則可將這些字節轉換為IEEE標準單精度浮點數:
1. 如果該字節是0,就是浮點0;
2. 否則,設置新浮點數的標志位為0;
3. 將字節中的指數加上48后作為新的浮點數的指數;
4. 將字節中的尾數映射到新浮點數尾數的高3位;并且
5. 設置新浮點數尾數的低21位為0。
被刪除的文檔(Deleted Document)
.del文件是可選的,只有在某段中存在刪除操作后才存在:
Deletions (.del) --> ByteCount,BitCount,Bits
ByteSize,BitCount --> Uint32
Bits --> <Byte>ByteCount
ByteCount表示的是Bits列表中Byte的數量。典型的,它等于(SegSize/8)+1。
BitCount表示Bits列表中多少個已經被設置過了。
Bits列表包含了一些位(bit),順序表示一個文檔。當對應于文檔號的位被設置了,就標
志著這個文檔已經被刪除了。位的順序是從低到高。因此,如果Bits包含兩個字節,0x00
和0x02,那么表示文檔9已經刪除了。
局限性(Limitations)
在以上的文件格式中,好幾處都有限制項和文檔的最大個數為32位數的極限,即接近于40
億。今天看來,這不會造成問題,但是,長遠的看,可能造成問題。因此,這些極限應該
或者換為UInt64類型的值,或者更好的,換為VInt類型的值(VInt值沒有上限)。
有兩處地方的代碼要求必須是定長的值,他們是:
1. FieldValuesPosition變量(存儲于域索引文件中,.fdx文件)。它已經是一個UInt64
型,所以不會有問題。
2. TermCount變量(存儲于項信息文件中,.tis文件)。這是最后輸出到文件中的,但是
最先被讀取,因此是存儲于文件的最前端 。索引代碼先在這里寫入一個0值,然后在其他
文件輸出完畢后覆蓋這個值。所以無論它存儲在什么地方,它都必須是一個定長的值,它
應該被變成UInt64型。
除此之外,所有的UInt值都可以換成VInt型以去掉限制
------------------------------------------------------------------------------
---------
下面是lucene組成結構中的類說明:
org.apache.Lucene.search/ 搜索入口
org.apache.Lucene.index/ 索引入口
org.apache.Lucene.analysis/ 語言分析器
org.apache.Lucene.queryParser/ 查詢分析器
org.apache.Lucene.document/ 存儲結構
org.apache.Lucene.store/? 底層IO/存儲結構
org.apache.Lucene.util/ 一些公用的數據結構
域存儲字段規則
方法 切詞 索引 存儲 用途
Field.Text(String name, String value) 切分詞索引并存儲,比如:標題,內容字段
Field.Text(String name, Reader value)? 切分詞索引不存儲,比如:META信息,
不用于返回顯示,但需要進行檢索內容
Field.Keyword(String name, String value)? 不切分索引并存儲,比如:日期字段
Field.UnIndexed(String name, String value)? 不索引,只存儲,比如:文件路徑
Field.UnStored(String name, String value)? 只全文索引,不存儲
建立索引的例子:
public class IndexFiles {??
//使用方法:: IndexFiles [索引輸出目錄] [索引的文件列表] ...??
public static void main(String[] args) throws Exception {???
String indexPath = args[0];??? IndexWriter writer;???
//用指定的語言分析器構造一個新的寫索引器(第3個參數表示是否為追加索引)???
writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);???
for (int i=1; i<args.length; i++) {?????
System.out.println("Indexing file " + args[i]);?????
InputStream is = new FileInputStream(args[i]);?????
//構造包含2個字段Field的Document對象?????
//一個是路徑path字段,不索引,只存儲?????
//一個是內容body字段,進行全文索引,并存儲?????
Document doc = new Document();?????
doc.add(Field.UnIndexed("path", args[i]));?????
doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));?????
//將文檔寫入索引????
?writer.addDocument(doc);?????
is.close();??? };???
//關閉寫索引器???
writer.close();? }
}
索引過程中可以看到:
語言分析器提供了抽象的接口,因此語言分析(Analyser)是可以定制的,雖然lucene缺省
提供了2個比較通用的分析器SimpleAnalyser和StandardAnalyser,這2個分析器缺省都不
支持中文,所以要加入對中文語言的切分規則,需要修改這2個分析器。
Lucene并沒有規定數據源的格式,而只提供了一個通用的結構(Document對象)來接受索
引的輸入,因此輸入的數據源可以是:數據庫,WORD文檔,PDF文檔,HTML文檔……只要能
夠設計相應的解析轉換器將數據源構造成成Docuement對象即可進行索引。
對于大批量的數據索引,還可以通過調整IndexerWrite的文件合并頻率屬性(mergeFactor
)來提高批量索引的效率。
檢索過程和結果顯示:
搜索結果返回的是Hits對象,可以通過它再訪問Document==>Field中的內容。
假設根據body字段進行全文檢索,可以將查詢結果的path字段和相應查詢的匹配度(score)
打印出來,
public class Search {??
public static void main(String[] args) throws Exception {???
String indexPath = args[0], queryString = args[1];???
//指向索引目錄的搜索器???
Searcher searcher = new IndexSearcher(indexPath);???
//查詢解析器:使用和索引同樣的語言分析器???
Query query = QueryParser.parse(queryString, "body",???????????????????????????
?? new SimpleAnalyzer());???
//搜索結果使用Hits存儲???
Hits hits = searcher.search(query);???
//通過hits可以訪問到相應字段的數據和查詢的匹配度???
for (int i=0; i<hits.length(); i++) {?????
System.out.println(hits.doc(i).get("path") + "; Score: " +?????????????????????
??? hits.score(i));??? };? }
}
添加修改刪除指定記錄(Document)
Lucene提供了索引的擴展機制,因此索引的動態擴展應該是沒有問題的,而指定記錄的修
改也似乎只能通過記錄的刪除,然后重新加入實現。如何刪除指定的記錄呢?刪除的方法
也很簡單,只是需要在索引時根據數據源中的記錄ID專門另建索引,然后利用
IndexReader.delete(Termterm)方法通過這個記錄ID刪除相應的Document。
根據某個字段值的排序功能
根據某個字段值的排序功能
lucene缺省是按照自己的相關度算法(score)進行結果排序的,但能夠根據其他字段進行
結果排序是一個在LUCENE的開發郵件列表中經常提到的問題,很多原先基于數據庫應用都
需要除了基于匹配度(score)以外的排序功能。而從全文檢索的原理我們可以了解到,任
何不基于索引的搜索過程效率都會導致效率非常的低,如果基于其他字段的排序需要在搜
索過程中訪問存儲字段,速度回大大降低,因此非常是不可取的。
但這里也有一個折中的解決方法:在搜索過程中能夠影響排序結果的只有索引中已經存儲
的docID和score這2個參數,所以,基于score以外的排序,其實可以通過將數據源預先排
好序,然后根據docID進行排序來實現。這樣就避免了在LUCENE搜索結果外對結果再次進行
排序和在搜索過程中訪問不在索引中的某個字段值。
這里需要修改的是IndexSearcher中的HitCollector過程:
... 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 (score >= minScore) {????????????? /* 原先:Lucene將
docID和相應的匹配度score例入結果命中列表中:??????? * hq.put(new ScoreDoc
(doc, score));?? // update hit queue?????????????? * 如果用doc 或 1/doc 代替
score,就實現了根據docID順排或逆排?????????????? * 假設數據源索引時已經按照某個
字段排好了序,而結果根據docID排序也就實現了?????????????? * 針對某個字段的排序
,甚至可以實現更復雜的score和docID的擬合。?????????????? */?????????????
hq.put(new ScoreDoc(doc, (float) 1/doc )); ??????
if (hq.size() > nDocs) {??? // if hit queue overfull??
hq.pop();???? // remove lowest in hit queue??
minScore = ((ScoreDoc)hq.top()).score; // reset minScore?????? }???? }?
? }?}????? }, reader.maxDoc());
Lucene面向全文檢索的優化在于首次索引檢索后,并不把所有的記錄(Document)具體內
容讀取出來,而起只將所有結果中匹配度最高的頭100條結果(TopDocs)的ID放到結果集
緩存中并返回,這里可以比較一下數據庫檢索:如果是一個10,000條的數據庫檢索結果集
,數據庫是一定要把所有記錄內容都取得以后再開始返回給應用結果集的。所以即使檢索
匹配總數很多,Lucene的結果集占用的內存空間也不會很多。對于一般的模糊檢索應用是
用不到這么多的結果的,頭100條已經可以滿足90%以上的檢索需求。
如果首批緩存結果數用完后還要讀取更后面的結果時Searcher會再次檢索并生成一個上次
的搜索緩存數大1倍的緩存,并再重新向后抓取。所以如果構造一個Searcher去查1-120條
結果,Searcher其實是進行了2次搜索過程:頭100條取完后,緩存結果用完,Searcher重
新檢索再構造一個200條的結果緩存,依此類推,400條緩存,800條緩存。由于每次
Searcher對象消失后,這些緩存也訪問那不到了,你有可能想將結果記錄緩存下來,緩存
數盡量保證在100以下以充分利用首次的結果緩存,不讓Lucene浪費多次檢索,而且可以分
級進行結果緩存。
Lucene的另外一個特點是在收集結果的過程中將匹配度低的結果自動過濾掉了。這也是和
數據庫應用需要將搜索的結果全部返回不同之處。