自己動手搭建搜索工具
作者 白寧超
2016年4月12日16:31:48
摘要:搜索已經作為生活中不可缺少的一部分,諸如:百度、google、還是在微信上尋找好友或者通過一段文本查找關鍵字。另外亞馬遜、京東、天貓、蘇寧等電商在搜索中也是別有洞天(多面搜索等)。對於開發人員,搜索往往是大部分應用的關鍵功能,特別是對大規模文本數據驅動應用更是如此。另一類搜索像語音智能檢索,其采用分類、聚類、神經網絡等方法進行模型評估,反饋給用戶比較理想的匹配結果,這里需要強調的是其采用評分機制反饋的模糊近似查詢結果,與傳統精確采用是不一樣的。這種結果的反饋評分主要依托正確率和召回率。這里自己構建搜索工具好處在於:靈活性、開發費用低、自己更了解自己的搜索工具、價格當然是免費的啦。本文作者花費大量時間,經過資料收集,研究和實驗所得,旨在技術分享。(本文原創,轉載需說明出處:自己動手搭建搜索工具。)
目錄
【文本挖掘(0)】快速了解什么是自然語言處理
【文本挖掘(1)】OpenNLP:駕馭文本,分詞那些事
【文本挖掘(2)】【NLP】Tika 文本預處理:抽取各種格式文件內容
【文本挖掘(3)】自己動手搭建搜索工具
1 Apache Solr搜索服務器簡介
1.1. Solr 是什么?
Solr它是一種開放源碼的、基於 Lucene Java 的搜索服務器,易於加入到 Web 應用程序中。Solr 提供了層面搜索(就是統計)、命中醒目顯示並且支持多種輸出格式(包括XML/XSLT 和JSON等格式)。它易於安裝和配置,而且附帶了一個基於HTTP 的管理界面。可以使用 Solr 的表現優異的基本搜索功能,也可以對它進行擴展從而滿足企業的需要。Solr的特性包括:
- 高級的全文搜索功能
- 專為高通量的網絡流量進行的優化
- 基於開放接口(XML和HTTP)的標准
- 綜合的HTML管理界面
- 可伸縮性-能夠有效地復制到另外一個Solr搜索服務器
- 使用XML配置達到靈活性和適配性
- 可擴展的插件體系
2. Lucene 是什么?
Lucene是一個基於Java的全文信息檢索工具包,它不是一個完整的搜索應用程序,而是為你的應用程序提供索引和搜索功能。Lucene 目前是 Apache Jakarta(雅加達) 家族中的一個開源項目。也是目前最為流行的基於Java開源全文檢索工具包。目前已經有很多應用程序的搜索功能是基於 Lucene ,比如Eclipse 幫助系統的搜索功能。Lucene能夠為文本類型的數據建立索引,所以你只要把你要索引的數據格式轉化的文本格式,Lucene 就能對你的文檔進行索引和搜索。
3. Solr vs Lucene
Solr與Lucene 並不是競爭對立關系,恰恰相反Solr 依存於Lucene,因為Solr底層的核心技術是使用Lucene 來實現的,Solr和Lucene的本質區別有以下三點:搜索服務器,企業級和管理。Lucene本質上是搜索庫,不是獨立的應用程序,而Solr是。Lucene專注於搜索底層的建設,而Solr專注於企業應用。Lucene不負責支撐搜索服務所必須的管理,而Solr負責。所以說,一句話概括 Solr: Solr是Lucene面向企業搜索應用的擴展。Solr使用Lucene並且擴展了它!一個真正的擁有動態字段(Dynamic Field)和唯一鍵(Unique Key)的數據模式(Data Schema)
- 對Lucene查詢語言的強大擴展!
- 支持對結果進行動態的分組和過濾
- 高級的,可配置的文本分析
- 高度可配置和可擴展的緩存機制
- 性能優化
- 支持通過XML進行外部配置
- 擁有一個管理界面
- 可監控的日志
- 支持高速增量式更新(Fast incremental Updates)和快照發布(Snapshot Distribution)
2 安裝下載最新的Solr
1)本文使用Solr4.8版本,Solr 最新版本有出入請以官方網站內容為准。Solr官方網站下載地址:http://www.apache.org/dyn/closer.cgi/lucene/solr/
Solr 程序包的部分目錄結構
dist :存放Solr 構建完成的JAR 文件、WAR 文件和Solr 依賴的JAR 文件。
example :是一個安裝好的Jetty 中間件,其中包括一些樣本數據和Solr 的配置信息。
example/etc :Jetty 的配置文件。
example/multicore :當安裝Slor multicore 時,用來放置多個Solr 主目錄。
example/solr :默認安裝時一個Solr 的主目錄。
example/webapps :Solr 的WAR 文件部署在這里。
Solr 的主目錄展開后為如下結構:
bin :建議將集群復制腳本放在這個目錄下。
conf :放置配置文件。
conf/schema.xml :建立索引的schema 包含了字段類型定義和其相關的分析器。
conf/solrconfig.xml :這個是Solr 主要的配置文件。
conf/xslt :包含了很多xslt 文件,這些文件能將Solr 的XML 的查詢結果轉換為特定的格式,比如:Atom/RSS。
data :放置Lucene 產生的索引數據。
lib :放置可選的JAR 文件比如對Slor 擴展的插件,這些JAR 文件將會在Solr 啟動時加載。
2)在example目錄有start.jar文件,啟動:瀏覽器訪問:http://localhost:8983/solr/,你看到的就是solr的管理界面
cd C:\\Users\\bnc\\Desktop\\NPL\\apache-solr\\example java -jar start.jar
目前你看到的界面沒有任何數據,你可以通過POSTing命令向Solr中添加(更新)文檔,刪除文檔,在exampledocs目錄包含一些示例文件,運行命令:
cd exampledocs java -jar post.jar *.xml
3 Solr在tomcat下配置集成與啟動驗證
1 下載solr源文件與tomcat源文件
2 建立文件夾命名solr放置3個文件夾:
solr-4.8.0\example\solr文件夾改名home
tomcat改名server
solr-4.8.0\dist下solr-4.8.0.war改名solr.war放在solr\server\webapps下
3 打開solr\server\conf下server.xml <Host></Host>放入
<Context path="" docBase="solr" reloadable="false" crosscontext="true"> <Environment name="sole/home" type="java.lang.String" value="C:\Users\bnc\Desktop\solr\home" override="true" /> </Context>
4 solr-4.8.0\example\lib\ext下庫文件放在solr\server\lib
5 配置solr\server\bin下setclasspath.bat
set "JAVA_HOME=C:\Program Files\Java\jdk1.7.0_79"
6 打開solr\server\bin下點擊startup.sh
7 啟動驗證
1)Tomcat(server)添加role和user \solr\server\conf下 tomcat-user.xml 添加
<role rolename="solr" />
<user username="admin" password="admin" roles="solr" />
2)solr\server\webapps\solr\WEB-INF和solr\server\webapps\ROOT\WEB-INF下web.xml
<security-constraint> <web-resource-collection> <web-resource-name>Solr Lockdown</web-recource-name> <url-pattern>/</url-pattern> </web-resource-collection> <auth-constraint> <role-name>solr</role-name> <role-name>admin</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>Solr</realm-name> </login-config>
4 solr中文分詞(MMSEG4j):配置中文分詞和驗證中文分詞
1 下載分詞工具
mmseg4j-analysis-*.jar mmseg4j-core-*.jar mmseg4j-solr-*.jar
2 拷貝到solr\server\webapps\solr\WEB-INF\lib和solr\server\webapps\ROOT\WEB-INF\lib
3 打開 \solr\home\collection1\conf下schema.xml添加
<fieldtype name="textComplex" class="solr.TextField" positionIncrementGap="100"> <analyzer> <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="complex" dicPath="../dic"/> </analyzer> </fieldtype> <fieldtype name="textMaxWord" class="solr.TextField" positionIncrementGap="100"> <analyzer> <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="max-word" /> </analyzer> </fieldtype> <fieldtype name="textSimple" class="solr.TextField" positionIncrementGap="100"> <analyzer> <tokenizer class="com.chenlb.mmseg4j.solr.MMSegTokenizerFactory" mode="simple" dicPath="../dic" /> </analyzer> </fieldtype>
然后修改
<field name="title" type="textComplex" indexed="true" stored="true" multiValued="true"/> <field name="subject" type="textComplex" indexed="true" stored="true"/> <field name="description" type="textComplex" indexed="true" stored="true"/> <field name="comments" type="textComplex" indexed="true" stored="true"/> <field name="author" type="textComplex" indexed="true" stored="true"/> <field name="keywords" type="textComplex" indexed="true" stored="true"/> <field name="category" type="textComplex" indexed="true" stored="true"/> <field name="resourcename" type="textComplex" indexed="true" stored="true"/> <field name="url" type="textComplex" indexed="true" stored="true"/> <field name="content_type" type="string" indexed="true" stored="true" multiValued="true"/> <field name="last_modified" type="date" indexed="true" stored="true"/> <field name="links" type="string" indexed="true" stored="true" multiValued="true"/>
4 啟動server/bin/start.bat文件
5 配置collection1 並在分析字段和字段類型配置中文分詞工具textComplex即可
5 solrJ后台的使用
1) solr-4.8.0\dist\solrj-lib下所有jar文件和solr-solrj-4.8.0.jar復制到java工程lib里面。
2) SolrJ客戶端插入數據
//Solr4.x版本插入數據 public void add(String id,String name) throws SolrServerException, IOException{ for(int i=0;i<5;i++){ SolrInputDocument doc=new SolrInputDocument(); doc.addField("id", id+i); doc.addField("name", "The "+name+" is "+i); server.add(doc); } }
3) SolrJ客戶端更數據
//Solr4.x版本插入數據 public void add(String id,String name) throws SolrServerException, IOException{ for(int i=0;i<5;i++){ SolrInputDocument doc=new SolrInputDocument(); doc.addField("id", id+i); doc.addField("name", "The "+name+" is "+i); server.add(doc); } }
4) SolrJ客戶端查詢數據
//solr查詢數據 public void select() throws SolrServerException{ ModifiableSolrParams params = new ModifiableSolrParams(); params.set("q", "id:*"); params.set("start", "0"); QueryResponse response = server.query(params); SolrDocumentList results = response.getResults(); for (int i = 0; i < results.size(); ++i) { System.out.println(results.get(i)); } }
5) SolrJ客戶端刪除數據
//刪除solr中的視頻文檔:按照規則刪除 public void delete() throws SolrServerException, IOException{ server.deleteByQuery("*:*"); server.commit(); }
6) 完整solrJ工具類源碼

package SolrSearch; import java.io.IOException; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrServer; import org.apache.solr.client.solrj.impl.HttpSolrServer; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.params.ModifiableSolrParams; /** * Solr工具類 * @author 白寧超 * @version 2016年4月11日12:57:01 */ public class SolrUtils { //baseURL是固定的訪問地址 public static final String SOLL_URL="http://localhost:8080/solr/"; private final HttpSolrServer server; public SolrUtils(){ server=new HttpSolrServer(SOLL_URL); server.setAllowCompression(true); //減少服務器交換的數據量 //server.setSoTimeout(72000); //設置收索超時 } //Solr4.x版本插入視頻文檔 public void add(Vidio vidio) throws SolrServerException, IOException{ SolrInputDocument doc=new SolrInputDocument(); doc.addField("id", vidio.getId()); doc.addField("name", vidio.getName()); doc.addField("type", vidio.getType()); server.add(doc); server.commit(); } //Solr4.x版本插入數據 public void add(String id,String name) throws SolrServerException, IOException{ for(int i=0;i<100;i++){ SolrInputDocument doc=new SolrInputDocument(); doc.addField("id", id+i); doc.addField("name", "The "+name+" is "+i); server.add(doc); } server.commit(); } //SolrCloud插入數據 public static void addCloud(String id,String name) throws SolrServerException, IOException { String zkHost = "localhost:8080"; String defaultCollection = "collection1"; CloudSolrServer server = new CloudSolrServer(zkHost); System.out.println(); for (int i = 0; i < 1000; ++i) { SolrInputDocument doc = new SolrInputDocument(); doc.addField("id", "book-" + i); doc.addField("name", "The Legend of Po part " + i); server.add(doc); } server.commit(); } //更新solr中的視頻文檔 public void update(Vidio vidio) throws SolrServerException, IOException { add(vidio); } //更新solr中信息 public void update(String id,String name) throws SolrServerException, IOException { add(id,name); } //刪除solr中的視頻文檔:按照規則刪除 public void delete() throws SolrServerException, IOException{ server.deleteByQuery("*:*"); server.commit(); } //刪除solr中的視頻文檔:按照規則刪除 public void deleteById(Vidio vidio) throws SolrServerException, IOException{ server.deleteById(vidio.getId() +""); server.commit(); } //solr查詢數據 public void select() throws SolrServerException{ ModifiableSolrParams params = new ModifiableSolrParams(); params.set("q", "id:*"); params.set("start", "0"); QueryResponse response = server.query(params); SolrDocumentList results = response.getResults(); for (int i = 0; i < results.size(); ++i) { System.out.println(results.get(i)); } } }
6 本文總結
1、解壓solr-4.8.0.zip到你想到存放的路徑,比如:d:/solr
2、cmd打開命令行窗口,進入d:/solr/example目錄
3、執行命令:java -jar start.jar
4、通過第三步以后,系統會啟動solr自帶的jetty服務器,通過http://localhost:8983/solr/便可訪問solr。
2)如何創建索引?
此時solr已安裝並啟動,但是還沒有索引,只有創建好索引,搜索才能有結果
1、cmd進入/solr/example/exampledocs目錄
2、執行命令:java -jar post.jar solr.xml monitor.xml,此時你已成功提交了2個solr文檔
3、執行完第二步后,我們可以通過瀏覽器訪問:http://localhost:8983/solr/collection1/select?q=solr&wt=xml
如果你想導入更多的文檔,執行命令:java -jar post.jar *.xml
3)如何更新索引?
當你重復執行命令:java -jar post.jar *.xml后,發現搜索的結果沒有出現重復的數據,原因:example目錄下的schema.xml中指定了列id為uniqueKey(即:唯一),所以當你重復提交數據到索引庫時,id相同的數據會替換原來document中的數據。
如果你想要得到重復的數據,你可以通過修改exampledocs目錄下*.xml中id值的方式實現
4)如何刪除索引?
1、執行命令:java -Ddata=args -Dcommit=false -jar post.jar “<delete><id>SP2514N</id></delete>”,可以刪除id為SP2514N的document
2、執行第一步后,再去搜索發現搜索結果中還有id為SP2514N的數據,難道我們第一步刪除不成功嗎?其實不是,因為第一步的命令中-Dcommit=false,所以第一步的刪除操作沒有提交到索引(index)中。
3、在沒有打開新的searcher之前,第一步刪除數據會一直存在於搜索結果中,所以我們可以強制打開一個新的searcher,執行命令:java -jar post.jar -
5)如何查詢數據
solr通過http以get的方式進行搜索數據,如:http://localhost:8983/solr/collection1/select?q=solr&wt=xml
q:查詢的關鍵詞(此時查詢的字段是solrconfig.xml中指定的默認查詢字段<str name=”df”>text</str>)
fl:搜索結果返回的字段
sort:排序
wt:搜索結果返回格式
- q=video&fl=name,id (return only name and id fields)
- q=video&fl=name,id,score (return relevancy score as well)
- q=video&fl=*,score (return all stored fields, as well as relevancy score)
- q=video&sort=price desc&fl=name,id,price (add sort specification: sort by price descending)
- q=video&wt=json (return response in JSON format)
排序
sorl提供了通過一個或多個字段進行排序的方法,使用sort參數,參數格式為“字段 排序(asc或desc)”。
score也可以用來排序
復雜的排序
如果沒有指定sort參數,默認”score desc”進行排序,把匹配度最高的優先顯示
6)如何高亮顯示
有時候我們想高亮顯示匹配的關鍵詞,可以通過參數hl=true,並指定需高亮顯示的字段hl.fl=name,features
…&q=video card&fl=name,id&hl=true&hl.fl=name,features
默認會把匹配的關鍵用“<em>”標簽進行包裝,如<em>手機</em>
7)如何進行門面搜索?
前面“查詢數據”一欄返回的是整個文檔的數據,門面搜索可以根據我們的需求返回結果,如下:
1、以下例子搜索整個文檔並根據字段cat技術匹配數量:
…&q=*:*&facet=true&facet.field=cat
注意:上面的例子雖然結果中只顯示10條,但返回的數量是整個文檔中匹配查詢條件的總的數量。
2、在例子1的基礎上可以再加一個字段inStock:
…&q=*:*&facet=true&facet.field=cat&facet.field=inStock
3、solr同樣可以為任意查詢條件計算數量,以下例子查詢關鍵為ipod、價格在0-99和>100
…&q=ipod&facet=true&facet.query=price:[0 TO 100]&facet.query=price:[100 TO *]
4、以下例子查詢字段manufacturedate_dt在2004年到2010年:
搜索界面
solr提供了搜索界面:http://localhost:8983/solr/collection1/browse
8)如何進行文本解析?
solr創建索引和進行搜索時都需要對文字進行解析,解析時需要用到分詞器,中文的分詞器我推薦使用mmseg4j分詞器
solr核心的配置文件是schema.xml,索引庫結構的定義及對每個字段采用什么分詞器等都在這個文件里面進行配置
如:
<field name=”features” type=”text_en_splitting” indexed=”true” stored=”true” multiValued=”true”/>
7 本文所有文件下載
1 Apache Cat 8.0下載:https://yunpan.cn/cqGwpLKtZ6MVF 訪問密碼 03ba
2 solr源文件以及本文配置文件打包下載: https://yunpan.cn/cqGwCsbpfy6p8 訪問密碼 dcfe
3 javaJ的java源碼下載: https://yunpan.cn/cqGQyevGaZ47W 訪問密碼 9f1a
8 參考文獻:
相關文章
【文本處理】自然語言處理在現實生活中運用
【文本處理】多種貝葉斯模型構建及文本分類的實現
【文本處理】快速了解什么是自然語言處理
【文本處理】領域本體構建方法概述