編寫Ansj的Solr外掛

rainystars發表於2014-02-25

http://rainyzz.com/2014/02/02/solr-ansj-plugin.html

在一個基於Solr搜尋的專案中之前使用的IKAnalyzer分詞效果不是很好,師兄推薦我用Ansj,但是當時Ansj沒有Solr的外掛,在作者Github專案主頁的issue中作者也說不準備支援Solr,當時就放棄了。前幾天一個同學說Solr有第三方的Solr外掛ansj4solr,下載使用發現該外掛不支援1.1版本以上的Ansj,因為Ansj在1.1的時候修改了分詞的呼叫介面,而且ansj4solr還有一些我使用不到的功能,所以決定自己寫一個最簡單的具備最基本功能的外掛。

實現分詞的Solr外掛主要是實現TokenizerFactory類和Tokenizer類,前者負責接受Solr中schema.xml配置檔案的呼叫,讀取xml檔案中的配置並返回對應的Tokenizer類,後者負責接受Solr傳送過來的資料流,呼叫分詞,產生最後分好詞的Term流。

在Ansj專案中作者提供了Ansj在Lucene下的外掛,這個外掛包含了Analyzer類的實現和Tokenizer類的實現,由於Solr是基於Lucene,Solr中的TokenizerFactory就相當於Lucene中的Analyzer,Tokenizer類是可以共用的,因此我就基於作者主頁中的Lucene4外掛中的Tokenizer類實現對應的TokenizerFactory類。

java
package me.rainystars.ansj.solr.plugin; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.ansj.lucene.util.AnsjTokenizer; import org.ansj.splitWord.analysis.IndexAnalysis; import org.ansj.splitWord.analysis.ToAnalysis; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.util.TokenizerFactory; import org.apache.lucene.util.AttributeSource.AttributeFactory; public class AnsjTokenizerFactory extends TokenizerFactory{ boolean pstemming; boolean isQuery; private String stopwordsDir; public Set<String> filter; public AnsjTokenizerFactory(Map<String, String> args) { super(args); assureMatchVersion(); isQuery = getBoolean(args, "isQuery", true); pstemming = getBoolean(args, "pstemming", false); stopwordsDir = get(args,"words"); addStopwords(stopwordsDir); } //add stopwords list to filter private void addStopwords(String dir) { if (dir == null){ System.out.println("no stopwords dir"); return; } //read stoplist System.out.println("stopwords: " + dir); filter = new HashSet<String>(); File file = new File(dir); InputStreamReader reader; try { reader = new InputStreamReader(new FileInputStream(file),"UTF-8"); BufferedReader br = new BufferedReader(reader); String word = br.readLine(); while (word != null) { filter.add(word); word = br.readLine(); } } catch (FileNotFoundException e) { System.out.println("No stopword file found"); } catch (IOException e) { System.out.println("stopword file io exception"); } } @Override public Tokenizer create(AttributeFactory factory, Reader input) { if(isQuery == true){ //query return new AnsjTokenizer(new ToAnalysis(new BufferedReader(input)), input, filter, pstemming); } else { //index return new AnsjTokenizer(new IndexAnalysis(new BufferedReader(input)), input, filter, pstemming); } } }

編寫TokenizerFactory只需要覆蓋create方法,在該方法內呼叫對應的Tokenizer。其他要做的就是需要在TokenizerFactory的建構函式中讀取schema.xml配置fieldType時提供的引數,將對應的引數傳給Tokenizer或做對應的處理,我這裡因為Tokenizer呼叫的是原作者的部分,所以總共支援三個引數isQuerypstemmingwords

其中的isQuery是用來判斷使用分詞的策略是檢索時需要的比較精確的分詞方式還是建立索引時所需要的比較不精確但是產生詞語較多的分詞方式,根據選擇呼叫不同的分詞器。

其中的pstemming是原作者提供的引數,用來判斷是否需要處理英文名詞的單複數,第三人稱等。

其中的words是停止詞的路徑,在我的使用中Solr伺服器所在的目錄為D:work_solrexample,如果把停止詞放置在D:work_solrexamplesolrcollection1conf資料夾下,就應該新增引數如下:

<tokenizer class="me.rainystars.ansj.solr.plugin.AnsjTokenizerFactory"  isQuery="false" words="solr/collection1/conf/stopwords_ch.txt"/>

然後就可以讀取檔案中的停止詞列表,傳遞給原作者的Tokenizer進行停止詞過濾,停止詞檔案請使用UTF-8格式。

該外掛使用時請將外掛的jar包與Ansj專案中的ansj_seg.jartree_split.jaransj_lucene4_plugin.jar放置在Solr的Web資料夾的lib目錄中,上述檔案下載地址請訪問作者主頁

在schema.xml中對應的配置檔案如下:

<fieldType name="text_ch" class="solr.TextField" positionIncrementGap="100">
    <analyzer type="index">
         <tokenizer class="me.rainystars.ansj.solr.plugin.AnsjTokenizerFactory"  isQuery="false"/>
    </analyzer>
    <analyzer type="query">
        <tokenizer class="me.rainystars.ansj.solr.plugin.AnsjTokenizerFactory"/>
    </analyzer>
</fieldType>

該外掛的更多資訊請見我Github的專案主頁

外掛下載地址:點選我

相關文章