Solr開(kāi)發(fā)文檔Solr 是一種可供企業(yè)使用的、基于 Lucene 的搜索服務(wù)器,它支持層面搜索、命中醒目顯示和多種輸出格式。在這篇文章中,將介紹 Solr 并展示如何輕松地將其表現優(yōu)異的全文本搜索功能加入到 Web 應用程序中。
開(kāi)發(fā)環(huán)境:
System:Windows
WebBrowser:IE6+、Firefox3+
JDK:1.6+
JavaEE Server:tomcat5.0.2.8、tomcat6
IDE:eclipse、MyEclipse 8
開(kāi)發(fā)依賴(lài)庫:
JavaEE 5、solr 3.4
個(gè)人博客:
http://hoojo.cnblogs.comhttp://blog.csdn.net/IBM_hoojoemail:
hoojo_@126.com一、配置和安裝solr
1、 首先去apache官方網(wǎng)站下載solr,下載地址
http://labs.renren.com/apache-mirror//lucene/solr/3.4.0/目前最新的是3.4的版本
2、 下載后解壓目錄如下
client是一個(gè)ruby實(shí)現的示例,這個(gè)我們暫時(shí)不管
contrib有一些功能模塊是需要的jar包
dist是打包發(fā)布好的工程war包
docs是幫助文檔
example是示例,里面有打包部署好的solr工程示例和servlet容器jetty。如果你沒(méi)有tomcat可以直接使用Jetty服務(wù)器部署你的solr示例。
3、 發(fā)布、部署solr示例
A、 利用自帶的Jetty服務(wù)器
首先在dos命令中進(jìn)入到下載好的solr解壓的目錄apache-solr-3.4.0的example目錄
cd E:\JAR\solr\apache-solr-3.4.0\example
然后利用java命令,啟動(dòng)jetty服務(wù)器。Java –jar start.jar
啟動(dòng)Jetty成功后,如果沒(méi)有看到錯誤消息,你可以看到端口信息。
如果你的端口沖突了,你可以到解壓的solr示例包的example/etc的jetty.xml中,修改端口port信息。
<Set name="port">
<SystemProperty name="jetty.port" default="8983"/>
</Set>
B、 利用tomcat發(fā)布solr示例
將下載的solr解壓后,進(jìn)入apache-solr-3.4.0\dist目錄,將里面的solr.war放到D:\tomcat-6.0.28\webapps目錄下,啟動(dòng)tomcat會(huì )自動(dòng)解壓。(當然,你也可以手動(dòng)解壓放到wabapps目錄下)
當然你也可以設置context指向你的solr工程,在D:\tomcat-6.0.28\conf\Catalina\localhost目錄加入solr.xml配置,配置如下:
<Context docBase="D:\solr.war" debug="0" crossContext="true" > <Environment name="solr/home" type="java.lang.String" value="D:\solr" override="true" /></Context>
上面的2步都是一樣的,這樣還沒(méi)有完。啟動(dòng)后你可能會(huì )看到如下錯誤:
我們需要將一些配置和index庫文件也放到解壓好的solr工程下。我們到解壓的apache-solr-3.4.0\example\solr目錄下,將里面的conf和data目錄copy到剛才我們部署的D:\tomcat-6.0.28\webapps\solr工程目錄下?;蚴莄opy到你的solr.xml中的context指定的路徑下工程目錄中。
重啟tomcat就ok了。
4、 這個(gè)時(shí)候你就可以訪(fǎng)問(wèn)
http://localhost:8983/solr/admin/你就可以看到如下界面:
在Query String中輸入solr,點(diǎn)擊Search就可以查詢(xún)到相應的結果,結果以xml形式返回。當然你也可以設置返回數據類(lèi)型為json。
<?xml version="1.0" encoding="UTF-8"><response><lst name="responseHeader"> <int name="status">0</int> <int name="QTime">0</int> <lst name="params"> <str name="indent">on</str> <str name="start">0</str> <str name="q">solr</str> <str name="version">2.2</str> <str name="rows">10</str> </lst></lst><result name="response" numFound="1" start="0"> <doc> <arr name="cat"><str>software</str><str>search</str></arr> <arr name="features"><str>Advanced Full-Text Search Capabilities using Lucene</str><str>Optimized for High Volume Web Traffic</str><str>Standards Based Open Interfaces - XML and HTTP</str> <str>Comprehensive HTML Administration Interfaces</str><str>Scalability - Efficient Replication to other Solr Search Servers</str><str>Flexible and Adaptable with XML configuration and Schema</str><str>Good unicode support: h¨|llo (hello with an accent over the e)</str></arr> <str name="id">SOLR1000</str> <bool name="inStock">true</bool> <date name="incubationdate_dt">2006-01-17T00:00:00Z</date> <str name="manu">Apache Software Foundation</str> <str name="name">Solr, the Enterprise Search Server</str> <int name="popularity">10</int> <float name="price">0.0</float> </doc></result></response>
二、Solr理論
1、 solr基礎
因為 Solr 包裝并擴展了 Lucene,所以它們使用很多相同的術(shù)語(yǔ)。更重要的是,Solr 創(chuàng )建的索引與 Lucene 搜索引擎庫完全兼容。通過(guò)對 Solr 進(jìn)行適當的配置,某些情況下可能需要進(jìn)行編碼,Solr 可以閱讀和使用構建到其他 Lucene 應用程序中的索引。
在 Solr 和 Lucene 中,使用一個(gè)或多個(gè) Document 來(lái)構建索引。Document 包括一個(gè)或多個(gè) Field。Field 包括名稱(chēng)、內容以及告訴 Solr 如何處理內容的元數據。例如,Field 可以包含字符串、數字、布爾值或者日期,也可以包含你想添加的任何類(lèi)型,只需用在solr的配置文件中進(jìn)行相應的配置即可。Field 可以使用大量的選項來(lái)描述,這些選項告訴 Solr 在索引和搜索期間如何處理內容?,F在,查看一下表 1 中列出的重要屬性的子集:
屬性名稱(chēng)
描述
Indexed
Indexed Field 可以進(jìn)行搜索和排序。你還可以在 indexed Field 上運行 Solr 分析過(guò)程,此過(guò)程可修改內容以改進(jìn)或更改結果。
Stored
stored Field 內容保存在索引中。這對于檢索和醒目顯示內容很有用,但對于實(shí)際搜索則不是必需的。例如,很多應用程序存儲指向內容位置的指針而不是存儲實(shí)際的文件內容。
2、 solr索引操作
在 Solr 中,通過(guò)向部署在 servlet 容器中的 Solr Web 應用程序發(fā)送 HTTP 請求來(lái)啟動(dòng)索引和搜索。Solr 接受請求,確定要使用的適當 SolrRequestHandler,然后處理請求。通過(guò) HTTP 以同樣的方式返回響應。默認配置返回 Solr 的標準 XML 響應。你也可以配置 Solr 的備用響應格式,如json、csv格式的文本。
索引就是接受輸入元數據(數據格式在schema.xml中進(jìn)行配置)并將它們傳遞給 Solr,從而在 HTTP Post XML 消息中進(jìn)行索引的過(guò)程。你可以向 Solr 索引 servlet 傳遞四個(gè)不同的索引請求:
add/update 允許您向 Solr 添加文檔或更新文檔。直到提交后才能搜索到這些添加和更新。
commit 告訴 Solr,應該使上次提交以來(lái)所做的所有更改都可以搜索到。
optimize 重構 Lucene 的文件以改進(jìn)搜索性能。索引完成后執行一下優(yōu)化通常比較好。如果更新比較頻繁,則應該在使用率較低的時(shí)候安排優(yōu)化。一個(gè)索引無(wú)需優(yōu)化也可以正常地運行。優(yōu)化是一個(gè)耗時(shí)較多的過(guò)程。
delete 可以通過(guò) id 或查詢(xún)來(lái)指定。按 id 刪除將刪除具有指定 id 的文檔;按查詢(xún)刪除將刪除查詢(xún)返回的所有文檔。
Lucene中操作索引也有這幾個(gè)步驟,但是沒(méi)有更新。Lucene更新是先刪除,然后添加索引。因為更新索引在一定情況下,效率沒(méi)有先刪除后添加的效率好。
3、 搜索
添加文檔后,就可以搜索這些文檔了。Solr 接受 HTTP GET 和 HTTP POST 查詢(xún)消息。收到的查詢(xún)由相應的 SolrRequestHandler 進(jìn)行處理。
solr查詢(xún)參數描述:
參數
描述
示例
q
Solr 中用來(lái)搜索的查詢(xún)。有關(guān)該語(yǔ)法的完整描述,請參閱
參考資料??梢酝ㄟ^(guò)追加一個(gè)分號和已索引且未進(jìn)行斷詞的字段(下面會(huì )進(jìn)行解釋?zhuān)┑拿Q(chēng)來(lái)包含排序信息。默認的排序是 score desc,指按記分降序排序。
q=myField:Java AND otherField:developerWorks; date asc
此查詢(xún)搜索指定的兩個(gè)字段,并根據一個(gè)日期字段對結果進(jìn)行排序。
start
將初始偏移量指定到結果集中??捎糜趯Y果進(jìn)行分頁(yè)。默認值為 0。
start=15
返回從第 15 個(gè)結果開(kāi)始的結果。
rows
返回文檔的最大數目。默認值為 10。
rows=25,返回25個(gè)結果集
fq
提供一個(gè)可選的篩選器查詢(xún)。查詢(xún)結果被限制為僅搜索篩選器查詢(xún)返回的結果。篩選過(guò)的查詢(xún)由 Solr 進(jìn)行緩存。它們對提高復雜查詢(xún)的速度非常有用。
任何可以用 q 參數傳遞的有效查詢(xún),排序信息除外。
hl
當 hl=true 時(shí),在查詢(xún)響應中醒目顯示片段。默認為 false。參看醒目顯示參數(見(jiàn)
參考資料)。
hl=true
fl
作為逗號分隔的列表指定文檔結果中應返回的 Field 集。默認為 “*”,指所有的字段?!皊core” 指還應返回記分。
*,score
sort
排序,對查詢(xún)結果進(jìn)行排序,
參考sort=date asc,price desc
4、 solr模式
上面有提到schema.xml這個(gè)配置,這個(gè)配置可以在你下載solr包的安裝解壓目錄的apache-solr-3.4.0\example\solr\conf中找到,它就是solr模式關(guān)聯(lián)的文件。打開(kāi)這個(gè)配置文件,你會(huì )發(fā)現有詳細的注釋。
模式組織主要分為三個(gè)重要配置
types 部分是一些常見(jiàn)的可重用定義,定義了 Solr(和 Lucene)如何處理 Field。也就是添加到索引中的xml文件屬性中的類(lèi)型,如int、text、date等
fileds是你添加到索引文件中出現的屬性名稱(chēng),而聲明類(lèi)型就需要用到上面的types
其他配置有
uniqueKey 唯一鍵,這里配置的是上面出現的fileds,一般是id、url等不重復的。在更新、刪除的時(shí)候可以用到。
defaultSearchField默認搜索屬性,如q=solr就是默認的搜索那個(gè)字段
solrQueryParser查詢(xún)轉換模式,是并且還是或者(and/or)
schema配置類(lèi)型
<fieldType name="text" class="solr.TextField" positionIncrementGap="100"> <analyzer type="index"> <tokenizer class="solr.WhitespaceTokenizerFactory" /> <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.EnglishPorterFilterFactory" protected="protwords.txt" /> <filter class="solr.RemoveDuplicatesTokenFilterFactory" /> </analyzer> <analyzer type="query"> <tokenizer class="solr.WhitespaceTokenizerFactory" /> <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.EnglishPorterFilterFactory" protected="protwords.txt" /> <filter class="solr.RemoveDuplicatesTokenFilterFactory" /> </analyzer></fieldType>
上面就是一個(gè)type了,然后你在fields配置field的時(shí)候就可以用這個(gè)type。
首先,上面的fieldType的配置中有兩個(gè)analyzer,它是分詞器。主要把我們的數據進(jìn)行分割成一個(gè)個(gè)的詞語(yǔ)。詞干提取、停止詞刪除以及相似的操作都被應用于標記,然后才進(jìn)行索引和搜索,導致使用相同類(lèi)型的標記。
上面的應用程序的 Solr 的fieldType配置按以下步驟進(jìn)行設置:
根據空白進(jìn)行斷詞,然后刪除所有的公共詞(StopFilterFactory)
使用破折號處理特殊的大小寫(xiě)、大小寫(xiě)轉換等等。(WordDelimiterFilterFactory);將所有條目處理為小寫(xiě)(LowerCaseFilterFactory)
使用 Porter Stemming 算法進(jìn)行詞干提?。‥nglishPorterFilterFactory)
刪除所有的副本(RemoveDuplicatesTokenFilterFactory)
Schema屬性、字段
<field name="id" type="string" indexed="true" stored="true" required="true" /><field name="sku" type="text_en_splitting_tight" indexed="true" stored="true" omitNorms="true" /><field name="name" type="text_general" indexed="true" stored="true" /><field name="alphaNameSort" type="alphaOnlySort" indexed="true" stored="false" /><field name="manu" type="text_general" indexed="true" stored="true" omitNorms="true" /><field name="cat" type="string" indexed="true" stored="true" multiValued="true" /><field name="features" type="text_general" indexed="true" stored="true" multiValued="true" /><field name="includes" type="text_general" indexed="true" stored="true" termVectors="true" termPositions="true" termOffsets="true" />
屬性是在添加索引、查詢(xún)的時(shí)候必須的配置,如果你不加這些配置。是無(wú)法完成索引的創(chuàng )建的。
首先id屬性是未經(jīng)分析的字符串類(lèi)型,是可以索引、存儲的,并且是唯一的。
sku是一個(gè)經(jīng)過(guò)分詞器分析出來(lái)的英文切割的類(lèi)型字符,可以索引、存儲、不要存儲規范
multiValued 屬性是一個(gè)特殊的例子,指 Document 可以擁有一個(gè)相同名稱(chēng)添加了多次的 Field。
omitNorms 屬性告訴 Solr(和 Lucene)不要存儲規范。
介紹一下字段聲明下方的 <dynamicField> 聲明。動(dòng)態(tài)字段是一些特殊類(lèi)型的字段,可以在任何時(shí)候將這些字段添加到任何文檔中,由字段聲明定義它們的屬性。動(dòng)態(tài)字段和普通字段之間的關(guān)鍵區別在于前者不需要在 schema.xml 中提前聲明名稱(chēng)。Solr 將名稱(chēng)聲明中的 glob-like 模式應用到所有尚未聲明的引入的字段名稱(chēng),并根據其 <dynamicField> 聲明定義的語(yǔ)義來(lái)處理字段。例如,<dynamicField name="*_i" type="sint" indexed="true" stored="true"/> 指一個(gè) myRating_i 字段被 Solr 處理為 sint,盡管并未將其聲明為字段。這種處理比較方便,例如,當需要用戶(hù)定義待搜索內容的時(shí)候。
5、 索引配置
Solr 性能因素,來(lái)了解與各種更改相關(guān)的性能權衡。
表 1 概括了可控制 Solr 索引處理的各種因素:
因素
描述
useCompoundFile
通過(guò)將很多 Lucene 內部文件整合到單一一個(gè)文件來(lái)減少使用中的文件的數量。這可有助于減少 Solr 使用的文件句柄數目,代價(jià)是降低了性能。除非是應用程序用完了文件句柄,否則 false 的默認值應該就已經(jīng)足夠。
mergeFactor
決定低水平的 Lucene 段被合并的頻率。較小的值(最小為 2)使用的內存較少但導致的索引時(shí)間也更慢。較大的值可使索引時(shí)間變快但會(huì )犧牲較多的內存。
maxBufferedDocs
在合并內存中文檔和創(chuàng )建新段之前,定義所需索引的最小文檔數。段 是用來(lái)存儲索引信息的 Lucene 文件。較大的值可使索引時(shí)間變快但會(huì )犧牲較多的內存。
maxMergeDocs
控制可由 Solr 合并的 Document 的最大數。較小的值 (< 10,000) 最適合于具有大量更新的應用程序。
maxFieldLength
對于給定的 Document,控制可添加到 Field 的最大條目數,進(jìn)而截斷該文檔。如果文檔可能會(huì )很大,就需要增加這個(gè)數值。然而,若將這個(gè)值設置得過(guò)高會(huì )導致內存不足錯誤。
unlockOnStartup
unlockOnStartup 告知 Solr 忽略在多線(xiàn)程環(huán)境中用來(lái)保護索引的鎖定機制。在某些情況下,索引可能會(huì )由于不正確的關(guān)機或其他錯誤而一直處于鎖定,這就妨礙了添加和更新。將其設置為 true 可以禁用啟動(dòng)鎖定,進(jìn)而允許進(jìn)行添加和更新。
6、 查詢(xún)處理配置
<maxBooleanClauses> 標記定義了可組合在一起形成一個(gè)查詢(xún)的子句數量的上限。對于大多數應用程序而言,默認的 1024 就應該已經(jīng)足夠;然而,如果應用程序大量使用了通配符或范圍查詢(xún),增加這個(gè)限值將能避免當值超出時(shí),拋出 TooManyClausesException。
若應用程序預期只會(huì )檢索 Document 上少數幾個(gè) Field,那么可以將 <enableLazyFieldLoading> 屬性設置為 true。懶散加載的一個(gè)常見(jiàn)場(chǎng)景大都發(fā)生在應用程序返回和顯示一系列搜索結果的時(shí)候,用戶(hù)常常會(huì )單擊其中的一個(gè)來(lái)查看存儲在此索引中的原始文檔。初始的顯示常常只需要顯示很短的一段信息。若考慮到檢索大型 Document 的代價(jià),除非必需,否則就應該避免加載整個(gè)文檔。
<query> 部分負責定義與在 Solr 中發(fā)生的事件相關(guān)的幾個(gè)選項。Searcher 的 Java 類(lèi)來(lái)處理 Query 實(shí)例。要改進(jìn)這一設計和顯著(zhù)提高性能,把這些新的 Searcher 聯(lián)機以便為現場(chǎng)用戶(hù)提供查詢(xún)服務(wù)之前,先對它們進(jìn)行 “熱身”。<query> 部分中的 <listener> 選項定義 newSearcher 和 firstSearcher 事件,您可以使用這些事件來(lái)指定實(shí)例化新搜索程序或第一個(gè)搜索程序時(shí)應該執行哪些查詢(xún)。如果應用程序期望請求某些特定的查詢(xún),那么在創(chuàng )建新搜索程序或第一個(gè)搜索程序時(shí)就應該反注釋這些部分并執行適當的查詢(xún)。
solrconfig.xml 文件的剩余部分,除 <admin> 之外,涵蓋了與 緩存、復制 和 擴展或定制 Solr 有關(guān)的項目。admin 部分讓您可以定制管理界面。有關(guān)配置 admin 節的更多信息,請參看solrconfig.xml 文件中的注釋。
7、 監視、記錄和統計數據
用于監視、記錄和統計數據的 Solr 管理選項
菜單名
URL
描述
Statistics
http://localhost:8080/solr/admin/stats.jspStatistics 管理頁(yè)提供了與 Solr 性能相關(guān)的很多有用的統計數據。這些數據包括:
關(guān)于何時(shí)加載索引以及索引中有多少文檔的信息。
關(guān)于用來(lái)服務(wù)查詢(xún)的 SolrRequestHandler 的有用信息。
涵蓋索引過(guò)程的數據,包括添加、刪除、提交等的數量。
緩存實(shí)現和 hit/miss/eviction 信息
Info
http://localhost:8080/solr/admin/registry.jsp有關(guān)正在運行的 Solr 的版本以及在當前實(shí)現中進(jìn)行查詢(xún)、更新和緩存所使用的類(lèi)的詳細信息。此外,還包括文件存于 Solr subversion 存儲庫的何處的信息以及對該文件功能的一個(gè)簡(jiǎn)要描述。
Distribution
http://localhost:8080/solr/admin/distributiondump.jsp顯示與索引發(fā)布和復制有關(guān)的信息。更多信息,請參見(jiàn) “發(fā)布和復制” 一節。
Ping
http://localhost:8080/solr/admin/ping向服務(wù)器發(fā)出 ping 請求,包括在 solrconfig.xml 文件的 admin 部分定義的請求。
Logging
http://localhost:8080/solr/admin/logging.jsp讓您可以動(dòng)態(tài)更改當前應用程序的日志記錄等級。更改日志記錄等級對于調試在執行過(guò)程中可能出現的問(wèn)題非常有用。
properties
http: //localhost:8080/solr/admin/get-properties.jsp
顯示當前系統正在使用的所有 Java 系統屬性。Solr 支持通過(guò)命令行的系統屬性替換。有關(guān)實(shí)現此特性的更多信息,請參見(jiàn) solrconfig.xml 文件。
Thread dump
http://localhost:8080/solr/admin/threaddump.jsp
thread dump 選項顯示了在 JVM 中運行的所有線(xiàn)程的堆棧跟蹤信息。
8、 智能緩存
智能緩存是讓 Solr 得以成為引人矚目的搜索服務(wù)器的一個(gè)關(guān)鍵性能特征。Solr 提供了四種不同的緩存類(lèi)型,所有四種類(lèi)型都可在 solrconfig.xml 的 <query> 部分中配置。solrconfig.xml 文件中所用的標記名列出了這些緩存類(lèi)型:
緩存標記名
描述
能否自熱
filterCache
通過(guò)存儲一個(gè)匹配給定查詢(xún)的文檔 id 的無(wú)序集,過(guò)濾器讓 Solr 能夠有效提高查詢(xún)的性能。緩存這些過(guò)濾器意味著(zhù)對 Solr 的重復調用可以導致結果集的快速查找。更常見(jiàn)的場(chǎng)景是緩存一個(gè)過(guò)濾器,然后再發(fā)起后續的精煉查詢(xún),這種查詢(xún)能使用過(guò)濾器來(lái)限制要搜索的文檔數。
可以
queryResultCache
為查詢(xún)、排序條件和所請求文檔的數量緩存文檔 id 的有序 集合。
可以
documentCache
緩存 Lucene Document,使用內部 Lucene 文檔 id(以便不與 Solr 惟一 id 相混淆)。由于 Lucene 的內部 Document id 可以因索引操作而更改,這種緩存不能自熱。
不可以
Named caches
命名緩存是用戶(hù)定義的緩存,可被 Solr 定制插件 所使用。
可以,
如果實(shí)現了 org.apache.solr.search.CacheRegenerator 的話(huà)。
每個(gè)緩存聲明都接受最多四個(gè)屬性:
class 是緩存實(shí)現的 Java 名。
size 是最大的條目數。
initialSize 是緩存的初始大小。
autoWarmCount 是取自舊緩存以預熱新緩存的條目數。如果條目很多,就意味著(zhù)緩存的 hit 會(huì )更多,只不過(guò)需要花更長(cháng)的預熱時(shí)間。
三、利用SolrJ操作solr API,完成index操作
使用SolrJ操作Solr會(huì )比利用httpClient來(lái)操作Solr要簡(jiǎn)單。SolrJ是封裝了httpClient方法,來(lái)操作solr的API的。SolrJ底層還是通過(guò)使用httpClient中的方法來(lái)完成Solr的操作。
1、 首先,你需要添加如下jar包
其中apache-solr-solrj-3.4.0.jar、slf4j-api-1.6.1.jar可以在下載的apache-solr-3.4.0的壓縮包中的dist中能找到。
2、 其次,建立一個(gè)簡(jiǎn)單的測試類(lèi),完成Server對象的相關(guān)方法的測試工作,代碼如下:
package com.hoo.test; import java.io.IOException;import java.net.MalformedURLException;import java.util.ArrayList;import java.util.Collection;import java.util.List;import org.apache.solr.client.solrj.SolrQuery;import org.apache.solr.client.solrj.SolrServer;import org.apache.solr.client.solrj.SolrServerException;import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;import org.apache.solr.client.solrj.response.QueryResponse;import org.apache.solr.client.solrj.response.UpdateResponse;import org.apache.solr.common.SolrDocumentList;import org.apache.solr.common.SolrInputDocument;import org.apache.solr.common.params.ModifiableSolrParams;import org.apache.solr.common.params.SolrParams;import org.junit.After;import org.junit.Before;import org.junit.Test;import com.hoo.entity.Index; /** * <b>function:</b> Server TestCase * @author hoojo * @createDate 2011-10-19 下午01:49:07 * @file ServerTest.java * @package com.hoo.test * @project SolrExample * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */public class ServerTest { private SolrServer server; private CommonsHttpSolrServer httpServer; private static final String DEFAULT_URL = "http://localhost:8983/solr/"; @Before public void init() { try { server = new CommonsHttpSolrServer(DEFAULT_URL); httpServer = new CommonsHttpSolrServer(DEFAULT_URL); } catch (MalformedURLException e) { e.printStackTrace(); } } @After public void destory() { server = null; httpServer = null; System.runFinalization(); System.gc(); } public final void fail(Object o) { System.out.println(o); } /** * <b>function:</b> 測試是否創(chuàng )建server對象成功 * @author hoojo * @createDate 2011-10-21 上午09:48:18 */ @Test public void server() { fail(server); fail(httpServer); } /** * <b>function:</b> 根據query參數查詢(xún)索引 * @author hoojo * @createDate 2011-10-21 上午10:06:39 * @param query */ public void query(String query) { SolrParams params = new SolrQuery(query); try { QueryResponse response = server.query(params); SolrDocumentList list = response.getResults(); for (int i = 0; i < list.size(); i++) { fail(list.get(i)); } } catch (SolrServerException e) { e.printStackTrace(); } }}
測試運行server case方法,如果成功創(chuàng )建對象,那你就成功的鏈接到。
注意:在運行本方法之前,請啟動(dòng)你的solr官方自動(dòng)的項目。
http://localhost:8983/solr/保證能夠成功訪(fǎng)問(wèn)這個(gè)工程。因為接下來(lái)的所有工作都是圍繞這個(gè)solr工程完成的。如果你現在還不知道,怎么部署、發(fā)布官方solr工程,請參考前面的具體章節。
3、 Server的有關(guān)配置選項參數,server是CommonsHttpSolrServer的實(shí)例
server.setSoTimeout(1000); // socket read timeout server.setConnectionTimeout(100); server.setDefaultMaxConnectionsPerHost(100); server.setMaxTotalConnections(100); server.setFollowRedirects(false); // defaults to false // allowCompression defaults to false. // Server side must support gzip or deflate for this to have any effect. server.setAllowCompression(true); server.setMaxRetries(1); // defaults to 0. > 1 not recommended. //sorlr J 目前使用二進(jìn)制的格式作為默認的格式。對于solr1.2的用戶(hù)通過(guò)顯示的設置才能使用XML格式。server.setParser(new XMLResponseParser()); //二進(jìn)制流輸出格式//server.setRequestWriter(new BinaryRequestWriter());
4、 利用SolrJ完成Index Document的添加操作
/** * <b>function:</b> 添加doc文檔 * @author hoojo * @createDate 2011-10-21 上午09:49:10 */@Testpublic void addDoc() { //創(chuàng )建doc文檔 SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", 1); doc.addField("name", "Solr Input Document"); doc.addField("manu", "this is SolrInputDocument content"); try { //添加一個(gè)doc文檔 UpdateResponse response = server.add(doc); fail(server.commit());//commit后才保存到索引庫 fail(response); fail("query time:" + response.getQTime()); fail("Elapsed Time:" + response.getElapsedTime()); fail("status:" + response.getStatus()); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } query("name:solr");}
在apache-solr-3.4.0\example\solr\conf目錄下的schema.xml中可以找到有關(guān)于field屬性的配置,schema.xml中的field就和上面Document文檔中的field(id、name、manu)對應。如果出現ERROR:unknown field 'xxxx'就表示你設置的這個(gè)field在schema.xml中不存在。如果一定要使用這個(gè)field,請你在schema.xml中進(jìn)行filed元素的配置。具體請參考前面的章節。
注意:在schema.xml中配置了uniqueKey為id,就表示id是唯一的。如果在添加Document的時(shí)候,id重復添加。那么后面添加的相同id的doc會(huì )覆蓋前面的doc,類(lèi)似于update更新操作,而不會(huì )出現重復的數據。
5、 利用SolrJ添加多個(gè)Document,即添加文檔集合
/** * <b>function:</b> 添加docs文檔集合 * @author hoojo * @createDate 2011-10-21 上午09:55:01 */@Testpublic void addDocs() { Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>(); SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", 2); doc.addField("name", "Solr Input Documents 1"); doc.addField("manu", "this is SolrInputDocuments 1 content"); docs.add(doc); doc = new SolrInputDocument(); doc.addField("id", 3); doc.addField("name", "Solr Input Documents 2"); doc.addField("manu", "this is SolrInputDocuments 3 content"); docs.add(doc); try { //add docs UpdateResponse response = server.add(docs); //commit后才保存到索引庫 fail(server.commit()); fail(response); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } query("solr");}
就是添加一個(gè)List集合
6、 添加JavaEntity Bean,這個(gè)需要先創(chuàng )建一個(gè)JavaBean,然后來(lái)完成添加操作;
JavaBean:Index的代碼
package com.hoo.entity; import org.apache.solr.client.solrj.beans.Field; /** * <b>function:</b> JavaEntity Bean;Index需要添加相關(guān)的Annotation注解,便于告訴solr哪些屬性參與到index中 * @author hoojo * @createDate 2011-10-19 下午05:33:27 * @file Index.java * @package com.hoo.entity * @project SolrExample * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */public class Index { //@Field setter方法上添加Annotation也是可以的 private String id; @Field private String name; @Field private String manu; @Field private String[] cat; @Field private String[] features; @Field private float price; @Field private int popularity; @Field private boolean inStock; public String getId() { return id; } @Field public void setId(String id) { this.id = id; } //getter、setter方法 public String toString() { return this.id + "#" + this.name + "#" + this.manu + "#" + this.cat; }}
注意上面的屬性是和在apache-solr-3.4.0\example\solr\conf目錄下的schema.xml中可以找到有關(guān)于field屬性的配置對應的。如果你Index JavaBean中出現的屬性在schema.xml的field配置無(wú)法找到,那么出出現unknown filed錯誤。
添加Bean完成doc添加操作
/** * <b>function:</b> 添加JavaEntity Bean * @author hoojo * @createDate 2011-10-21 上午09:55:37 */@Testpublic void addBean() { //Index需要添加相關(guān)的Annotation注解,便于告訴solr哪些屬性參與到index中 Index index = new Index(); index.setId("4"); index.setName("add bean index"); index.setManu("index bean manu"); index.setCat(new String[] { "a1", "b2" }); try { //添加Index Bean到索引庫 UpdateResponse response = server.addBean(index); fail(server.commit());//commit后才保存到索引庫 fail(response); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } queryAll();}
7、 添加Bean集合
/** * <b>function:</b> 添加Entity Bean集合到索引庫 * @author hoojo * @createDate 2011-10-21 上午10:00:55 */@Testpublic void addBeans() { Index index = new Index(); index.setId("6"); index.setName("add beans index 1"); index.setManu("index beans manu 1"); index.setCat(new String[] { "a", "b" }); List<Index> indexs = new ArrayList<Index>(); indexs.add(index); index = new Index(); index.setId("5"); index.setName("add beans index 2"); index.setManu("index beans manu 2"); index.setCat(new String[] { "aaa", "bbbb" }); indexs.add(index); try { //添加索引庫 UpdateResponse response = server.addBeans(indexs); fail(server.commit());//commit后才保存到索引庫 fail(response); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } queryAll();}
8、 刪除索引Document
/** * <b>function:</b> 刪除索引操作 * @author hoojo * @createDate 2011-10-21 上午10:04:28 */@Testpublic void remove() { try { //刪除id為1的索引 server.deleteById("1"); server.commit(); query("id:1"); //根據id集合,刪除多個(gè)索引 List<String> ids = new ArrayList<String>(); ids.add("2"); ids.add("3"); server.deleteById(ids); server.commit(true, true); query("id:3 id:2"); //刪除查詢(xún)到的索引信息 server.deleteByQuery("id:4 id:6"); server.commit(true, true); queryAll(); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}
9、 查詢(xún)索引
/** * <b>function:</b> 查詢(xún)所有索引信息 * @author hoojo * @createDate 2011-10-21 上午10:05:38 */@Testpublic void queryAll() { ModifiableSolrParams params = new ModifiableSolrParams(); // 查詢(xún)關(guān)鍵詞,*:*代表所有屬性、所有值,即所有index params.set("q", "*:*"); // 分頁(yè),start=0就是從0開(kāi)始,,rows=5當前返回5條記錄,第二頁(yè)就是變化start這個(gè)值為5就可以了。 params.set("start", 0); params.set("rows", Integer.MAX_VALUE); // 排序,,如果按照id 排序,,那么將score desc 改成 id desc(or asc) params.set("sort", "score desc"); // 返回信息 * 為全部 這里是全部加上score,如果不加下面就不能使用score params.set("fl", "*,score"); try { QueryResponse response = server.query(params); SolrDocumentList list = response.getResults(); for (int i = 0; i < list.size(); i++) { fail(list.get(i)); } } catch (SolrServerException e) { e.printStackTrace(); }}
10、 其他和Server有關(guān)方法
/** * <b>function:</b> 其他server相關(guān)方法測試 * @author hoojo * @createDate 2011-10-21 上午10:02:03 */@Testpublic void otherMethod() { fail(server.getBinder()); try { fail(server.optimize());//合并索引文件,可以?xún)?yōu)化索引、提供性能,但需要一定的時(shí)間 fail(server.ping());//ping服務(wù)器是否連接成功 Index index = new Index(); index.setId("299"); index.setName("add bean index199"); index.setManu("index bean manu199"); index.setCat(new String[] { "a199", "b199" }); UpdateResponse response = server.addBean(index); fail("response: " + response); queryAll(); //回滾掉之前的操作,rollback addBean operation fail("rollback: " + server.rollback()); //提交操作,提交后無(wú)法回滾之前操作;發(fā)現addBean沒(méi)有成功添加索引 fail("commit: " + server.commit()); queryAll(); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }}
11、 文檔查詢(xún)
/** * <b>function:</b> query 基本用法測試 * @author hoojo * @createDate 2011-10-20 下午04:44:28 */@Testpublic void queryCase() { //AND 并且 SolrQuery params = new SolrQuery("name:apple AND manu:inc"); //OR 或者 params.setQuery("name:apple OR manu:apache"); //空格 等同于 OR params.setQuery("name:server manu:dell"); //params.setQuery("name:solr - manu:inc"); //params.setQuery("name:server + manu:dell"); //查詢(xún)name包含solr apple params.setQuery("name:solr,apple"); //manu不包含inc params.setQuery("name:solr,apple NOT manu:inc"); //50 <= price <= 200 params.setQuery("price:[50 TO 200]"); params.setQuery("popularity:[5 TO 6]"); //params.setQuery("price:[50 TO 200] - popularity:[5 TO 6]"); //params.setQuery("price:[50 TO 200] + popularity:[5 TO 6]"); //50 <= price <= 200 AND 5 <= popularity <= 6 params.setQuery("price:[50 TO 200] AND popularity:[5 TO 6]"); params.setQuery("price:[50 TO 200] OR popularity:[5 TO 6]"); //過(guò)濾器查詢(xún),可以提高性能 filter 類(lèi)似多個(gè)條件組合,如and //params.addFilterQuery("id:VA902B"); //params.addFilterQuery("price:[50 TO 200]"); //params.addFilterQuery("popularity:[* TO 5]"); //params.addFilterQuery("weight:*"); //0 < popularity < 6 沒(méi)有等于 //params.addFilterQuery("popularity:{0 TO 6}"); //排序 params.addSortField("id", ORDER.asc); //分頁(yè):start開(kāi)始頁(yè),rows每頁(yè)顯示記錄條數 //params.add("start", "0"); //params.add("rows", "200"); //params.setStart(0); //params.setRows(200); //設置高亮 params.setHighlight(true); // 開(kāi)啟高亮組件 params.addHighlightField("name");// 高亮字段 params.setHighlightSimplePre("<font color='red'>");//標記,高亮關(guān)鍵字前綴 params.setHighlightSimplePost("</font>");//后綴 params.setHighlightSnippets(1);//結果分片數,默認為1 params.setHighlightFragsize(1000);//每個(gè)分片的最大長(cháng)度,默認為100 //分片信息 params.setFacet(true) .setFacetMinCount(1) .setFacetLimit(5)//段 .addFacetField("name")//分片字段 .addFacetField("inStock"); //params.setQueryType(""); try { QueryResponse response = server.query(params); /*List<Index> indexs = response.getBeans(Index.class); for (int i = 0; i < indexs.size(); i++) { fail(indexs.get(i)); }*/ //輸出查詢(xún)結果集 SolrDocumentList list = response.getResults(); fail("query result nums: " + list.getNumFound()); for (int i = 0; i < list.size(); i++) { fail(list.get(i)); } //輸出分片信息 List<FacetField> facets = response.getFacetFields(); for (FacetField facet : facets) { fail(facet); List<Count> facetCounts = facet.getValues(); for (FacetField.Count count : facetCounts) { System.out.println(count.getName() + ": " + count.getCount()); } } } catch (SolrServerException e) { e.printStackTrace(); } }
12、 分片查詢(xún)、統計
/** * <b>function:</b> 分片查詢(xún), 可以統計關(guān)鍵字及出現的次數、或是做自動(dòng)補全提示 * @author hoojo * @createDate 2011-10-20 下午04:54:25 */@Testpublic void facetQueryCase() { SolrQuery params = new SolrQuery("*:*"); //排序 params.addSortField("id", ORDER.asc); params.setStart(0); params.setRows(200); //Facet為solr中的層次分類(lèi)查詢(xún) //分片信息 params.setFacet(true) .setQuery("*:*") .setFacetMinCount(1) .setFacetLimit(5)//段 //.setFacetPrefix("electronics", "cat") .setFacetPrefix("cor")//查詢(xún)manu、name中關(guān)鍵字前綴是cor的 .addFacetField("manu") .addFacetField("name");//分片字段 try { QueryResponse response = server.query(params); //輸出查詢(xún)結果集 SolrDocumentList list = response.getResults(); fail("Query result nums: " + list.getNumFound()); for (int i = 0; i < list.size(); i++) { fail(list.get(i)); } fail("All facet filed result: "); //輸出分片信息 List<FacetField> facets = response.getFacetFields(); for (FacetField facet : facets) { fail(facet); List<Count> facetCounts = facet.getValues(); for (FacetField.Count count : facetCounts) { //關(guān)鍵字 - 出現次數 fail(count.getName() + ": " + count.getCount()); } } fail("Search facet [name] filed result: "); //輸出分片信息 FacetField facetField = response.getFacetField("name"); List<Count> facetFields = facetField.getValues(); for (Count count : facetFields) { //關(guān)鍵字 - 出現次數 fail(count.getName() + ": " + count.getCount()); } } catch (SolrServerException e) { e.printStackTrace(); } }
分片查詢(xún)在某些統計關(guān)鍵字的時(shí)候還是很有用的,可以統計關(guān)鍵字出現的次數,可以通過(guò)統計的關(guān)鍵字來(lái)搜索相關(guān)文檔的信息。
四、Document文檔和JavaBean相互轉換
這里轉換的Bean是一個(gè)簡(jiǎn)單的User對象
package com.hoo.entity; import java.io.Serializable;import org.apache.solr.client.solrj.beans.Field; /** * <b>function:</b> User Entity Bean;所有被添加Annotation @Field 注解的屬性將參與index操作 * @author hoojo * @createDate 2011-10-19 下午04:16:00 * @file User.java * @package com.hoo.entity * @project SolrExample * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */public class User implements Serializable { /** * @author Hoojo */ private static final long serialVersionUID = 8606788203814942679L; //@Field private int id; @Field private String name; @Field private int age; /** * 可以給某個(gè)屬性重命名,likes就是solr index的屬性;在solrIndex中將顯示like為likes */ @Field("likes") private String[] like; @Field private String address; @Field private String sex; @Field private String remark; public int getId() { return id; } //setter 方法上面也可以 @Field public void setId(int id) { this.id = id; } public String getName() { return name; } //getter、setter @Override public String toString() { return this.id + "#" + this.name + "#" + this.age + "#" + this.like + "#" + this.address + "#" + this.sex + "#" + this.remark; }}
測試類(lèi)代碼如下
package com.hoo.test; import org.apache.solr.client.solrj.beans.DocumentObjectBinder;import org.apache.solr.common.SolrDocument;import org.apache.solr.common.SolrDocumentList;import org.apache.solr.common.SolrInputDocument;import org.apache.solr.common.SolrInputField;import org.junit.Test;import com.hoo.entity.User; /** * <b>function:</b>SolrInputDocument implements Map, Iterable * @author hoojo * @createDate 2011-10-19 下午03:54:54 * @file SolrInputDocumentTest.java * @package com.hoo.test * @project SolrExample * @blog http://blog.csdn.net/IBM_hoojo * @email hoojo_@126.com * @version 1.0 */public class SolrInputDocumentTest { public final void fail(Object o) { System.out.println(o); } /** * <b>function:</b> 創(chuàng )建SolrInputDocument * @author hoojo * @createDate 2011-10-21 下午03:38:20 */ @Test public void createDoc() { SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", System.currentTimeMillis()); doc.addField("name", "SolrInputDocument"); doc.addField("age", 22, 2.0f); doc.addField("like", new String[] { "music", "book", "sport" }); doc.put("address", new SolrInputField("guangzhou")); doc.setField("sex", "man"); doc.setField("remark", "china people", 2.0f); fail(doc); } /** * <b>function:</b> 利用DocumentObjectBinder對象將SolrInputDocument 和 User對象相互轉換 * @author hoojo * @createDate 2011-10-21 下午03:38:40 */ @Test public void docAndBean4Binder() { SolrDocument doc = new SolrDocument(); doc.addField("id", 456); doc.addField("name", "SolrInputDocument"); doc.addField("likes", new String[] { "music", "book", "sport" }); doc.put("address", "guangzhou"); doc.setField("sex", "man"); doc.setField("remark", "china people"); DocumentObjectBinder binder = new DocumentObjectBinder(); User user = new User(); user.setId(222); user.setName("JavaBean"); user.setLike(new String[] { "music", "book", "sport" }); user.setAddress("guangdong"); fail(doc); // User ->> SolrInputDocument fail(binder.toSolrInputDocument(user)); // SolrDocument ->> User fail(binder.getBean(User.class, doc)); SolrDocumentList list = new SolrDocumentList(); list.add(doc); list.add(doc); //SolrDocumentList ->> List fail(binder.getBeans(User.class, list)); } /** * <b>function:</b> SolrInputDocument的相關(guān)方法 * @author hoojo * @createDate 2011-10-21 下午03:44:30 */ @Test public void docMethod() { SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", System.currentTimeMillis()); doc.addField("name", "SolrInputDocument"); doc.addField("age", 23, 1.0f); doc.addField("age", 22, 2.0f); doc.addField("age", 24, 0f); fail(doc.entrySet()); fail(doc.get("age")); //排名有用,類(lèi)似百度競價(jià)排名 doc.setDocumentBoost(2.0f); fail(doc.getDocumentBoost()); fail(doc.getField("name")); fail(doc.getFieldNames());//keys fail(doc.getFieldValues("age")); fail(doc.getFieldValues("id")); fail(doc.values()); }}
作者:
hoojo出處:
http://www.cnblogs.com/hoojo/archive/2011/10/21/2220431.htmlblog:
http://blog.csdn.net/IBM_hoojo本文版權歸作者和博客園共有,歡迎轉載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,否則保留追究法律責任的權利。