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