前面一篇開始學習solr的時候,做了個入門的示例http://blog.csdn.net/zjc/article/details/24414271 。雖然可以檢索出内容,但總和想象的結果有差異——比如,檢索“天龍”兩個字,按正常了解,就應該隻出來《天龍八部》才對,可是竟然也會把《倚天屠龍記》檢出來。後來研究了一下,發現系統是這樣處理的:無論是抽索引時還是分析檢索詞時,都把所有文字按單字拆開。這樣,剛好《倚天屠龍記》裡包含“天”和“龍”。于是對照solr的配置檔案schema.xml做了一些分析和驗證。下面來看一下:
在schema.xml裡,對檢索結果有最直接影響的有這麼幾項:
<solrQueryParserdefaultOperator="OR"/>
這一行在檔案較靠後的位置,但我最先拿出來說。這表示,對拆分後的檢索詞是按照“且”還是“或”的關系標明結果。預設是OR,可以改成AND。象前面例子,如果再有一本《天天向上》,也一樣會被檢索出來。因為包含了“天”字。那就差的更遠了。是以如果采用這個預設的單字拆分法,那最好是用AND,否則結果就太亂了。
好了,假定我們改成了AND,那麼結果是《天天向上》沒有了,但《倚天屠龍記》還在的。因為既有“天”又有“龍”,說明單字拆分法有不足之處。
決定這個單字拆分法的是誰呢?仔細檢查檔案,我們發現這一行:
<field name="name"type=" text_general" indexed="true"stored="true"/>
表示name這個字段是text_general類型的。這就靠譜了,多半text_general類型的處理方式就是單字拆分。
有沒有更好的分詞方法呢,當然有,而且肯定不止一種。我按照網上的例子采用了mmseg4j。使用方法很簡單,把解壓後的幾個jar檔案放到classpath目錄下。然後在schema.xml裡增加下面幾行:
<fieldType name="textComplex"class="solr.TextField" positionIncrementGap="100" >
<analyzer>
<tokenizerclass="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory"mode="complex" dicPath="./dic"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
<fieldTypename="textMaxWord" class="solr.TextField"positionIncrementGap="100" >
<analyzer>
<tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory"mode="max-word" dicPath="./dic"/>
<filterclass="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
<fieldType name="textSimple"class="solr.TextField" positionIncrementGap="100" >
<tokenizerclass="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory"mode="simple" dicPath="./dic"/>
這裡定義了三種fieldType,分别是textComplex、textMaxWord、textSimple。名稱無所謂,重要的是子元素定義了分詞器和過濾器使用的類:com.chenlb.mmseg4j.solr.MMSegTokenizerFactory、solr.LowerCaseFilterFactory。其實三種用的類是相同的,隻是後面有個mode不同。
這裡隻是定義了分詞類型,我們要把這個分詞類型應用到我們的資料裡才行。是以,要把<field name="name" type="textSimple"indexed="true" stored="true"/> ——我這裡改成了textSimple 。在fieldType和fieldname的共同作用下,我們終于可以完成中文習慣上的分詞了。當然要重新運作代碼,重新抽索引才行,而且字段name裡得有東西(參考上一篇的代碼)。
搜尋“name:天龍”,結果為空。這又是怎麼回事呢?難道又錯了?其實如果想看分詞效果,solr的管理端有個分析工具很好用。
進到分析頁面,上面輸入字段名稱name,下面輸入文本,看一下它倒底是怎麼分的
原來textSimple方式把“天龍八部”作為一個整詞了,難怪我們搜“天龍”沒結果,再搜“天龍八部”,有結果了。暈,這也太不符合習慣了。少字沒結果,多字反倒有結果。接着,我們再換一種試試:
<field name="name" type="textMaxWord"indexed="true" stored="true"/> name字段用textMaxWord類型,這表示采用最大化分詞的方式。再來分析一下:
這回差不多了,“天龍八部”被分成“天龍”“八”“部”三個詞。搜尋這三個詞都有結果了,而且是唯一結果。
換個搜尋寫法:
不寫name:天龍,直接寫天龍。暈死,倚天屠龍記又出來了。接着看schema.xml:
<defaultSearchField>text</defaultSearchField>
這表示預設搜尋字段,如果前面什麼都不寫,就到text裡去查找。而text怎麼定義的呢?再找:
<field name="text" type="text_general"indexed="true" stored="false"multiValued="true"/>
果然,又回到text_general來了,還是單字拆分法。
等等,回憶一下,我們并沒有text這個字段啊(參考上一篇代碼),我們隻錄入了三個字段id、name、price,這個text是誰?繼續找,在這裡——
<copyField source="cat"dest="text"/>
<copyField source="name"dest="text"/>
<copyField source="manu"dest="text"/>
<copyField source="features"dest="text"/>
<copyField source="includes"dest="text"/>
這幾行表示,把cat、name、manu、features、includes都作為text看待(一般是為提供通用檢索或簡單檢索功能用的),text是text_general類型的,還是預設的。是以不寫條件時當然又回到單字查找法了。
總結一下:
這5個元素互動作用,最終共同影響着搜尋結果。
fieldType:定義了可選的類型,當然定義了未必就用
field:定義了某個字段具體是什麼fieldType的
copyField:提供了一個同時查找多個字段的簡便方法
defaultSearchField:定義了不寫字段條件時的查找範圍
solrQueryParser defaultOperator:定義了各分詞之間采用什麼邏輯組合