六、Solr高亮與Field權重


Solr高亮

原理

做搜索時,高亮是很常見的需求,那么Solr肯定也為高亮提供了支持。先解釋下Solr高亮的原理,在我們設置了需要高亮顯示的Field之后,查詢得到的返回結果會多出來下面的內容:

"highlighting": {

"519": {

"Artist_Name": [

"<em>周傑倫</em>"

]
}
}

 

其實就是多了highlighting的字段,並沒有改變原來返回的字段內容。

Json串是使用 Unique Field :{高亮顯示的內容}的形式。

SolrJ有三種高亮類型:

如果要對某field做高亮顯示,必須對該field設置stored=true

Standard Highlighter,根據查詢的docIdSet,獲取Documents,並獲取當前document的需要高亮的field的value,根據query的term和該field的value做匹配算法
FastVector Highlighter,效率比普通的高亮顯示要高;需要定義termvector(占用空間和IO),包括position和offset,根據query term的termvector到field value中做快速的定位標記,進而實現快速的高亮顯示
Postings Highlighter,由於高亮顯示需要對field設置為store=true,所有對於單節點數據量比較大並且該字段比較大的話,會消耗大量的IO操作,那么可以把該字段存儲在另外的地方,比如Hbase,在外部做高亮顯示的匹配。

其中推薦使用的是Standard Highlighter,下面也是針對Standard Highlighter,

配置

下面介紹兩種配置方式:

  1.SolrJ配置:

 

//設置高亮
songQuery.setHighlight(true); // 開啟高亮組件或用query.setParam("hl", "true");     songQuery.addHighlightField("Song_Name,Song_SingerName");// 高亮字段
songQuery.set("hl.highlightMultiTerm","true");//啟用多字段高亮
songQuery.setHighlightSimplePre("<font style=\"color:#A7D043;font-weight:bold;\">"); //標記,高亮關鍵字前綴
songQuery.setHighlightSimplePost("</font>");//后綴

 

  2.solrConfig.xml配置: 

<requestHandler name="search" class="solr.SearchHandler" default="true">
<!-- default values for query parameters can be specified, these
        will be overridden by parameters in the request
-->
  <lst name="defaults">
    <str name="echoParams">explicit</str>
      <int name="rows">10</int>
      <str name="df">text</str>
   <str name="hl">true</str>
   <str name="hl.fl">content</str>
   <str name="f.name.hl.fragsize">50</str>
   <str name="hl.simple.pre">&lt;font color=&quot;red&gt;</str>
   <str name="hl.simple.post">&lt;/font&gt;</str>
  </lst>
</requestHandler>

 

詳細的RequestHandler配置請參看博客:五、SolrJ、Request Handler

其實這兩種配置並沒有本質上的區別。我個人習慣使用SolrJ配置。

 

解析

獲取highlighting是非常簡單的,一條語句搞定:

 

Map<String,Map<String,List<String>>> tempMap = response.getHighlighting();

 

相信大家也注意到了,雖然接受結果簡單,但是如果想遍歷就比較復雜了,因為接受到的結果是嵌套了很多層的類型Map<String,Map<String,List<String>>>

那么我這邊把我解析的方法分享下:

 

Map<String,Map<String,List<String>>> tempMap = songHighlight.getHighlighting();
         for(Map.Entry<String, Map<String,List<String>>> entry : tempMap.entrySet()) {
            if(Integer.parseInt(entry.getKey()) == song.getSong_SongID()) {
 
               for(Map.Entry<String, List<String>> entryLayer2 : entry.getValue().entrySet()) {
                  if(null != entryLayer2.getKey() && "Song_Name".equals(entryLayer2.getKey())) {
                     //your Operation
                  }
                  if(null != entryLayer2.getKey() && "Song_SingerName".equals(entryLayer2.getKey())) {
                     //your Operation
                  }
               }
            }
         }

 

這個方法比Iterator和foreach效率稍高。

我設置了兩個字段需要高亮,所以在循環中判斷了高亮是屬於哪個字段,之后進行相應的操作。

因為我做的時候,一首歌可能有幾個Song_SingerName,在數據庫中用"/"分隔,所以這種情況更加復雜,我首先是把后綴中的/換成了出現概率很小的@

songQuery.setHighlightSimplePost("<@font>");//后綴

然后再用split("@")分隔出不同的Song_SingerName,但是這樣就會有一個問題,就是我不知道高亮的歌手到底是哪一個歌手,所以這個時候,我還需要從分割后的String[]中提取所有的中文字符,比對后,存入另一個變量,最后再用"/"替換掉"@"。

 

Solr權重

概念

Solr底層依然用的是Lucene的權重算法,也就是通過一個公式計算每個Documents的得分,然后按得分高低排序,公式如下:

簡單解釋下這個公式中包含的一些因子:

Tf:Term frequency,就是條目出現的次數。

Idf: Inverse document frequency,就是用來描述在一個搜索關鍵字中,不同字詞的稀有程度。比如搜索The Cat in the Hat,那么很明顯The和in遠沒有Cat和Hat重要。

Boosting:這個使我們設置權重的重點,比如搜索歌手名,那么在一個document中還有歌手的ID、歌曲的清晰度、歌曲上傳時間,而boosting是不同的Filed有不同權重,之后根據公式計算得分。所以可以看到,我們並不能直接影響solr搜索結果的排序,需要改變權重,進而改變不同Document的得分,從而影響排序

其中還有很多因子和公式的解釋,有興趣的同學可以參考Solr in action這本書,里面有比較詳細的解釋。

因為我們只需簡單的根據某一Filed的權重影響結果的排序,所以我們需要改變Document的Boosting,那么就需要用到Dismax,Dismax是一個查詢解析器(Query parser),查詢解析器的概念就是提供了一系列查詢的參數,一旦我們在查詢url中設置了相應的參數,那么查詢解析器將會解析查詢信息,從而得到搜索結果,其實完全也可以把查詢解析器理解為一個Api,就是提供了相應的方法,我們設置,之后Solr根據我們設置的參數進行查詢,只不過不同的Query Parser提供了不同的參數而已

一共提供了三種Query Parser

Standard:最常用的,並且是默認

Dismax:

Extended Dismax

功能從上至下是逐漸遞增的,在大部分情況下,Standard已經可以完全滿足需求,但是因為要使用權重排序,那么需要用到Dismax,具體提供的參數請查看wiki:

https://cwiki.apache.org/confluence/display/solr/The+Standard+Query+Parser

 

那么首先需要設置Query Parser為Dismax:

 

songQuery.set("defType","dismax");

 

之后設置需要查詢的Field:

 

songQuery.set("qf","Song_Name^2 Song_SingerName^0.2");

 

比如我這里就需要根據用戶輸入的關鍵字查詢歌手名和歌曲名,之后返回這兩個Field命中的結果、多個Query Filed中可以設置不同的權重,比如Song_Name的權重就為2,必須注意,在Solr權重的設置中,所有權重標准為1,意思是當權重設置大於1時,代表這個字段的權重變大,如果權重設置小於1並且大於0的時候,代表這個字段權重變小

之后設置其它Field的權重:

 

songQuery.set("bf", "sum(div(Song_Quality,0.01),if(exists(Song_FileMV),20000,0),recip(ms(NOW,Song_CreateTimeForNew),1,10000,1))");

 

這里面用到了很多Function Query,比如div,代表相除、exists代表如果Song_FileMV如果不為空那么設置它的權重為20000,為空則為0。記住最后要sum起來,因為從上面的公式可以看出來,boosting是一個變量,所以最好要有一個和值。相關的函數請參考wiki:

http://wiki.apache.org/solr/FunctionQuery

 

這樣搜索結果就會按照有MV的優先顯示、最近上傳的優先顯示、清晰度高的優先顯示。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM