1. 程式人生 > >理解和學習Solr的score機制

理解和學習Solr的score機制

在solr的document文件定義中(schema.xml),需要對每個欄位進行定義indexed, stored,其中表示的含義為:
欄位名稱 欄位含義
indexed 如果該欄位是要做查詢的,需要將其設定為indexed,進行索引,以便能夠根據該欄位進行查詢。 但是與具體分詞手段無關,如果涉及到如果分詞,需要使用type屬性
stored 在solr查詢結果中能夠正常返回,如果一個欄位stored=false,則查詢結果不會包括該欄位。
而只有indexed的欄位可以用於查詢,雖然solr中有sort欄位可以進行排序,這一般用於精確匹配的查詢,例如按照分類/品牌進行搜尋得到結果。如果使用者採用關鍵詞的方式進行模糊匹配,此時使用sort根據某個欄位進行排序會顯得不合時宜,無法幫助使用者搜尋到他想要的結果(更多是被sort所控制,這就好比百度的競價排名系統)。 根據查詢打分的相關文件可以看出,Solr中採用了最基本的向量空間模型:

  其中存在的索引檔案.tvx, tvd, tvf儲存了term vector相關資訊,我們學習如何使用term vector來反映相似性程度。在向量空間模型中,夾角越小,說明相似程度越大,可以用cos餘弦函式定理來計算。
 

  相似值計算公式:t=term, d=document, q=query, f=field
  • tf(t in d ) 表示該term 在 這個文件裡出現的頻率(即出現了幾次)。
  •  idf(t) 表示 出現該term的文件個數。
  •  t.getBoost() 查詢語句中每個詞的權重,可以在查詢中設定某個詞更加重要。
  • norm(t,d) 標準化因子d.getBoost() • lengthNorm(f) • f.getBoost() ,它包括三個引數:
    • Document boost:此值越大,說明此文件越重要。
    • Field boost:此域越大,說明此域越重要。
    • lengthNorm(field) = (1.0 / Math.sqrt(numTerms)):一個域中包含的Term總數越多,也即文件越長,此值越小,文件越短,此值越大。
  • coord(q,d):一次搜尋可能包含多個搜尋詞,而一篇文件中也可能包含多個搜尋詞,此項表示,當一篇文件中包含的搜尋詞越多,則此文件則打分越高 ,numTermsInDocumentFromQuery / numTermsInQuery 
  • queryNorm(q):計算每個查詢條目的方差和,此值並不影響排序,而僅僅使得不同的query之間的分數可以比較。
我們當前環境的/select相關配置:
<!-- SearchHandler -->
  <requestHandler name="/select" class="com.zp.solr.handler.component.ZpSearchHandler">
    <lst name="defaults">
      <str name="defType">edismax</str>
      <str name="echoParams">explicit</str>
      <str name="wt">json</str>
      <str name="indent">true</str>
      <str name="df">text</str>
      <str name="bf">
          map(psfixstock,0,0,0,100)
      </str>
    </lst>
    <shardHandlerFactory class="HttpShardHandlerFactory">
      <int name="maxConnectionsPerHost">1000</int>
      <int name="corePoolSize">50</int>
    </shardHandlerFactory>
  </requestHandler>
 
  <!-- A request handler that returns indented JSON by default -->
  <requestHandler name="/query" class="solr.SearchHandler">
     <lst name="defaults">
       <str name="echoParams">explicit</str>
       <str name="wt">json</str>
       <str name="indent">true</str>
       <str name="df">text</str>
     </lst>
  </requestHandler>
  可以看出當前使用了edismax方式,預設查詢欄位df為text,boost function已經被設定,但存在較大問題:搜尋的排序嚴重與貨物的庫存數量相關,這肯定會導致使用者無法查詢出其想要的最精確的結果(即便是無庫存,使用者也可能希望能夠返回正確的商品,而不是有庫存的不相關商品)。Solr查詢中的常用引數列表:
引數 含義
df default fields,預設查詢欄位
wt writer type,指定查詢輸出結構格式,預設xml
defType 設定查詢解析器名稱
bf boost function,可接受多個函式查詢,用空格隔開
qf query fields,指定索引中查詢欄位,如果沒有指定,預設使用df
q 查詢字串,必輸項
q.op 預設查詢連線符,AND OR
sort 排序,sort=<field_name>+<desc|asc>,...
start 分頁定義結果起始記錄數,預設為0
rows 分頁定義結果每頁返回記錄數,預設為10
fq filter query,可充分利用filter query cache,提高檢索效能。在q查詢符合結果中同時是fq查詢符合的
fl field list,指定返回結果欄位,以空格或逗號分隔
timeAllowed 設定查詢超時時間
bq boost query,指定一個單詞或短語提升查詢權重
mm Minimum Should Match,指定查詢中必須匹配的最小規則數,如果沒有在查詢中或在solrconfig.xml檔案中指定mm引數值,q.op引數的有效性將會受到影響。如果q.op是AND,則mm=100%,如果q.op是OR,則mm=1(100%表示全部匹配,1表示只要有一個匹配即可)。如果使用者想修改這些行為,可以在solrconfig.xml檔案中定義mm引數
Solr 支援多種查詢解析,給搜尋引擎開發人員提供靈活的查詢解析。Solr 中主要包含這幾個查詢解析器:標準查詢解析器、DisMax 查詢解析器,擴充套件 DisMax 查詢解析器(eDisMax)。 在solr查詢時,使用debugQuery可以打印出其打分的詳細資訊以便我們能夠正確的分析:
"1046888": "
26.279617 = sum of:
  0.9810601 = sum of:
    0.1401725 = weight(text:女士 in 431) [DefaultSimilarity], result of:
      0.1401725 = score(doc=431,freq=2.0), product of:
        0.3656968 = queryWeight, product of:
          1.4455243 = idf(docFreq=37139, maxDocs=57987)
          0.25298557 = queryNorm
        0.3833025 = fieldWeight in 431, product of:
          1.4142135 = tf(freq=2.0), with freq of:
            2.0 = termFreq=2.0
          1.4455243 = idf(docFreq=37139, maxDocs=57987)
          0.1875 = fieldNorm(doc=431)
    0.8408876 = weight(text:手提包 in 431) [DefaultSimilarity], result of:
      0.8408876 = score(doc=431,freq=2.0), product of:
        0.89569205 = queryWeight, product of:
          3.5404868 = idf(docFreq=4570, maxDocs=57987)
          0.25298557 = queryNorm
        0.9388133 = fieldWeight in 431, product of:
          1.4142135 = tf(freq=2.0), with freq of:
            2.0 = termFreq=2.0
          3.5404868 = idf(docFreq=4570, maxDocs=57987)
          0.1875 = fieldNorm(doc=431)
  25.298557 = FunctionQuery(map(int(psfixstock),0.0,0.0,const(0))), product of:
    100.0 = map(int(psfixstock)=1,min=0.0,max=0.0,target=const(0))
    1.0 = boost
    0.25298557 = queryNorm
",
   女士手提包,搜尋詞拆分成兩個詞元:“女士” “手提包”,q.op預設為OR(通過設定mm的值可以影響該屬性), idf為出現的頻率,單獨搜尋“女士”總條目數37139,單獨搜尋“手提包”總條目數4570,出現頻次越多就越不重要,idf的計算公式:
idf(t) = 1 + log (numDocs / (docFreq +1))
   termFreq=2.0,tf的計算公式,2的1/2次方,得出1.414:
tf(t in d) = numTermOccurrencesInDocument 1/2
  fieldNorm取決於匹配的文件field總數,大概在29個左右(由於查詢中並沒有設定boost),計算公式:
lengthNorm(field) = (1.0 / Math.sqrt(numTerms))
  queryNorm,用來計算每個查詢條目的方差和,使得不同的query之間的分數可以進行比較: 寫道 queryNorm(q) = 1 / (sumOfSquaredWeights )
sumOfSquaredWeights = q.getBoost()2 • ∑ ( idf(t) • t.getBoost() )2   Solr Copy Field對打分的影響 如果使用了solr中的copyfield,會對打分造成什麼影響?copyfield,solr允許將不同的欄位copy到一個欄位中,搜尋只需要搜尋拷貝欄位即可,當然這樣會造成內容中包含非常多的搜尋詞。 根據在StackOverflow上的回答,如果要設定各自欄位的boost,就不能使用統一的copyfield,或者將copyfield進行分組: 當前我們設定的預設df(default field)為<str name="df">text</str>,整個欄位,當前我們可以通過更改qf的方式來做自定義boost,
SearchText, SearchText2^3, SearchText3^10, SearchText4^100
  另一種說法,在solr中支援的多值域(multiValued)其實也就是copyfield,同一個field對應多個value。當一個文件中出現同名的多值域時,倒排索引和項向量都會在邏輯上將這些詞的詞彙單元附加進去。當對多值域進行儲存的時候,它們在文件中的儲存順序是分離的,當在搜尋期間對文件進行檢索時,會發現多個field例項,最後該field的boost如何計算?使用每一個值域的boost相乘。 使用Query Field 我們設定query field,將下面的三個條件作為篩選條件,並設定其boost權重:
      <str name="qf">brand_name^0.9 category_name^0.8 product_name^2.0</str>
  此時我們查詢 “胸飾”,從開啟debugQuery,可以看到其中的詳細評分:
"debug": { "rawquerystring": "胸飾", "querystring": "胸飾", "parsedquery": "(+DisjunctionMaxQuery((brand_name:胸飾^0.9 | category_name:胸飾^0.8 | product_name:胸飾^2.0)) FunctionQuery(map(int(psfixstock),0.0,0.0,const(0),const(100))))/no_coord", "parsedquery_toString": "+(brand_name:胸飾^0.9 | category_name:胸飾^0.8 | product_name:胸飾^2.0) map(int(psfixstock),0.0,0.0,const(0),const(100))",
  我們可以定義多個 requestHandler,用來專門處理多種型別,例如模糊查詢,根據商品id定位商品,後端的內部查詢,將它們分開這樣就可以完成不同的查詢了。