利用solr實(shí)現(xiàn)商品的搜索功能(實(shí)例講解)
后期補(bǔ)充:
為什么要用solr服務(wù),為什么要用luncence?
問(wèn)題提出:當(dāng)我們?cè)L問(wèn)購(gòu)物網(wǎng)站的時(shí)候,我們可以根據(jù)我們隨意所想的內(nèi)容輸入關(guān)鍵字就可以查詢(xún)出相關(guān)的內(nèi)容,這是怎么做到呢?這些隨意的數(shù)據(jù)不可能是根據(jù)數(shù)據(jù)庫(kù)的字段查詢(xún)的,那是怎么查詢(xún)出來(lái)的呢,為什么千奇百怪的關(guān)鍵字都可以查詢(xún)出來(lái)呢?
答案就是全文檢索工具的實(shí)現(xiàn),luncence采用了詞元匹配和切分詞。舉個(gè)例子:北京天安門(mén)------luncence切分詞:北京 京天 天安 安門(mén) 等等這些分詞。所以我們搜索的時(shí)候都可以檢索到。
有一種分詞器就是IKAnalyzer中文分詞器,它有細(xì)粒度切分和智能切分,即根據(jù)某種智能算法。
這就使用solr的最大的好處:檢索功能的實(shí)現(xiàn)。
使用步驟;
(1)solr服務(wù)器搭建,因?yàn)椋螅铮欤蚴怯茫辏幔觯幔甸_(kāi)發(fā)的,所以需要jdk和tomcat。搭建部署
(2)搭建完成后,我們需要將要展示的字段引入solr的庫(kù)中。配置spring與solr結(jié)合,工程啟動(dòng)的時(shí)候啟動(dòng)solr
(3)將數(shù)據(jù)庫(kù)中的查詢(xún)內(nèi)容導(dǎo)入到solr索引庫(kù),這里使用的是solrj的客戶(hù)端實(shí)現(xiàn)的。具體使用可以參考api
(4)建立搜索服務(wù),供客戶(hù)端調(diào)用。調(diào)用solr,查詢(xún)內(nèi)容,這中間有分頁(yè)功能的實(shí)現(xiàn)。solr高亮顯示的實(shí)現(xiàn)。
(5)客戶(hù)端接收頁(yè)面的請(qǐng)求參數(shù),調(diào)用搜索服務(wù),進(jìn)行搜索。
業(yè)務(wù)字段判斷標(biāo)準(zhǔn):
1、在搜索時(shí)是否需要在此字段上進(jìn)行搜索。例如:商品名稱(chēng)、商品的賣(mài)點(diǎn)、商品的描述
(這些相當(dāng)于將標(biāo)簽給了solr,導(dǎo)入商品數(shù)據(jù)后,solr?qū)@些字段的對(duì)應(yīng)的商品的具體內(nèi)容進(jìn)行分詞切分,然后,我們就可以搜索到相關(guān)內(nèi)容了)
2、后續(xù)的業(yè)務(wù)是否需要用到此字段。例如:商品id。
需要用到的字段:
1、商品id
2、商品title
3、賣(mài)點(diǎn)
4、價(jià)格
5、商品圖片
6、商品分類(lèi)名稱(chēng)
7、商品描述
Solr中的業(yè)務(wù)字段:
1、id——》商品id
其他的對(duì)應(yīng)字段創(chuàng)建solr的字段。
<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_desc" type="text_ik" indexed="true" stored="false" /> <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 source="item_desc" dest="item_keywords"/>
重新啟動(dòng)tomcat
Solr 是Apache下的一個(gè)頂級(jí)開(kāi)源項(xiàng)目,采用Java開(kāi)發(fā),它是基于Lucene的全文搜索服務(wù)器。Solr提供了比Lucene更為豐富的查詢(xún)語(yǔ)言,同時(shí)實(shí)現(xiàn)了可配置、可擴(kuò)展,并對(duì)索引、搜索性能進(jìn)行了優(yōu)化。
Solr是一個(gè)全文檢索服務(wù)器,只需要進(jìn)行配置就可以實(shí)現(xiàn)全文檢索服務(wù)。有效降低頻繁訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)對(duì)數(shù)據(jù)庫(kù)造成的壓力。
第一步:將solr部署在linux系統(tǒng)下。
第二步:solrJ是solr的客戶(hù)端,使用它需要依賴(lài)solrJ的jar包。
第三步:將數(shù)據(jù)庫(kù)的內(nèi)容添加到solr的索引庫(kù),這樣查詢(xún)就在索引庫(kù)查詢(xún),而不是數(shù)據(jù)庫(kù)了。
controller層:
@Controller
@RequestMapping("/manager")
public class ItemController {
@Autowired
private ItemService itemService;
@RequestMapping("/importall")
@ResponseBody
public TaotaoResult importAllItem(){
TaotaoResult result= itemService.importAllItem();
return result;
}
}<br>service層編寫(xiě):<br>多表查詢(xún)商品,顯示在頁(yè)面的邏輯編寫(xiě):<br>mapper.java
package com.taotao.search.mapper;
import java.util.List;
import com.taotao.search.pojo.Item;
public interface ItemMapper {
List<Item> getItemList();
}
mapper.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="com.taotao.search.mapper.ItemMapper"> <select id="getItemList" resultType="com.taotao.search.pojo.Item"> 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 </select> </mapper>

第四步:從索引庫(kù)查詢(xún)的邏輯編寫(xiě):
//從索引庫(kù)里面獲取商品信息,現(xiàn)在這個(gè)dao層是從索引庫(kù)獲取信息,因?yàn)橹暗膶?xiě)的邏輯是將db里面的數(shù)據(jù)導(dǎo)入到索引庫(kù)。后面的查詢(xún)都是從索引庫(kù)中進(jìn)行,而不從數(shù)據(jù)庫(kù)了
@Repository
public class SearchDaoImpl implements SearchDao {
@Autowired
private SolrServer solrServer;
@Override
public SearchResult search(SolrQuery query) throws Exception {
//這是從索引庫(kù)里面,直接執(zhí)行查詢(xún)
QueryResponse response = solrServer.query(query);
//獲取查詢(xún)的結(jié)果
SolrDocumentList documentList= response.getResults();
SearchResult result=new SearchResult();
//這是獲取總記錄數(shù)
result.setRecordCount(documentList.getNumFound());
List<Item> itemList=new ArrayList<>();
//商品的高亮顯示,即當(dāng)鼠標(biāo)移到字上時(shí),該字體變色,這是從QueryResponse中獲取的
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
for (SolrDocument solrDocument : documentList) {
//每個(gè)SolrDocument都是一個(gè)商品pojo的內(nèi)容,所以這里要?jiǎng)?chuàng)建一個(gè)商品的pojo對(duì)象,來(lái)獲取詳細(xì)的字段
Item item=new Item();
item.setId((String) solrDocument.get("id"));
//高亮顯示是title的高亮顯示
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");
}
item.setTitle(title);
item.setPrice((Long) solrDocument.get("item_price"));
item.setImage((String) solrDocument.get("item_image"));
item.setCategory_name((String) solrDocument.get(" item_category_name"));
item.setSell_point((String) solrDocument.get("item_sell_point"));
itemList.add(item);
}
result.setItemList(itemList);
return result;
}
}
第五步:索引庫(kù)內(nèi)容建立好后,開(kāi)始編寫(xiě)對(duì)外的服務(wù)接口,即通過(guò)條件搜索具體的商品,比如手機(jī),會(huì)顯示出總共的手機(jī)列表信息,第幾頁(yè),總共多少頁(yè),總共多少個(gè)搜索結(jié)果

請(qǐng)求的url:
/search/query?q={查詢(xún)條件}&page={page}&rows={rows}
返回的結(jié)果:TaotaoResult包裝商品列表。
創(chuàng)建一個(gè)sql語(yǔ)句對(duì)應(yīng)的pojo,單獨(dú)建立一個(gè)pojo
用來(lái)裝顯示的內(nèi)容列表:
public class Item {
private String id;
private String title;
private String sell_point;
private long price;
private String image;
private String category_name;
private String item_des;
}
controller層:
@Controller
public class SearchController {
@Autowired
private SearchService searchService;
@RequestMapping(value="/query", method=RequestMethod.GET)
@ResponseBody
public TaotaoResult search(@RequestParam("q")String queryString,
@RequestParam(defaultValue="1")Integer page,
@RequestParam(defaultValue="60")Integer rows) {
//查詢(xún)條件不能為空
if (StringUtils.isBlank(queryString)) {
return TaotaoResult.build(400, "查詢(xún)條件不能為空");
}
SearchResult searchResult = null;
try {
queryString = new String(queryString.getBytes("iso8859-1"), "utf-8");
searchResult = searchService.search(queryString, page, rows);
} catch (Exception e) {
e.printStackTrace();
return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
}
return TaotaoResult.ok(searchResult);
}
}<br><br><br>
1
<span style="font-size: 16px">service層:利用solrJ的solrQurery來(lái)查詢(xún):</span>
前提是要寫(xiě)好如何從索引庫(kù)讀取數(shù)據(jù):
下面是服務(wù)的接口層編寫(xiě):
controller:
@Controller
public class SearchController {
@Autowired
private SearchService searchService;
@RequestMapping(value="/query", method=RequestMethod.GET)
@ResponseBody
public TaotaoResult search(@RequestParam("q")String queryString,
@RequestParam(defaultValue="1")Integer page,
@RequestParam(defaultValue="60")Integer rows) {
//查詢(xún)條件不能為空
if (StringUtils.isBlank(queryString)) {
return TaotaoResult.build(400, "查詢(xún)條件不能為空");
}
SearchResult searchResult = null;
try {
queryString = new String(queryString.getBytes("iso8859-1"), "utf-8");
searchResult = searchService.search(queryString, page, rows);
} catch (Exception e) {
e.printStackTrace();
return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
}
return TaotaoResult.ok(searchResult);
}
}
@Service
public class SearchServiceImpl implements SearchService {
@Autowired
private SearchDao searchDao;
@Override
public SearchResult search(String queryString, int page, int rows) throws Exception {
SolrQuery query=new SolrQuery();
query.setQuery(queryString);
query.setStart((page-1)*rows);
query.setRows(rows);
//設(shè)置默認(rèn)的查詢(xún)搜索域,即默認(rèn)的查詢(xún)
query.set("df","item_keywords");
//設(shè)置高亮顯示
query.setHighlight(true);
query.addHighlightField("item_title");
query.setHighlightSimplePre("<em style=\"color:red\">");
query.setHighlightSimplePost("</em>");
//執(zhí)行查詢(xún)
SearchResult searchResult = searchDao.search(query);
//根據(jù)結(jié)果來(lái)計(jì)算商品總共多少頁(yè)
long recordCount=searchResult.getRecordCount();
long pageCount=recordCount/rows;
if (recordCount % rows > 0) {
pageCount++;
}
searchResult.setPageCount(pageCount);
searchResult.setCurPage((long) page);
return searchResult;
}
}
客戶(hù)端通過(guò)輸入商品來(lái)實(shí)現(xiàn)搜索功能:
controller層:
@Controller
public class SearchController {
@Autowired
private SearchService searchService;
@RequestMapping("/search")
public String search(@RequestParam("q")String queryString, @RequestParam(defaultValue="1")Integer page, Model model) {
if (queryString != null) {
try {
queryString = new String(queryString.getBytes("iso8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
SearchResult searchResult = searchService.search(queryString, page);
//向頁(yè)面?zhèn)鬟f參數(shù)
model.addAttribute("query", queryString);
//model.addAttribute("totalPages", searchResult.getPageCount());
model.addAttribute("itemList", searchResult.getItemList());
model.addAttribute("page", page);
return "search";
}
}
service層:
@Service
public class SearchServiceImpl implements SearchService {
@Value("${SEARCH_BASE_URL}")
private String SEARCH_BASE_URL;
@Override
public SearchResult search(String queryString, int page) {
//這里需要的是連接+參數(shù).這里每頁(yè)顯示的記錄條數(shù),可以傳遞也可以不用傳遞
// 調(diào)用taotao-search的服務(wù)
//查詢(xún)參數(shù)
Map<String, String> param = new HashMap<>();
param.put("q", queryString);
param.put("page", page + "");
try {
//調(diào)用服務(wù)
String json = HttpClientUtil.doGet(SEARCH_BASE_URL, param);
//把字符串轉(zhuǎn)換成java對(duì)象
TaotaoResult taotaoResult = TaotaoResult.formatToPojo(json, SearchResult.class);
SearchResult result = (SearchResult) taotaoResult.getData();
return result;
/* if (taotaoResult.getStatus() == 200) {
}*/
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
以上這篇利用solr實(shí)現(xiàn)商品的搜索功能(實(shí)例講解)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java實(shí)現(xiàn)簡(jiǎn)單GUI登錄和注冊(cè)界面
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單GUI登錄和注冊(cè)界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Java刷題之最小k個(gè)數(shù)的思路及具體實(shí)現(xiàn)
這篇文章主要介紹了Java刷題之最小k個(gè)數(shù)的思路及具體實(shí)現(xiàn),最小K個(gè)數(shù)是一個(gè)經(jīng)典的top-K問(wèn)題,可以通過(guò)整體排序、建立小根堆或大根堆的方式解決,排序方式時(shí)間復(fù)雜度較高,適合數(shù)據(jù)量小的場(chǎng)景,小根堆適合k較小的情況,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-10-10
如何通過(guò)zuul添加或修改請(qǐng)求參數(shù)
這篇文章主要介紹了如何通過(guò)zuul添加或修改請(qǐng)求參數(shù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java多線(xiàn)程實(shí)現(xiàn)FTP批量上傳文件
這篇文章主要為大家詳細(xì)介紹了Java多線(xiàn)程實(shí)現(xiàn)FTP批量上傳文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
SpringCloud?Ribbon負(fù)載均衡流程分析
在Eureka注冊(cè)中心中我們?cè)谔砑油闌LoadBalanced注解,即可實(shí)現(xiàn)負(fù)載均衡功能,現(xiàn)在一起探索一下負(fù)載均衡的原理(Ribbon),感興趣的朋友一起看看吧2024-03-03
Spring?Boot實(shí)現(xiàn)文件上傳下載
這篇文章主要為大家詳細(xì)介紹了Spring?Boot實(shí)現(xiàn)文件上傳下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
Java實(shí)現(xiàn)動(dòng)態(tài)獲取文件的絕對(duì)路徑
我們知道在?Java?中讀取一些配置文件信息,是在開(kāi)發(fā)中十分常用的要求。這篇文章就來(lái)和大家聊聊Java如何實(shí)現(xiàn)動(dòng)態(tài)獲取文件的絕對(duì)路徑,感興趣的可以了解一下2023-02-02
Springboot解決no main manifest attribute錯(cuò)誤
在開(kāi)發(fā)Springboot項(xiàng)目時(shí),使用java -jar命令運(yùn)行jar包可能出現(xiàn)no main manifest attribute錯(cuò)誤,本文就來(lái)介紹一下該錯(cuò)誤的解決方法,感興趣的可以了解一下2024-09-09
劍指Offer之Java算法習(xí)題精講數(shù)組與字符串
跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過(guò)之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化2022-03-03

