關於Solr搜索標點與符號的中文分詞你必須知道的(mmseg源碼改造)html
摘要:在中文搜索中的標點、符號每每也是有語義的,好比咱們要搜索「C++」或是「C#」,咱們不但願搜索出來的全是「C」吧?那樣對程序員來講是個噩夢。然而在中文分詞工具mmseg中,它的中文分詞是將標點與符號均去除的,它認爲對於中文來說標點符號無心義,這明顯不能知足咱們的需求。那麼怎樣改造它讓它符合咱們的要求呢?本文就是針對這一問題的詳細解決辦法,咱們改mmseg的源代碼。git
關鍵字:Solr, mmseg, 中文, 分詞, 標點, 符號, 語義程序員
前提:Solr(5.0.0版本),mmseg4j(1.10.0版本)github
做者:王安琪(博客地址:http://www.cnblogs.com/wgp13x/)緩存
0、Solr的mmseg默認中文分詞效果app
作個實驗,入Solr的語句爲:t#\"\&\*CTY C# "#"&*^#とう華뭄내ㅛ #\"\&\*C8:8。3 C# \"#\"&*^#√とう ,使用的是mmseg中的「max-word」型分詞。分詞後會變成什麼樣呢?在對Solr進行簡單的mmseg配置操做後,咱們在Solr的Analysis中對以上語句進行分析,以下圖所示。函數
圖0-1 mmseg默認中文分詞效果工具
從上圖中能夠看出,默認的mmseg「max-word」型分詞將全部的標點、符號都拋棄掉了,餘下的只是中文、數字、英文、韓文、日文等。通過mmseg的其餘類型如:「complex」和「simple」分析操做後,其結果也是把全部的標點、符號均刪除。然而使用Ansj進行中文分詞的話,其默認是不刪除標點符號的。使用IKAanalyzer來進行中文分詞,它也刪除掉全部的標點符號。具體狀況見博客:中文分詞器性能比較 http://www.cnblogs.com/wgp13x/p/3748764.html。性能
mmseg在中文分詞過程當中刪除標點符號,這直接致使搜索不出標點和符號,由於被刪除的將不被創建索引,如:搜索「#」,返回的是全部。爲了解釋這個問題,咱們分析一下Solr建立索引的過程。spa
一、Solr建立索引的過程
在建立索引的過程當中,進入的每一句字符串,均依據fieldType中配置的:tokenizer及如下的filter,從上至下依次處理。正以下圖所示的,當進入的字符串爲 #Yummm :) Drinking a latte at ... 第一步通過StandardTokenizer後,變成了一個個單詞:Yummm | Drinking | a | latte | at | ,能夠看出這一步就已經將標點符號去除掉了,並使用標點符號和空格將句子劃分紅一個個單詞。第二步通過的是StopFilter,它將stop words:a at in 等刪掉,它認爲他們是無語義的詞彙,這要看具體狀況了,這步結束後原文變成了:Yummm | Drinking | latte | 。第三步是通過LowercaseFilter,很明顯從字面上解釋就是把全部的單詞小寫化,最終的結果是:yummm | drinking | latte |。
圖1-1 Solr建立索引的過程
在搜索的過程當中,新入的搜索字符串,也須要經歷這幾個過程,再將經歷這些過程後的單詞以「與」或「或」的關係,進行搜索。這就解釋了,上一個問題,爲何輸入的搜索條件是「#」,返回的是全部,由於條件經歷這些過程後,條件是空,即搜索全部了。
二、Solr的mmseg通過改進後的中文分詞效果
通過咱們的改進,在入Solr的語句爲:!,工;1 - 低 ... 時, 中文分詞效果以下圖所示。
圖2-1 mmseg通過改進後的中文分詞效果
從上圖能夠看到,通過MMST後,全部的單詞都已經大寫小化了,因此能夠去除LowerCaseFilter,對結果不影響,即在配置中將<filter class="solr.LowerCaseFilterFactory"/>去掉。再次分析的效果以下圖所示:
圖2-2 mmseg通過改進後並去除LowerCaseFilter後的中文分詞效果
能夠看出,C++這樣輸入的輸出變成了:c | + | +,這樣的話,當搜索條件爲入C++時,即可以匹配出來了!這正是咱們想要的。最終效果能夠從下圖中看出,在圖2-3中將一串帶有標點符號的字符串添加入Solr的mmseg fild中。在圖2-4中對mmseg fild搜索帶有標點符號的字符串,能夠看到,剛添加的字符串被正確搜索到了!
圖2-3 添加帶有標點符號的Document
圖2-4 搜索條件帶有標點符號的搜索結果
三、Solr的mmseg的中文分詞效果改進辦法
首先,根據mmseg做者chenlb https://github.com/chenlb/mmseg4j-solr 的提示與啓發,能夠在next()函數中進行修改源碼,以達到不去除標點符號的目的。咱們在mmseg源碼中找到MMSeg類中存在next()函數,經過閱讀源碼,咱們知道,這便是對已識別的各類類型的字符進行分門別類地處理,如數字、字母、韓語等。函數內對其餘的字符均視爲無效字符,其中標點與符號便落入了此類別,其對此類別的字符處理辦法是:「不理睬」。下面就是我依照中文字符的處理過程,編寫了標點與符號的處理過程,同時對空格及Tab、\n這些字符采起「不理睬」策略,由於他們真的是無語義的,具體的代碼以下。
public Word next() throws IOException { // 先從緩存中取 Word word = bufWord.poll(); ; if (word == null) { bufSentence.setLength(0); int data = -1; boolean read = true; while (read && (data = readNext()) != -1) { read = false; // 默認一次能夠讀出同一類字符,就能夠分詞內容 int type = Character.getType(data); String wordType = Word.TYPE_WORD; switch (type) { 。。。。。。。。 case Character.SPACE_SEPARATOR: case Character.CONTROL: read = true; break; default: // 其它認爲無效字符 // read = true; bufSentence.appendCodePoint(data); readChars(bufSentence, new ReadCharByType(type)); // bufWord.add(createWord(bufSentence, Word.TYPE_LETTER)); currentSentence = createSentence(bufSentence); bufSentence.setLength(0); }// switch // 中文分詞 if (currentSentence != null) { do { Chunk chunk = seg.seg(currentSentence); for (int i = 0; i < chunk.getCount(); i++) { bufWord.add(chunk.getWords()[i]); } } while (!currentSentence.isFinish()); currentSentence = null; } word = bufWord.poll(); } return word; }
通過編譯後,將MMSeg類相關的class替換到mmseg4j-core-1.10.0.jar目錄下,如圖3-1所示。而後從新部署Solr,一切運行正常!
圖3-1 編譯並替換MMSeg
四、Solr的配置補充
通過剛纔的操做,已經解決了標點與符號刪除的問題。下面講一下autoGeneratePhraseQueries的配置。
圖4-1 mmSeg配置
如上圖的配置所示,autoGeneratePhraseQueries="false",autoGeneratePhraseQueries配置爲false有下面的做用:將搜索關鍵詞分詞後,以或的條件進行搜索,好比入的是 ,搜索關鍵詞是
,關鍵詞通過分詞後有些分詞結果不在Doc範圍內,可是仍舊能夠搜索出來;然而若是autoGeneratePhraseQueries="true" ,則搜索不出來,此時是且的關係。
這簡直是太棒了!