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
1using System;
2using System.Collections.Generic;
3using Lucene.Net.Analysis;
4using Lucene.Net.Analysis.Standard;
5using Lucene.Net.Documents;
6using Lucene.Net.Index;
7using Lucene.Net.QueryParsers;
8using Lucene.Net.Search;
9using NUnit.Framework;
10
11namespace 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>
4private 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>
4private 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>
4private 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包含相同詞數的文檔估計也不是很多。那這樣區分有什麼意義呢?