11.3.2 Lucene评分算法
那么,Lucene中是如何确定各个Document评分的呢?下面将详细介绍该功能的基本原理。
文档的得分是在用户进行检索时实时计算出来的。如果在建立索引时就已经将每个文档的得分计算好,那么当用户输入任何关键字时,得分最高的文档都会被排在返回结果的最前面,这显然是不合理的。
因此,所有文档的得分应当都与用户输入的关键字有关系,而且是实时运算的结果。其实,所谓得分,可以简单理解成是某个关键字在某文档中出现的频率。
图11-6所示的公式就是Lucene用于计算某个关键字在对应于某文档的得分。
图11-6 Lucene的得分公式
在Lucene得分公式中,已经包含了影响文档评分的各种因素。在表11.1中详细介绍了每一种因素对搜索结果评分的影响作用。
表11-1 Lucene得分公式的解释
因 素 | 在公式中的作用描述 |
tf(t in d) | 词条t在文档d中出现的词频 |
idf( t ) | 词条t在文档中的倒排词频 |
boost(t.field in d) | 在索引过程中设置的字段参数 |
lengthNorm(t.field in d) | 字段的标准化值,表明在字段中存储了多少词条,这个数值是在索引过程中计算出来的,并且也存储在索引中 |
coord(q, d) | 协调因子,它的计算是基于文档d中所包含的所有可供查询的词条数量 |
queryNorm(q) | 在给出每个查询条目的方差和后,计算某查询的标准化值 |
11.3.3 改变文档的得分
除了内置的得分算法外,Lucene还提供了一种方法来改变每个文档的得分。
在代码11.3中,初始化Document后,使用了Document的setBoost方法来改变一下文档的boost因子。这种做法的实际目的是将文档的得分乘以这个因子,以这个新的数作为文档的得分。
代码11.3 使用Boost的例子
public static void buildIndex() throws Exception {
//生成新的Document对象,下同
Document doc1 = new Document();
doc1.add(Field.Text("contents", "word1 word"));
doc1.add(Field.Keyword("path", "path//document1.txt"));
//改变文档的boost因子,下同
doc1.setBoost(1.0f);
Document doc2 = new Document();
doc2.add(Field.Text("contents", "word2 word"));
doc2.add(Field.Keyword("path", "path//document2.txt"));
doc2.setBoost(0.1f);
Document doc3 = new Document();
doc3.add(Field.Text("contents", "word3 word"));
doc3.add(Field.Keyword("path", "path//document3.txt"));
doc3.setBoost(0.5f);
Document doc4 = new Document();
doc4.add(Field.Text("contents", "word4 word"));
doc4.add(Field.Keyword("path", "path//document4.txt"));
doc4.setBoost(0.2f);
Document doc5 = new Document();
doc5.add(Field.Text("contents", "word5 word"));
doc5.add(Field.Keyword("path", "path//document5.txt"));
doc5.setBoost(0.8f);
Document doc6 = new Document();
doc6.add(Field.Text("contents", "word6 word"));
doc6.add(Field.Keyword("path", "path//document6.txt"));
doc6.setBoost(0.1f);
Document doc7 = new Document();
doc7.add(Field.Text("contents", "word7 word"));
doc7.add(Field.Keyword("path", "path//document7.txt"));
doc7.setBoost(0.5f);
Document doc8 = new Document();
doc8.add(Field.Text("contents", "word8 word"));
doc8.add(Field.Keyword("path", "path//document8.txt"));
doc8.setBoost(0.7f);
Document doc9 = new Document();
doc9.add(Field.Text("contents", "word9 word"));
doc9.add(Field.Keyword("path", "path//document9.txt"));
doc9.setBoost(0.2f);
Document doc10 = new Document();
doc10.add(Field.Text("contents", "word10 word"));
doc10.add(Field.Keyword("path", "path//document10.txt"));
doc10.setBoost(0.4f);
Document doc11 = new Document();
doc11.add(Field.Text("contents", "word11 word"));
doc11.add(Field.Keyword("path", "path//document11.txt"));
Document doc12 = new Document();
doc12.add(Field.Text("contents", "word12 word"));
doc12.add(Field.Keyword("path", "path//document12.txt"));
IndexWriter writer = new IndexWriter("c://index", new StandardAnalyzer(), true);
//添加到索引中,下同
writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);
writer.addDocument(doc4);
writer.addDocument(doc5);
writer.addDocument(doc6);
writer.addDocument(doc7);
writer.addDocument(doc8);
writer.addDocument(doc9);
writer.addDocument(doc10);
writer.addDocument(doc11);
writer.addDocument(doc12);
writer.close();
}
代码11.3的运行效果,如图11-7所示。
图11-7 改变Boost后的运行效果
从图11-7可以看出,每个文档的分值已经发生了变化,其中,由于文档1、11、12的boost值和原来一样,因此分值排在最前面,显示的顺序也到了最前面。而其他的文档则已经因为boost值发生了改变,显示的顺序也发生了变化。可以看到,排在最后一个位置的文档是文档6,它的boost值为0.1,所以分值也成了原来的十分之一。
像代码11.3中这样通过Boost值来改变分值的方式相当灵活,可以很有效的达到对文档顺序进行控制的目的。不过,这仍然不是一种理想的方式,因为在建立索引时还需要人为地指定每个文档的boost值。