lucene是一個高效的,基於Java的全文檢索庫,利用lucene能夠很方便加入到現有應用中來提供全文檢索的功能。好比分佈式搜索引擎Elastic Search底層的搜索就是使用lucene。(全文檢索是從大量的現有的文檔中,搜索出與查詢語句最相關的文檔集的過程)java
爲了更高效的完成全文檢索的過程,lucene使用倒排索引來完成查詢詞到文檔查找。lucene全文檢索的過程主要由建立索引和搜索索引,本文主要介紹文檔相關性排序部分。mysql
lucene中相關概念:sql
note:本文分析的lucene版本爲5.1.0數據庫
倒排索引模型網絡
左邊一列是按照必定順序排列的通過分詞處理後的Term,稱爲字典;每一個詞都指向的包含該詞的文檔組成鏈表稱爲倒排表。分佈式
lucene處理流程搜索引擎
建立索引的過程:spa
搜索索引的過程:3d
空間向量模型code
lucene的評分計算模型是基於VSM,即空間向量模型,但實現上稍有不一樣。
空間向量模型的邏輯就是將每一個文檔看作是由N個Term組成的向量,文檔間相關性就是兩個向量的在N維空間內的夾角大小,因爲兩個向量的之間的夾角越小,相關性就越大,因此就能夠將夾角的餘弦值做爲相關性的得分,夾角越小,餘弦值越大,相關性越大。
VSM(空間向量模型) 計算公式:
好比索引中的文檔
Document Vector = {Weight1, Weight2, ..., Weight N},查詢語句也能夠看作是一個文檔,查詢文檔:Query Vector = {Weight1, Weight2, ..., Weight N},Weight是某個Term的權重,N是全部文檔包括查詢文檔在內的全部Term總數,若是文檔不包含某個Term,則文檔向量中Term的Weight爲0。
Term的權重計算
影響一個Term在文檔中權重大小的因素主要有兩個:
計算公式:
以上是Term weight計算的典型實現,Lucene實現於此稍有不一樣,使用的是tf(t in d ) * idf(t)。
lucene公式推導
咱們首先計算餘弦公式的分子部分,也即兩個向量的點積:
在這裏有三點須要指出:
下面推導查詢語句的長度
由上面的討論,查詢語句中 tf 都爲 1,idf 都忽略查詢語句這篇小文檔,獲得以下公式:
接下來推導文檔的長度
爲何在打分過程當中,須要除以文檔的長度呢? 由於在索引中,不一樣的文檔長度不同,很顯然,對於任意一個 term,在長的文檔中的 tf 要大的多,於是分數也越高,這樣對小的文檔不公平,舉一個極端的例子,在一篇 1000 萬 個詞的鴻篇鉅著中,「lucene」 這個詞出現了 11 次,而在一篇 12 個詞的短小文檔中,「lucene」 這個詞出現了 10 次,若是不考慮長度在內,固然鴻篇鉅著應該分數更高,然而顯然這篇小文檔纔是真正關注「lucene」 的。然而若是按照標準的餘弦計算公式,徹底消除文檔長度的影響,則又對長文檔不公平(畢竟它是包含更多的信息),偏向於首先返回短小的文檔的,這樣在實際應用中使得搜索結果 很難看。
在默認情況下,Lucene 採用 DefaultSimilarity,認爲在計算文檔的向量長度的時候,每一個 Term 的權重就再也不考慮在內了,而是所有爲一。
再加上各類 boost 和 coord,則可得出 Lucene 的計算公式
lucene計算公式
coord(q,d):得分因子,score factor,一個文檔中包含越多的查詢Term詞,則該文檔的得分越高,對應lucene類TFIDFSimilarity.coord(int overlap, int maxOverlap)
;
queryNorm(q):歸一化因子,normalizing factor,使得不一樣查詢間的得分具備可比性,但並不會影響文檔的排序,對應lucene類TFIDFSimilarity.queryNorm(float sumOfSquaredWeights)
;
tf(t in d):Term在文檔d中出現的次數,對應lucene類TFIDFSimilarity.tf(float freq)
;
idf(t):Term的逆文檔頻率,即一個Term在全部文檔中出現的次數越多,重要性越小,對應lucene類TFIDFSimilarity.idf(long docFreq, long numDocs)
;
norm(t,d):封裝了文檔字段field的權重和field內容長度因子,在index時計算,對應lucene類 TFIDFSimilarity.computeNorm(FieldInvertState state)
;
lengthNorm:field內容長度因子,field字段內容長度越短,則值越大,對應lucene類TFIDFSimilarity.lengthNorm(FieldInvertState state);
q.getBoost():查詢的權重(默認值爲1.0)
t.getBoost():子查詢的權重(默認值爲1.0)
f.getBoost():filed字段的權重(認值爲1.0)
設置Query權重
BooleanQuery parentQuery=new BooleanQuery();
parentQuery.setBoost(1.0f);
TermQuery termQuery=new TermQuery(new Term("search"));
termQuery.setBoost(2.0f);
parentQuery.add(termQuery,BooleanClause.Occur.SHOULD);
termQuery=new TermQuery(new Term("掘金"));
termQuery.setBoost(2.0f);
parentQuery.add(termQuery,BooleanClause.Occur.SHOULD);
reader=DirectoryReader.open(index_directory);
IndexSearcher searcher=new IndexSearcher(reader);
TopScoreDocCollector collector=TopScoreDocCollector.create(5);
searcher.search(parentQuery,collector);
ScoreDoc[]hits=collector.topDocs().scoreDocs;複製代碼
設置Field權重
Document document = new Document();
TextField questionField = new TextField(FieldConstants.QUESTION, question, Field.Store.YES);
questionField.setBoost(2.0f);
TextField answerField = new TextField(FieldConstants.ANSWER, question, Field.Store.YES);
answerField.setBoost(2.0f);
document.add(questionField);
document.add(answerField);
documents.add(document);複製代碼
note:從lucene4.0以後就不在支持設置document的boost,應該使用filed