摘引自:http://www.cnblogs.com/forfuture1978/archive/2010/02/08/1666137.html
Lucene的搜索結果默認按相關度排序,這個相關度排序是基於內部的Score和DocID,Score又基於關鍵詞的內部評分和做索引時的 boost。默認Score高的排前面,如果Score一樣,再按索引順序,先索引的排前面。
Sort groupSort = new Sort(new SortField("排序字段name", SortField.Type.long, true));//true為逆向排序
按字段排序:searcher.search(query, sort)
一:索引階段設置Document Boost和Field Boost
//Document Boost和Field Boost默認為1。
Document doc = new Document(); Field f = new Field("contents", "hello world", Field.Store.NO, Field.Index.ANALYZED);
//Field f = new Field("contents", "hello world", Field.Store.NO, Field.Index.ANALYZED_NO_NORMS); f.setBoost(100); doc.add(f); doc.setBoost(100);
1).Document boost:此值越大,說明此文檔越重要。
2).Field boost:此域越大,說明此域越重要。
3).lengthNorm(field) = (1.0 / Math.sqrt(numTerms)):一個域中包含的Term總數越多,也即文檔越長,此值越小,文檔越短,此值越大。
當設置:Field.Index.ANALYZED_NO_NORMS 第三個因素影響無效
當設置:Field.Index.ANALYZED 第三個因素影響生效
norms(標准化因子)意義:
沒有norms意味着索引階段禁用了文檔boost和域的boost及長度標准化。
好處:在於節省內存,不用在搜索階段為索引中的每篇文檔的每個域都占用一 個字節來保存norms信息了。但是對norms信息的禁用是必須全部域都禁用的,一旦有一個域不禁用,則其他禁用的域也會存放默認的norms值。因為為了加快norms的搜索速度,Lucene是根據文檔號乘以每篇文檔的norms信息所占用的大小來計算偏移量的,中間少一篇文檔,偏移量將無法計算,因為大家知道,偏移是相對來說的,一旦它相對的某篇文檔缺失了,那么后面的偏移量也就不復存在了 也即norms信息要么都保存,要么都不保存。
二、在搜索語句中,設置Query Boost.
同域:
title:馬德華^4 title:劉德華
這樣就人為的干預了搜出來的結果,馬德華的結果比較靠前
不同域:
title:馬德華^100 content:馬德華
title中包含馬德華的文檔比content中包含馬德華的文檔獲得更高的分數
三、繼承並實現自己的Similarity
Similariy是計算Lucene打分的最主要的類,實現其中的很多接口可以干預打分的過程
(1) float computeNorm(String field, FieldInvertState state) (2) float lengthNorm(String fieldName, int numTokens) (3) float queryNorm(float sumOfSquaredWeights) (4) float tf(float freq) (5) float idf(int docFreq, int numDocs) (6) float coord(int overlap, int maxOverlap) (7) float scorePayload(int docId, String fieldName, int start, int end, byte [] payload, int offset, int length) 它們分別影響Lucene打分計算的如下部分:
score(q,d) = (6)coord(q,d) · (3)queryNorm(q) · ∑( (4)tf(t in d) · (5)idf(t)2 · t.getBoost() · (1)norm(t,d) )
t in q norm(t,d) =doc.getBoost()· (2)lengthNorm(field)· ∏f.getBoost() field f in d named as t
(1) float computeNorm(String field, FieldInvertState state)
影響標准化因子的計算,如上述,他主要包含了三部分:文檔boost,域boost,以及文檔長度歸一化。此函數一般按照上面norm(t, d)的公式進行計算。
(2) float lengthNorm(String fieldName, int numTokens)
在一篇1000萬個詞的鴻篇巨著中,"lucene"這個詞出現了11次,而在一篇12個詞的短小文檔中,"lucene"這個詞出現了10次,如果不考慮長度在內,當然鴻篇巨著應該分數更高,然而顯然這篇小文檔才是真正關注"lucene"的。因而在此處是要除以文檔的長度,從而減少因文檔長度帶來的打分不公。
但是實際情況是需要靈活多變的,所以文檔長度也需要自己控制下,比如我想做一個經濟學論文的搜索系統,經過一定時間的調研,發現大多數的經濟學論文的長度在8000到10000詞,因而lengthNorm的公式應該 是一個倒拋物線型的,8000到10000詞的論文分數最高,更短或更長的分數都應該偏低,方能夠返回給用戶最好的數據。
(3) float queryNorm(float sumOfSquaredWeights)
這是按照向量空間模型,對query向量的歸一化。此值並不影響排序,而僅僅使得不同的query之間的分數可以比較。
(4) float tf(float freq)
freq是指在一篇文檔中包含的某個詞的數目。tf是根據此數目給出的分數,默認為Math.sqrt(freq)。也即此項並不是隨着包含的數目的增多而線性增加的。
Math.sqrt(freq); freq開平方
(5) float idf(int docFreq, int numDocs)
idf是根據包含某個詞的文檔數以及總文檔數計算出的分數,默認為(Math.log(numDocs/(double)(docFreq+1)) + 1.0)。
由於此項計算涉及到總文檔數和包含此詞的文檔數,因而需要全局的文檔數信息,這給跨索引搜索造成麻煩。
從下面的例子我們可以看出,用MultiSearcher來一起搜索兩個索引和分別用IndexSearcher來搜索兩個索引所得出的分數是有很大差異的。
(6) float coord(int overlap, int maxOverlap)
一次搜索可能包含多個搜索詞,而一篇文檔中也可能包含多個搜索詞,此項表示,當一篇文檔中包含的搜索詞越多,則此文檔則打分越高。
(7) float scorePayload(int docId, String fieldName, int start, int end, byte [] payload, int offset, int length)
由於Lucene引入了payload,因而可以存儲一些自己的信息,用戶可以根據自己存儲的信息,來影響Lucene的打分。
