Solr作為搜索應用服務器,我們在使用過程中,不可避免的要使用中文搜索。以下介紹solr自帶的中文分詞器和第三方分詞器IKAnalyzer。
注:下面操作在Linux下執行,所添加的配置在windon下依然有效。
運行環境
-
- Solr:6.5.1
- 系統 : Linux
以下是設置 solr中文分詞器的方法。
注:開始之前,假定你已經成功登錄solr的界面,並創建了core。
一:使用solr自帶 中文分詞器
1、進入解壓好的solr文件夾根目錄下執行以下命令
1 cp ./contrib/analysis-extras/lucene-libs/lucene-analyzers-smartcn-6.5.1.jar /opt/tomcat-solr/webapps/solr/WEB-INF/lib/
復制lucene-analyzers-smartcn-6.5.1.jar該文件到 Tomcat下的 solr web應用中的lib目錄下,不清楚的可以看 執行命令的地址。復制的文件就是 solr自帶的中文分詞器。
注:如果沒有解壓的目錄,那么在solrhome的目錄下也存在contrib這個文件夾,如果你已經成功登錄過solr界面的話。
2、進入core目錄下的conf文件夾,
打開 managed-schema文件,跳至文件最后,在最后添加新的字段類型如下
1 <!--solr cnAnalyzer-->
2 <fieldType name="solr_cnAnalyzer" class="solr.TextField" positionIncrementGap="100">
3 <analyzer type="index">
4 <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>
5 </analyzer>
6 <analyzer type="query">
7 <tokenizer class="org.apache.lucene.analysis.cn.smart.HMMChineseTokenizerFactory"/>
8 </analyzer>
9 </fieldType>
fieldType: 字段類型屬性
name: 字段類型名稱(可以理解為Java的 數據類型名稱。例如: int、double、String等Java中的數據類型名稱)
class: 數據類型(默認文本數據即可,還有其他的例如:字符串、浮點、整形等)
看下面的配置文件中 其他字段類型,應該很容易理解了:
1 <!-- 字符串 --> 2 <fieldType name="string" class="solr.StrField" sortMissingLast="true" /> 3 <!-- 布爾類型 --> 4 <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/> 5 <!-- 整形 --> 6 <fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0"/> 7 <!-- 浮點 --> 8 <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" positionIncrementGap="0"/>
positionIncrementGap:一個doc中的屬性有多個值時候,設置每個屬性之間的增量值和multiValued屬性配合使用(避免錯誤匹配)。
type: 分詞生效的范圍,兩個參數分別是 index和query,表示 創建索引和搜索時候都生效。不寫默認情況下兩者均生效。
3、添加完畢之后,保存退出並重新啟動 Tomcat服務器,繼續訪問solr。在創建的core中 Analyzer測試中文分詞結果如下。
測試之后,可以看到 短語確實被分割了,但是有些停止詞沒有被去掉(的、是),也沒有去除符號(,),可以在屬性上添加 words添加停詞字典。那么我們下面試試第三方的分詞器.
二:solr 第三方中文分詞器 IKAnalyzer
在使用IKAnalyzer分詞器之前,先說明由於作者在12年之后沒有更新,導致舊版本的分詞器和新版本的solr無法匹配。因此在源碼的基礎上做些改動,以兼容新版的solr。
1、首先修改分詞器:
IK的分詞器 IKTokenizer類實現了抽象類Tokenizer。在IKTokenizer的構造方法中調用了父類Tokenizer的構造方法,代碼如下
1 public IKTokenizer(Reader in, boolean useSmart) { 2 super(in); 3 offsetAtt = addAttribute(OffsetAttribute.class); 4 termAtt = addAttribute(CharTermAttribute.class); 5 typeAtt = addAttribute(TypeAttribute.class); 6 _IKImplement = new IKSegmenter(input, useSmart); 7 }
Tokenizer構造器:
1 protected Tokenizer(AttributeFactory factory) { 2 super(factory); 3 }
可以看到上面的代碼中,構造器調用了父類的構造器,出現不兼容的原因是因為現在的抽象類Tokenizer的構造方法中接受的是 AttributeFactory這個類型,而IKTokenizer傳遞的Reader不匹配。所以在此基礎上做了如下修改
1 //分析器調用
2 public IKTokenizer(Reader in, boolean useSmart) { 3 offsetAtt = addAttribute(OffsetAttribute.class); 4 termAtt = addAttribute(CharTermAttribute.class); 5 typeAtt = addAttribute(TypeAttribute.class); 6 _IKImplement = new IKSegmenter(input, useSmart); 7 } 8
9 //分詞器工廠調用
10 public IKTokenizer(AttributeFactory factory, boolean useSmart) { 11 super(factory); 12 offsetAtt = addAttribute(OffsetAttribute.class); 13 termAtt = addAttribute(CharTermAttribute.class); 14 typeAtt = addAttribute(TypeAttribute.class); 15 _IKImplement = new IKSegmenter(input, useSmart); 16 }
在第一個代碼中刪除了調用父類構造器的過程,主要用於分析器調用,然后第二個是因為 在設置配置文件managed-schema中設置分析器和構造器結合使用的時候需要用到工廠類,因此在此處也創建了一個新的構造方法,接收一個AttributeFactory類型的參數 ,下面會看到他的用處。
2、分析器 IKAnalyzer。
IK分析器中IKAnalyzer重寫父抽象類Analyzer中的createComponents方法,原代碼如下
1 /**
2 * 重載Analyzer接口,構造分詞組件 3 */
4 @Override 5 protected TokenStreamComponents createComponents(String fieldName, final Reader in) { 6 Tokenizer _IKTokenizer = new IKTokenizer(in, this.useSmart()); 7 return new TokenStreamComponents(_IKTokenizer); 8 }
由於現在的Analyzer的抽象方法createComponents,只需要一個 fieldName參數,並不需要Reader,因此直接刪除Reader即可。同時因為分詞器中也不需要Reader對象,在原來的分詞器IKAnalyzer是接收Reader對象后又傳遞給了父類的構造器,但是在新版的solr中不需要了,而且分詞器IKAnalyzer中也沒有使用該對象。
1 @Override 2 protected TokenStreamComponents createComponents(String fieldName) { 3 IKTokenizer it = new IKTokenizer(useSmart); 4 return new Analyzer.TokenStreamComponents(it); 5 }
其實並沒更改什么,去掉了Reader,然后創建分詞器實例返回。如果這時候在managed-schema配置文件中設置分析器已經可以使用了 如下:
1 <fieldType name="IK_cnAnalyzer" class="solr.TextField">
2 <analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
3 </fieldType>
直接指定修改后的IK分詞器給分析器。在單獨給分析器指定分詞器時候,不要在 fieldType(字段類型) 加上positionIncrementGap 參數,否則會報錯:
java.lang.RuntimeException: Can't set positionIncrementGap on custom analyzer class org.wltea.analyzer.lucene.IKAnalyzer
直接使用solr中文分析器時候,同樣無法指定 fieldType 的 屬性positionIncrementGap,那么應該 solr在直接設定 分析器的時候是無法指定該屬性的。
注:analyzer(分析器)上還可以設置 type屬性,告訴solr在什么時候會生效。分別是index和query,在創建索引時候生效,在查詢時候生效。默認不寫同時生效.
class:直接指定的分析器(Analyzer)不能是分詞的工廠類(Factory)或者分詞器(Tokenizer)
上面的修改已經可以使用分析器了,但是如果和分詞器和過濾器配合使用,那么必須創建一個可以生產分詞器的工廠類。該工廠類實org.apache.lucene.analysis.util.TokenizerFactory抽象類.而且必須實現create方法。
同時還要在構造器中調用父接口的構造器,並傳遞一個Map類型的參數。
3、IKTokenizerFactory工廠類
1 package org.wltea.analyzer.lucene; 2
3 import java.util.Map; 4
5 import org.apache.lucene.analysis.Tokenizer; 6 import org.apache.lucene.analysis.util.TokenizerFactory; 7 import org.apache.lucene.util.AttributeFactory; 8
9 /**
10 * IK分詞工廠類。 用於配置文件中 分析器添加分詞器(必須工廠類)。 11 */
12 public final class IKTokenizerFactory extends TokenizerFactory { 13
14 private boolean useSmart; 15
16 // 從頁面傳遞的值中。設置 useSmart 的值
17 public IKTokenizerFactory(Map<String, String> args) { 18 super(args); 19 /*
20 * 判斷Map容器中是否存在useSmart的可以,如果有獲取該key對應的value。 21 * 如果沒有,則設置默認值,也就是第三個參數 false 22 */
23 useSmart = this.getBoolean(args, "useSmart", false); 24 if (!args.isEmpty()) { 25 throw new IllegalArgumentException("Unknown parameters: " + args); 26 } 27 } 28
29 @Override 30 public Tokenizer create(AttributeFactory factory) { 31 return new IKTokenizer(factory, useSmart); 32 } 33 }
可以看到該分詞器實現父類的create方法時候接受了一個AttributeFactory, 是不是很熟悉,在上面修改的IKTokenizer中新增的構造器內接受該類型的參數,並調用父類的構造器,又將參數傳遞給了父類。
因此IKTokenizer中的第二個構造器就是用於該工廠調用並傳遞參數,然后創建實例返回。
至於另外一個問題,構造器必須調用父類的構造器,然后創建Map類型的參數,傳遞給父類構造器,是因為父類TokenizerFactory只有一個帶參數的構造器,沒有默認構造器。子類IKTokenizerFactory在初始化過程中,必須調用父類的構造器。即使傳遞null值給父類。
而Map容器的作用是:在配置文件managed-schema中,設置分詞器的時候,可以傳遞參數。用於設置分詞器中的參數,例如上面的 useSmart,就是用於IK分詞器是否開啟智能分詞的關。
至此修改全部完畢,最后只需要將修改后的編譯文件放入 IK的jar包內即可。注意包路徑為
org.wltea.analyzer.lucene
如果覺得修改麻煩,可以直接下載修改后的文件 下載地址:點我下載 壓縮包內包含了 修改后的文件,和 IK的源碼等。
4、IK在Linux上的設置方式
先將IK的jar文件和配置文件上傳到Linux系統中
復制 IK jar包到 solr/WEB-INF/lib 目錄下
1 cp IKAnalyzer2012FF_u1-6.51.jar /opt/tomcat-solr/webapps/solr/WEB-INF/lib/
復制配置文件到 solr/WEB-INF/classes目錄下
1 cp ext.dic IKAnalyzer.cfg.xml stopword.dic /opt/tomcat-solr/webapps/solr/WEB-INF/classes/
進入solrhome中打開managed-schema文件,添加IKAnalyzer
我的路徑配置路徑:/opt/tomcat-solr/solrhome/home/mycore/conf 請根具個人路徑修改配置文件
5、在上問添加solr中文分詞器后面重現加入以下代碼
1 <!-- IKAnalyzer -->
2 <fieldType name="IK_cnAnalyzer" class="solr.TextField" positionIncrementGap="100">
3 <analyzer type="index">
4 <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false"/>
5 </analyzer>
6 <analyzer type="query">
7 <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false"/>
8 </analyzer>
9 </fieldType>
10
11 <!-- IKAnalyzer Field-->
12 <field name="IK_content" type="IK_cnAnalyzer" indexed="true" stored="true"/>
注:如果在操作之前,通過solr界面的 AddField 方式添加了新的字段,那么 配置文件 中的數據會被solr全部重新更改,字段類型出現在第一行。所有請根據情況修改配置文件
在analyzer(分析器)中設置了 index和query說明創建索引和查詢的時候都使用分詞,因此如果有特殊要求,可以指定索引和查詢時候設置不同的分詞器。
useSmart為工廠對象的構造方法接受的參數,就是上面說到的分詞工廠類中的 Map接受該參數。設置是否使用智能分詞.默認為false,使用細粒度分詞
注:此處的 class只能是工廠對象,並由工廠對象負責創建分詞器實例。工廠對象需要繼承org.apache.lucene.analysis.util.TokenizerFactory這個抽象類,並實現其中的create方法,實現的工廠對象必須用final修飾。
添加分詞結果后的managed-schema文件如下:
6、添加完保存之后,重新訪問 solr,然后在 core的 Analyzer中測試結果如下
因為新增的字段 IK_content,指定的字段類型是IK_cnAnalyzer,該類型使用的是IK中文分詞器,所以使用指定字段或者字段類型,分詞結果是一樣的。
7、設置ik的擴展詞庫,
在/opt/tomcat-solr/webapps/solr/WEB-INF/classes 目錄下的可以設置 ik的擴展字典
現在打開配置文件 IKAnalyzer.cfg.xml,
然后在打開擴展字典 配置文件 ext.dic 添加 全文搜索、服務器。然后重啟 Tomcat。
8、重啟Tomcat服務器之后,繼續分詞查看結果如下
很顯然擴展字典生效了,出現了新增的全文搜索、服務器兩個詞組,因此如有需要可以在擴展字典中添加詞組。至於后面重復出現詞組(分詞后的),是因為IK分詞子默認開啟了細粒度的分詞,如果需要開啟智能分詞可以將配置文件中的 useSmart 設置為true即可。 下面是開啟智能分詞后的結果:
可以根據需求是否開啟智能分詞,至此solr的中文分詞到此結束。
總結:
設置solr的分詞只需要將分詞器的jar包放入solr的依賴文件內,然后在核心core中的managed-schema設置需要的分詞即可。
文章有很多不足的地方,歡迎大家指正!