原文:Lucene.Net 2.3.1开发介绍 —— 三、索引(四)
4、索引对搜索排序的影响
搜索的时候,同一个搜索关键字和同一份索引,决定了一个结果,不但决定了结果的集合,也确定了结果的顺序。那个这个结果是怎么得出来的?这个顺序又是怎么排的呢?这两个问题不是本节讨论的重点,但是这两个问题却关系到本节要讨论的,索引对结果的影响问题。在不使用字段排序的情况下,Lucene.Net默认是按文档的得分来排序的,这个公式看着很复杂,感觉像是大学时高数书上的那些个公式,其实说清楚了也简单。
关于文档排序有几个要素:
(1)、查询词在文档中出现的频率,就是一个文档中包含了几个查询词,然后再开个平方,这个很好理解;
(2)、反转文档频率,这个复杂一点。影响它的有两个因素,一个是包含查询词的文档总数,一个是文档的整体数量。比如,现在的索引文件有1000个文档,而现在搜索的关键字“博客园”,能找到499个文档包含它。那么就会拿文档总数除以包含关键字的文档数,这里包含关键字的文档数加了1,是防止一个也没找到,出现除以0的情况。这样,在这个例子中,就会得到结果2。然后再对这个2进行log操作,操作完了再加1。这样就得到值了。可以看出,文档总数越多,包含关键字的文档越少,那么这个值就越大。
(3)、权重,很多书上叫激励因子,我喜欢叫权重。这个就是我们索引的时候能够调整的值。本节也就是对这个分析。
(4)、保有率。这个名字比较难起,我就照着城市汽车保有率起一个吧。意思呢就是,按照分词结果,一个Field如果含有100个词,那么就会把100开平方,得到10,然后再拿1除以这个10,得到0.1。
这里就不详细论述了,下面进入本节的重点。
1、什么是索引的权重?
从上面看出,有4个部分影响了文档的得分,那么,它们是乘积关系,所以权重对文档排序影响很大。什么是权重呢?就是你可以根据文档内容的好坏,设置好的文档权重高,设置差的文档权重低,是一个调节排序的因子,而不用在搜索是按字段排序。
2、怎么调整权重?
在Lucene.Net中怎么调整权重呢?在不修改评分公式的前提下,可以通过设置Boost来控制权重的值。这个过程在索引文档的时候就进行了,一旦文档被写入,这个值就不可更改了,除非删了重新建一个。调整权重也有两种调法,来仔细分析下设置权重如何影响排序的。
2.1 Document的boost
对以前的代码做个修改,变成代码2.1.1。
代码2.1.1
1
using
System;
2
using
System.Collections.Generic;
3
using
Lucene.Net.Analysis;
4
using
Lucene.Net.Analysis.Standard;
5
using
Lucene.Net.Documents;
6
using
Lucene.Net.Index;
7
using
Lucene.Net.QueryParsers;
8
using
Lucene.Net.Search;
9
using
NUnit.Framework;
10
11
namespace
Test
12
{
13
[TestFixture]
14
public
class
StandardAnalyzerCaseTest
15
{
16
/**/
///
<summary>
17
///
执行测试的入口
18
///
</summary>
19
[Test]
20
public
void
SearcherTest()
21
{
22
Index();
23
List
<
string
list
new
List
<
string
()
{
"
测试
"
}
;
24
for
(
int
i
0
; i
<
list.Count; i
++
)
25
{
26
Console.WriteLine(
"
搜索词:
"
+
list[i]);
27
Console.WriteLine(
"
结果:
"
);
28
Searcher(list[i]);
29
Console.WriteLine(
"
"
);
30
}
31
}
32
33
/**/
///
<summary>
34
///
搜索
35
///
</summary>
36
///
<param name="querystring">
搜索输入
</param>
37
private
void
Searcher(
string
querystring)
38
{
39
Analyzer analyzer
new
StandardAnalyzer();
40
IndexSearcher searcher
new
IndexSearcher(
"
IndexDirectory
"
);
41
QueryParser parser
new
QueryParser(
"
content
"
, analyzer);
42
Query query
parser.Parse(querystring);
43
Hits hits
searcher.Search(query);
44
for
(
int
i
0
; i
<
hits.Length(); i
++
)
45
{
46
Document doc
hits.Doc(i);
47
48
Console.WriteLine(doc.Get(
"
content
"
)
+
"
_得分:
"
+
hits.Score(i).ToString(
"
f2
"
));
49
}
50
}
51
52
/**/
///
<summary>
53
///
索引数据
54
///
</summary>
55
private
void
Index()
56
{
57
Analyzer analyzer
new
StandardAnalyzer();
58
IndexWriter writer
new
IndexWriter(
"
IndexDirectory
"
, analyzer,
true
);
59
AddDocument(writer,
"
测试标题一
"
,
"
测试内容一
"
,
1.0f
);
60
AddDocument(writer,
"
测试标题二
"
,
"
测试内容二
"
,
1.0f
);
61
AddDocument(writer,
"
测试标题三
"
,
"
测试内容三
"
,
1.0f
);
62
AddDocument(writer,
"
测试标题四
"
,
"
测试内容四
"
,
1.0f
);
63
writer.Optimize();
64
writer.Close();
65
}
66
/**/
///
<summary>
67
///
为索引准备数据
68
///
</summary>
69
///
<param name="writer">
索引实例
</param>
70
///
<param name="content">
需要索引的数据
</param>
71
void
AddDocument(IndexWriter writer,
string
title,
string
content,
float
boost)
72
{
73
Document document
new
Document();
74
document.Add(
new
Field(
"
title
"
, title, Field.Store.YES, Field.Index.TOKENIZED));
75
document.Add(
new
Field(
"
content
"
, content, Field.Store.YES, Field.Index.TOKENIZED));
76
document.SetBoost(boost);
77
writer.AddDocument(document);
78
}
79
}
80
}
测试,输出:
搜索词:测试
结果:
测试内容一_得分:0.68
测试内容二_得分:0.68
测试内容三_得分:0.68
测试内容四_得分:0.68
这个在预料之中,在得分相同的情况下,是按照加入的顺序排的。现在把索引部分代码换成代码 2.1.2。
代码2.1.2
1
/**/
///
<summary>
2
///
索引数据
3
///
</summary>
4
private
void
Index()
5
{
6
Analyzer analyzer
new
StandardAnalyzer();
7
IndexWriter writer
new
IndexWriter(
"
IndexDirectory
"
, analyzer,
true
);
8
AddDocument(writer,
"
测试标题一
"
,
"
测试内容一111
"
,
1.0f
);
9
AddDocument(writer,
"
测试标题二
"
,
"
测试内容二11
"
,
1.0f
);
10
AddDocument(writer,
"
测试标题三
"
,
"
测试内容三1
"
,
1.0f
);
11
AddDocument(writer,
"
测试标题四
"
,
"
测试内容四
"
,
1.0f
);
12
writer.Optimize();
13
writer.Close();
14
}
测试结果:
搜索词:测试
结果:
测试内容四_得分:0.68
测试内容一111_得分:0.58
测试内容二11_得分:0.58
测试内容三1_得分:0.58
因为数字部分都是同一个分词,所以前三个都一样,这个也好理解,这样就理解了为什么一般文字越多,排得越后了。现在词的数量较少,改变数量对得分影响极大。
现在对boost的值调整一下,变成代码2.1.3。
代码2.1.3
1
/**/
///
<summary>
2
///
索引数据
3
///
</summary>
4
private
void
Index()
5
{
6
Analyzer analyzer
new
StandardAnalyzer();
7
IndexWriter writer
new
IndexWriter(
"
IndexDirectory
"
, analyzer,
true
);
8
AddDocument(writer,
"
测试标题一
"
,
"
测试内容一111
"
,
1.3f
);
9
AddDocument(writer,
"
测试标题二
"
,
"
测试内容二11
"
,
1.2f
);
10
AddDocument(writer,
"
测试标题三
"
,
"
测试内容三1
"
,
1.1f
);
11
AddDocument(writer,
"
测试标题四
"
,
"
测试内容四
"
,
1.0f
);
12
writer.Optimize();
13
writer.Close();
14
}
测试结果:
搜索词:测试
结果:
测试内容一111_得分:0.78
测试内容二11_得分:0.68
测试内容三1_得分:0.68
测试内容四_得分:0.68
似乎,调整得小了点,结果影响并不是很大,只有调成1.3的对结果造成了比较大的影响,是不是调成1.3以上就会对结果产生比较大的影响呢?来试试代码2.1.4。
代码2.1.4
1
/**/
///
<summary>
2
///
索引数据
3
///
</summary>
4
private
void
Index()
5
{
6
Analyzer analyzer
new
StandardAnalyzer();
7
IndexWriter writer
new
IndexWriter(
"
IndexDirectory
"
, analyzer,
true
);
8
AddDocument(writer,
"
测试标题一
"
,
"
测试内容一111
"
,
1.3f
);
9
AddDocument(writer,
"
测试标题二
"
,
"
测试内容二11
"
,
1.4f
);
10
AddDocument(writer,
"
测试标题三
"
,
"
测试内容三1
"
,
1.5f
);
11
AddDocument(writer,
"
测试标题四
"
,
"
测试内容四
"
,
1.6f
);
12
writer.Optimize();
13
writer.Close();
14
}
果然,尤其是文档含有词少的,影响更加明显,评分结果:
搜索词:测试
结果:
测试内容四_得分:0.97
测试内容一111_得分:0.78
测试内容二11_得分:0.78
测试内容三1_得分:0.78
现在先对以上数据分析一下,不难看出,得分越高的,对权重的敏感度越高,而相同的,就会比较迟钝。这样一般达不到我们想要的目地。当然在文档索引过程中出现索引文档Field包含相同词数的文档估计也不是很多。那这样区分有什么意义呢?