1、開篇語
2、概述
3、淵源
4、初識Solr
5、Solr的安裝
6、Solr分詞順序
7、Solr中文應用的一個執行個體
8、Solr的檢索運算符
[開篇語]按照慣例應該寫一篇技術文章了,這次結合Lucene/Solr來分享一下開發經驗。
Lucene是一個使用Java語言寫的全文檢索開發包(API),利用它可以實作強大的檢索功能,它的詳細介紹大家可以去Google上搜尋一下,本文重點放在Solr相關的讨論上。
[概述]目前國内研究Solr的人不多,而且大多是因為項目開發需要。Solr師承Lucene,為Apache基金會下的一個項目,具體的說它還是Lucene下的一個子項目。Solr出身豪門,而且具有自己的技術特點,填補了以往Lucene僅僅作為開發工具包的遺憾,它是一個完完整整地應用。換句話說,它是一個全文檢索伺服器,它開箱即用,讓我們能立馬體會到Lucene的強大功能,為Lucene産品化走出了一大步。

Solr分詞原理示範界面
[淵源]最初,CNET Networks使用Lucene API來開發了一些應用,并在這個基礎上産生了Solr的雛形,後來Apache Software Foundation在Lucene頂級項目的支援下得到了Solr,這已經是2006年1月份的事了。2006年1月17日,Solr正是加入Apache基金會的孵化項目,在整個項目孵化期間,Solr 穩步地積累各種特性并吸引了一個穩定的user群體、developer群體和Committer群體,并于1年之後的17日正式醞釀成熟,在這之前已經成功釋出了1.1.0版。目前的穩定版本是1.2,Solr在9月份的2007Apache年會上大放異彩,在今年11月底将來到香港參加2007亞洲開源軟體峰會,遺憾的是為什麼不來北京:-(
[初識Solr]Solr伺服器不同于普通的關系型資料庫,不僅僅在于它核心本質的不同(面向結構化和非結構化資料的不同),很大的不同還在于它的體系架構上。Solr伺服器一般情況下需要部署于應用伺服器/Java容器上(如果是本機通信不涉及RPC可以不使用Java容器,如采用嵌入方式使用Solr),無法獨立工作于JVM上。
Solr架構圖
Solr伺服器可以存儲資料并通過索引對其進行快速高效檢索。對外提供HTTP/XML和Json API接口,這使得它能夠在多語言環境下內建,比如針對它的用戶端的開發。Solr目前的用戶端面向的有Java、PHP、Python、C#、Json和Ruby等,遺憾的是沒有面向C/C++(這也是本人目前在研究的),研究音樂搜尋分類的Brian Whitman曾在蘋果平台上使用JNI技術在C代碼中嵌入Solr實作檢索,不過是一個Cocoa工程。有了這些用戶端,使用者能很友善地将Solr內建到具體運用中。目前最完善的當屬Java用戶端Solrj,以及加入到Solr trunk,并将在1.3版本中正式釋出。
如果不研究開發Solr,隻是使用Solr,隻需要關注Solr的以下幾個方面:
1、Solr伺服器的配置在solrconfig.xml中完成,包括對緩存,servlet的個性化配置等等,即系統全局的配置;
2、索引方法、索引域(字段)等等在schema.xml中完成,這個配置是針對Solr執行個體的;
3、索引資料檔案預設放在Solr文檔根目錄下的data/index目錄下,這個路徑可以通過第1點配置,同時可以将這個目錄下的檔案進行複制粘貼,即可完成索引的複用;
4、建立索引的時間相當長,我采用按詞無字典索引方式對2G110萬條中文記錄進行索引,花了将近2個半小時的時間(當然這個時間和很多因素有關,有興趣的話大家可以留言和我讨論),相對而言,在linux下建索引時間要比windows下快很多,可以使用commit操作使新增索引生效,同時注意索引的優化,索引優化也是很費資源和時間的,但是優化索引也是提高檢索速度的重要方法,是以需要好好權衡這一點;
5、安裝完後的Solr目錄下有這麼幾個檔案夾:bin檔案夾裡主要是用于建立鏡像和完成遠端同步的腳本;conf檔案夾下主要是1、2點中提到的配置檔案;admin檔案夾下是的主要是提供web管理界面的檔案;
6、目前Solr1.2不具備安全性設計,沒有使用者組及權限設定,在進行具體應用時需要注意安全,目前最有效的方法是通過應用伺服器上的授權實作。
本文永久連結:http://www.jinsehupan.com/blog/?p=25
[Solr的安裝]Solr發行版中已經有一個使用Jetty為servlet容器的小例子,可以使用這個例子來體驗,那正在在自己想部署的平台和應用伺服器上該怎麼一個步驟呢?
要開始使用 Solr,需安裝以下軟體:
1、Java 1.5 或更高版本;
2、Ant 1.6.x 或更高版本(用于編譯管理Solr工程,個人推薦,當然可以使用eclipse);
3、Web 浏覽器,用來檢視管理頁面(官方建議使用Firefox,但實際沒有發現和IE有什麼差别);
4、servlet 容器,如Tomcat 5.5(不建議使用6版本)。本文以Tomcat 在 8080 端口上運作為例。如果運作的是其他 servlet 容器或在其他的端口上運作,則可能要修改代碼中的URL才能通路示例應用程式和 Solr。
下面開始安裝配置:
1、使用Ant編譯工程或下載下傳示例應用程式,将Solr WAR 檔案複制到 servlet 容器的webapps目錄中;
2、得到Solr檔案夾,以備随後将其複制到目前目錄,可以使用ant build得到,也可以在下載下傳的壓縮包中找到,以它為模闆以備之後的修改;
3、可以通過以下三種方式之一設定 Solr 的主位置:
設定 java 系統屬性 solr.solr.home (沒錯,就是 solr.solr.home,一般在嵌入式內建中用得多);
配置 java:comp/env/solr/home 的一個 JNDI 查找指向 solr 目錄,建立/tomcat55/conf/Catalina/localhost/solr.xml檔案,注意這個xml檔案名将是Solr執行個體名稱,2中的目前目錄被指定為下面中的f:/solrhome,檔案内容如下:
- <context docBase="f:/solr.war" debug="0" crossContext="true" >
- <environment name="solr/home" type="java.lang.String" value="f:/solrhome" override="true" />
- </context>
在包含 solr 目錄的目錄中啟動 servlet 容器(預設的 Solr 主目錄是目前工作目錄下的 solr);
4、最後一點就是如果有CJK(中日韓文字)應用,出現亂碼問題,采用如下方法解決(其實已經不算是solr配置問題,而是應用伺服器配置問題),修改Tomcat的conf/server.xml檔案中對于端口(本文為8080)的連接配接器統一資源編碼為UTF-8,因為Solr1.2核心支援UTF-8編碼:
- <server ...>
- <service ...>
- <connector ... URIEncoding="UTF-8"/>
- ...
- </service>
- </server>
[Solr分詞順序]Solr建立索引和對關鍵詞進行查詢都得對字串進行分詞,在向索引庫中添加全文檢索類型的索引的時候,Solr會首先用空格進行分詞,然後把分詞結果依次使用指定的過濾器進行過濾,最後剩下的結果才會加入到索引庫中以備查詢。分詞的順序如下:
索引
1:空格whitespaceTokenize
2:過濾詞StopFilter
3:拆字WordDelimiterFilter
4:小寫過濾LowerCaseFilter
5:英文相近詞EnglishPorterFilter
6:去除重複詞RemoveDuplicatesTokenFilter
查詢
1:查詢相近詞
2:過濾詞
3:拆字
4:小寫過濾
5:英文相近詞
6:去除重複詞
以上是針對英文,中文的除了空格,其他都類似
[Solr中文應用的一個執行個體]
1、首先配置schema.xml,這個相當于資料表配置檔案,它定義了加入索引的資料的資料類型的。1.2版本的schema.xml主要包括types、fields和其他的一些預設設定。
A、首先需要在types結點内定義一個FieldType子結點,包括name,class,positionIncrementGap等等一些參數,name就是這個FieldType的名稱,class指向org.apache.solr.analysis包裡面對應的class名稱,用來定義這個類型的行為。在FieldType定義的時候最重要的就是定義這個類型的資料在建立索引和進行查詢的時候要使用的分析器analyzer,包括分詞和過濾。在例子中text這個FieldType在定義的時候,在index的analyzer中使用solr.WhitespaceTokenizerFactory這個分詞包,就是空格分詞,然後使用solr.StopFilterFactory,solr.WordDelimiterFilterFactory,solr.LowerCaseFilterFactory,solr.EnglishPorterFilterFactory,solr.RemoveDuplicatesTokenFilterFactory這幾個過濾器。在向索引庫中添加text類型的索引的時候,Solr會首先用空格進行分詞,然後把分詞結果依次使用指定的過濾器進行過濾,最後剩下的結果才會加入到索引庫中以備查詢。Solr的analysis包并沒有帶支援中文的包,在這裡我們采用lucene裡的語言包(在下載下傳後的solr壓縮包内,lib目錄下有一個lucene-analyzers-2.2.0.jar包,裡面含有中文處理的cn和cjk類),有cn和cjk兩個類可以支援中文。我們采用cjk類,并在schema.xml中加入如下配置:
- <fieldtype name="text_cjk" class="solr.TextField">
- <analyzer class="org.apache.lucene.analysis.cjk.CJKAnalyzer"/>
- </fieldtype>
支援類型定義完成了。
B、接下來的工作就是在fields結點内定義具體的字段(類似資料庫中的字段),就是filed,filed定義包括name,type(為之前定義過的各種FieldType),indexed(是否被索引),stored(是否被儲存),multiValued(是否有多個值)等等。例如定義如下:
- <field name="記錄号" type="slong" indexed="true" stored="true" required="true" />
- <field name="檔案名" type="string" indexed="true" stored="true" />
- <field name="日期" type="date" indexed="true" stored="true" />
- <field name="版次" type="string" indexed="true" stored="true" multiValued="true"/>
- <field name="欄目" type="string" indexed="true" stored="true" multiValued="true"/>
- <field name="标題" type="text_cjk" indexed="true" stored="true" multiValued="true"/>
- <field name="作者" type="text_cjk" indexed="true" stored="true" multiValued="true"/>
- <field name="正文" type="text_cjk" indexed="true" stored="true" multiValued="true"/>
- <field name="标記" type="text_cjk" indexed="true" stored="true" multiValued="true"/>
field的定義相當重要,有幾個技巧需注意一下,對可能存在多值得字段盡量設定multiValued屬性為true,避免建索引是抛出錯誤;如果不需要存儲相應字段值,盡量将stored屬性設為false。
C、建議建立了一個拷貝字段,将所有的全文字段複制到一個字段中,以便進行統一的檢索:
- <field name="text_com" type="text_cjk" indexed="true" stored="false" multiValued="true"/>
并在拷貝字段結點處完成拷貝設定:
- <copyfield source="标題" dest="text_com"/>
- <copyfield source="正文" dest="text_com"/>
D、除此之外,還可以定義動态字段,所謂動态字段就是不用指定具體的名稱,隻要定義字段名稱的規則,例如定義一個dynamicField,name為*_i,定義它的type為text,那麼在使用這個字段的時候,任何以_i結尾的字段都被認為是符合這個定義的,例如name_i,gender_i,school_i等。
2、配置solrconfig.xml,用來配置Solr的一些系統屬性,比較重要的一個就是可以通過更改其中的dataDir屬性來指定索引檔案的存放位置,對于有大資料量的情況下還要進行自動commit操作配置,以下設定為當記憶體索引量達到20W條時自動進行往磁盤寫操作,以免堆溢出,這也是解決單個入庫xml檔案最好不要超過30M的有效方法:
- <autocommit>
- <maxdocs>200000</maxdocs>
- </autocommit>
3、配置好這些後,需要重新啟動Solr伺服器使配置生效,然後向其中添加資料。
4、添加資料是通過向伺服器的update Servlet POST xml格式的資料來實作的,xml結構是這樣的add中間有很多個doc,每個doc中有很多個field。添加到索引庫中的每條記錄都必須指定唯一的數字id來唯一辨別這條索引。建立好xml檔案(例如solr.xml)之後,在exampledocs目錄下執行:java -jar post.jar solr.xml來添加索引資料。對于post的jar包,如果重新配置了應用伺服器,如使用了comcat,端口改為8080,執行個體名稱改為solrx了需要重新生成相應的post.jar包進行操作。
另附ronghao實作中文分詞的案例供大家參考:
對全文檢索而言,中文分詞非常的重要,這裡采用了qieqie庖丁分詞(非常不錯:))。內建非常的容易,我下載下傳的是2.0.4-alpha2版本,其中它支援最多切分和按最大切分。建立自己的一個中文TokenizerFactory繼承自solr的BaseTokenizerFactory。
**
* Created by IntelliJ IDEA.
* User: ronghao
* Date: 2007-11-3
* Time: 14:40:59
* 中文切詞 對庖丁切詞的封裝
*/
public class ChineseTokenizerFactory extends BaseTokenizerFactory {
public static final String MOST_WORDS_MODE = “most-words”;
public static final String MAX_WORD_LENGTH_MODE = “max-word-length”;
private String mode = null;
public void setMode(String mode) {
if (mode==null||MOST_WORDS_MODE.equalsIgnoreCase(mode)
|| “default”.equalsIgnoreCase(mode)) {
this.mode=MOST_WORDS_MODE;
} else if (MAX_WORD_LENGTH_MODE.equalsIgnoreCase(mode)) {
this.mode=MAX_WORD_LENGTH_MODE;
}
else {
throw new IllegalArgumentException(”不合法的分析器Mode參數設定:” + mode);
}
}
@Override
public void init(Map args) {
super.init(args);
setMode(args.get(”mode”));
}
public TokenStream create(Reader input) {
return new PaodingTokenizer(input, PaodingMaker.make(),
createTokenCollector());
}
private TokenCollector createTokenCollector() {
if( MOST_WORDS_MODE.equals(mode))
return new MostWordsTokenCollector();
if( MAX_WORD_LENGTH_MODE.equals(mode))
return new MaxWordLengthTokenCollector();
throw new Error(”never happened”);
}
}
在schema.xml的字段text配置裡加入該分詞器。
- <fieldtype name="text" class="solr.TextField" positionIncrementGap="100">
- <analyzer type="index">
- <tokenizer class="com.ronghao.fulltextsearch.analyzer.ChineseTokenizerFactory" mode="most-words"/>
- <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
- <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0"/>
- <filter class="solr.LowerCaseFilterFactory"/>
- <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
- </analyzer>
- <analyzer type="query">
- <tokenizer class="com.ronghao.fulltextsearch.analyzer.ChineseTokenizerFactory" mode="most-words"/>
- <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
- <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt"/>
- <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0"/>
- <filter class="solr.LowerCaseFilterFactory"/>
- <filter class="solr.RemoveDuplicatesTokenFilterFactory"/>
- </analyzer>
- </fieldtype>
完成後重新開機tomcat,即可在http://localhost:8080/solr/admin/analysis.jsp
體驗到庖丁的中文分詞。注意要将paoding-analysis.jar複制到solr的lib下,注意修改jar包裡字典的home。
[Solr的檢索運算符]
“:” 指定字段查指定值,如傳回所有值*:*
“?” 表示單個任意字元的通配
“*” 表示多個任意字元的通配(不能在檢索的項開始使用*或者?符号)
“~” 表示模糊檢索,如檢索拼寫類似于”roam”的項這樣寫:roam~将找到形如foam和roams的單詞;roam~0.8,檢索傳回相似度在0.8以上的記錄。
鄰近檢索,如檢索相隔10個單詞的”apache”和”jakarta”,”jakarta apache”~10
“^” 控制相關度檢索,如檢索jakarta apache,同時希望去讓”jakarta”的相關度更加好,那麼在其後加上”^”符号和增量值,即jakarta^4 apache
布爾操作符AND、||
布爾操作符OR、&&
布爾操作符NOT、!、- (排除操作符不能單獨與項使用構成查詢)
“+” 存在操作符,要求符号”+”後的項必須在文檔相應的域中存在
( ) 用于構成子查詢
[] 包含範圍檢索,如檢索某時間段記錄,包含頭尾,date:[200707 TO 200710]
{} 不包含範圍檢索,如檢索某時間段記錄,不包含頭尾
date:{200707 TO 200710}
/ 轉義操作符,特殊字元包括+ - && || ! ( ) { } [ ] ^ ” ~ * ? : /
[2007.11.8更新,待續]