Lucene 对文档打分的规则整理记录

摘引自: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的打分。

四、继承并实现自己的collector

上一篇:一个3D视频播放器的演示APK


下一篇:MongoDB day02