Solr全文搜尋伺服器的搭建以及在Java中的使用(solr單機版)
直接步入正題。。。。。。
Solr的搭建環境:
JDK:1.8.0_161
Tomcat:7.0.57
OS:CentOS 7
Solr服務搭建:
第一步:將solr的壓縮包上傳至Linux系統,下載地址:http://www.apache.org/dyn/closer.lua/lucene/solr/7.3.1
第二步:解壓,命令:tar zxf solr-4.10.3.tgz 我這裡用的是很早之前下載的,最新版本是7.3.1,解壓完成後得到一個目錄:
第三步:將solr部署到Tomcat中,solr工程的war包位於解壓目錄下的dist資料夾下:
這裡我在/usr/local資料夾中新建了一個solr資料夾,solr資料夾中有一個資料夾tomcat,其中放了一份tomcat,命令:
cp solr-4.10.3.war /usr/local/solr/tomcat/webapps/solr.war
第四步:啟動Tomcat,然後Tomcat就會自動解壓war包,待其解壓好之後關閉tomcat,將webapps下的solr的war包刪除。
第五步:將/root/solr-4.10.3/example/lib/ext目錄下的所有jar包,新增到solr工程中
第六步:建立一個solrhome。解壓後的/example/solr目錄就是一個solrhome。複製此目錄到/usr/local/solr/solrhome
第七步:關聯solr和solrhome。修改solr工程的web.xml檔案:
位置:/usr/local/solr/tomcat/webapps/solr/WEB-INF/web.xml
需要注意的是,<env-entry>標籤原來是在註釋標籤中的,不要忘了刪除上下的註釋標記。
第八步:啟動Tomcat,訪問solr:
至此,Solr服務的搭建就完成了,但是具體使用還需要進行一些配置:
Solr業務域的配置
這裡我以一個具體的需求做示範,需求為商品搜尋功能的實現,首先看一下資料表:
然後看一下商品搜尋後的展示頁面:
可以看到,需要的資訊有:商品標題、商品價格、商品圖片、商品分類,而我們下一步肯定檢視商品詳情,所以還需要商品ID,同時,對於購物網站商品的查詢,肯定不只是根據標題查詢,還會根據賣點查詢,所以賣點資訊也是需要的。
因此,我們需要在schema.xml中定義
1.商品id
2.商品標題
3.商品賣點
4.商品價格
5.商品圖片
6.分類名稱
而建立對應的業務域需要制定中文分詞器,這裡我用的是IKAnalyzer。
業務域的建立:
第一步:新增中文分詞器到工程中
1.將IKAnalyzer的jar包新增到solr工程的lib目錄下
2.在solr工程的WEB-INF目錄下建立目錄classes,並編寫擴充套件詞典與配置檔案:
a) 建立配置檔案: touch IKAnalyzer.cfg.xml
b) 建立擴充套件詞典和擴充套件停止詞典 touch mydict.dic 和 touch ext_stopword.dic 當分詞時遇到擴充套件詞典中的詞,不會對其進行拆分,比如安陽師範學院不會再拆分成為 安陽、師範、學院,而是作為一個整體
因為這裡涉及到了中文,所以編輯是在本地遠端完成的。
第二步:配置一個FieldType,指定使用IKAnalyzer
修改/usr/local/solr/solrhome/collection1/conf/schema.xml檔案,新增如下程式碼,放到最後的</schema>標籤之前即可:
第三步:配置業務域,也是在這個檔案中,其中type使用鋼材定義的fieldType:
<field name="item_title" type="text_ik" indexed="true" stored="true"/>
<field name="item_sell_point" type="text_ik" indexed="true" stored="true"/>
<field name="item_price" type="long" indexed="true" stored="true"/>
<field name="item_image" type="string" indexed="false" stored="true" />
<field name="item_category_name" type="string" indexed="true" stored="true" />
<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<copyField source="item_title" dest="item_keywords"/>
<copyField source="item_sell_point" dest="item_keywords"/>
<copyField source="item_category_name" dest="item_keywords"/>
copyField的作用是,比如這裡我要通過item_keywords搜尋,搜尋k,那麼只要item_title或者item_sell_point或者item_category_name中含有k欄位的,都會查詢出來。
然後重啟Tomcat,使得配置生效。
分詞測試:
可以看到,“劉強東”已經作為了一個整體。
Solr服務的使用
匯入索引
在使用之前,我們首先要做的是將資料匯入索引庫中,這裡以Java的方式實現:
實體類:SearchItem.java:
package cn.e3mall.common.pojo;
import java.io.Serializable;
public class SearchItem implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String title;
private String sell_point;
private long price;
private String image;
private String category_name;
// 省略setter、getter方法
}
ItemMapper.java
package cn.e3mall.search.mapper;
import java.util.List;
import cn.e3mall.common.pojo.SearchItem;
public interface ItemMapper {
List<SearchItem> getItemList();
}
ItemMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.e3mall.search.mapper.ItemMapper">
<select id="getItemList" resultType="cn.e3mall.common.pojo.SearchItem">
SELECT
a.id,
a.title,
a.sell_point,
a.price,
a.image,
b. NAME category_name
FROM
tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
WHERE
a.`status` = 1
</select>
</mapper>
Spring配置:
<!-- 單機版solrJ,引數為solr伺服器的url -->
<bean class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<constructor-arg index="0" value="http://192.168.25.130:8080/solr/collection1"/><!-- /collection1可省略 -->
</bean>
從資料庫中查詢,處理後匯入索引庫:
package cn.e3mall.search.service.impl;
import java.util.List;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.common.SolrInputDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.e3mall.common.pojo.SearchItem;
import cn.e3mall.common.utils.E3Result;
import cn.e3mall.search.mapper.ItemMapper;
import cn.e3mall.search.service.SearchItemService;
/**
* 索引庫維護Service
*/
@Service
public class SearchItemServiceImpl implements SearchItemService {
@Autowired
private ItemMapper itemMapper;
@Autowired
private SolrServer solrServer;
@Override
public E3Result importAllItem() {
try {
// 查詢商品列表
List<SearchItem> itemList = itemMapper.getItemList();
// 遍歷商品列表
for (SearchItem item : itemList) {
// 建立文件物件
SolrInputDocument document = new SolrInputDocument();
// 向文件物件中新增域
document.addField("id", item.getId());
document.addField("item_title", item.getTitle());
document.addField("item_sell_point", item.getSell_point());
document.addField("item_price", item.getPrice());
document.addField("item_image", item.getImage());
document.addField("item_category_name", item.getCategory_name());
// 把文件物件寫入索引庫
solrServer.add(document);
}
// 提交
solrServer.commit();
// 返回匯入成功
return E3Result.ok();
} catch (Exception e) {
e.printStackTrace();
return E3Result.fail();
}
}
}
這樣,便可以將資料庫中的資料匯入索引庫,之後查詢商品便不再經過資料庫,而是直接從Solr中檢索,不但可以大大提高查詢速率,查詢準確率也可以大大提高。簡單查詢示例:
@Test
public void queryIndex() throws Exception {
// 建立一個SolrServer物件,建立一個連結。引數solr伺服器的url
SolrServer solrServer = new HttpSolrServer("http://192.168.25.130:8080/solr/collection1");
// 建立一個solrQuery查詢物件
SolrQuery query = new SolrQuery();
// 設定查詢條件
// query.setQuery("*:*");
query.set("q", "*:*"); // 和上面的等同,*:*表示查詢所有
// 執行查詢,得到queryResponse物件
QueryResponse response = solrServer.query(query);
// 取文件列表(當前頁文件),取查詢結果總記錄數
SolrDocumentList solrDocumentList = response.getResults();
long numFound = solrDocumentList.getNumFound();
System.out.println("查詢結果總記錄數:" + numFound);
System.out.println("-----------------------------------------------");
// 遍歷文件列表,從文件中取域的內容。
for (SolrDocument solrDocument : solrDocumentList) {
System.out.println(solrDocument.get("id"));
System.out.println(solrDocument.get("item_title"));
System.out.println(solrDocument.get("item_sell_point"));
System.out.println(solrDocument.get("item_price"));
System.out.println(solrDocument.get("item_image"));
System.out.println(solrDocument.get("item_category_name"));
System.out.println("----------------------------------------------------------------------------------------------");
}
};
複雜查詢示例:
@Test
public void queryIndexFuZa() throws Exception {
// 建立一個SolrServer物件,建立一個連結。引數solr伺服器的url
SolrServer solrServer = new HttpSolrServer("http://192.168.25.130:8080/solr/collection1");
// 建立一個solrQuery查詢物件
SolrQuery query = new SolrQuery();
// 設定查詢條件
query.setQuery("三星");
// 設定查詢的起始索引,類似於mysql的limit a,b 中的a
query.setStart(0);
// 設定查詢的條目數,類似於mysql的limit a,b 中的b
query.setRows(20);
// 設定查詢的業務域
query.set("df", "item_title");
// 設定開啟高亮顯示
query.setHighlight(true);
// 設定要高亮的列
query.addHighlightField("item_title");
// 設定高亮顯示的字首
query.setHighlightSimplePre("<em>");
// 設定高亮顯示的字尾
query.setHighlightSimplePost("</em>");
// 執行查詢,得到queryResponse物件
QueryResponse queryResponse = solrServer.query(query);
// 取文件列表(當前頁文件),取查詢結果總記錄數
SolrDocumentList solrDocumentList = queryResponse.getResults();
System.out.println("查詢結果總記錄數:" + solrDocumentList.getNumFound());
System.out.println("-----------------------------------------------");
// 遍歷文件列表,從文件中取域的內容。
for (SolrDocument solrDocument : solrDocumentList) {
System.out.println(solrDocument.get("id"));
// 取高亮顯示
Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
List<String> list = highlighting.get(solrDocument.get("id")).get("item_title");
String title = "";
if (list != null && list.size() > 0) {
title = list.get(0);
} else {
title = (String) solrDocument.get("item_title");
}
System.out.println(title);
System.out.println(solrDocument.get("item_sell_point"));
System.out.println(solrDocument.get("item_price"));
System.out.println(solrDocument.get("item_image"));
System.out.println(solrDocument.get("item_category_name"));
System.out.println("----------------------------------------------------------------------------------------------");
}
};
程式碼中幾乎每一行都加有註釋,在這裡就不一一解釋了,最後再說一下Solr查詢返回的結果的格式:
Java程式碼與響應的對照:
- QueryResponse queryResponse = solrServer.query(query); 對應的是整個響應
- SolrDocumentList solrDocumentList = queryResponse.getResults();對應的是響應的查詢結果,所以可以通過solrDocumentList.getNumFound()方法獲得總共查詢到的結果數。
這裡我為了方便解釋高亮查詢的結果,再查詢一個:
盡力了,哈哈,就這樣吧