本次目標
後端:使用Elasticsearch實現商品搜索。
前端:使用Vue對查詢的商品列表進行展示。
使用Elasticsearch實現商品搜索
配置TransportClient客戶端
通過TransportClient發起對ES(Elasticsearch的簡稱)的索引創建、關鍵詞查詢等功能。
1. 在工程的POM.xm文件中添加相關依賴Jar包。
在父級工程中,添加TransportClient的版本管理:
<!--Elasticsearch-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>5.5.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>5.5.2</version>
</dependency>
內容管理系統,添加TransportClient依賴:
<!--Elasticsearch-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
</dependency>
這樣,內容管理微服務對TransportClient和Elasticsearch(前者依賴後者,版本需要統一)的Jar包都使用的5.5.2版本的了:
這裏需要注意,你服務器的版本需要和這裏的統一,否則會造成連接失敗。
2. 創建TransportClient對象,由Spring容器對其管理。
package liwen.zhao.config;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.InetAddress;
import java.util.List;
@Configuration
@ConfigurationProperties("config.es")
public class ESconfig {
private List<String> nodes;
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
private TransportClient transportClient;
@Bean
public TransportClient iniTransportClient(){
transportClient=new PreBuiltTransportClient(Settings.EMPTY);
//獲取配置文件中nodes節點信息中的ip和port
for(String node: nodes){
String ip=node.split(":")[0];
String port=node.split(":")[1];
try {
InetSocketTransportAddress address= new InetSocketTransportAddress(InetAddress.getByName(ip), Integer.parseInt(port));
transportClient.addTransportAddress(address);
} catch (Exception e) {
e.printStackTrace();
}
}
return transportClient;
}
}
創建操作TransportClient的工具類:
package liwen.zhao.utils;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.client.transport.TransportClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component("esUtil")
public class ESUtil {
@Autowired
TransportClient transportClient;
//創建索引
public TransportClient indexIsExists(String name){
transportClient.admin().indices().prepareCreate(name).get().isAcknowledged();
//拿到集羣索引管理對象
IndicesAdminClient indicesAdminClient = transportClient.admin().indices();
//判斷這個索引是否存在
IndicesExistsResponse indicesExistsResponse = indicesAdminClient.prepareExists(name).get();
if(!indicesExistsResponse.isExists()){
throw new RuntimeException("already exists");
}else{
return transportClient;
}
}
}
配置文件中需要添加:
#Elasticsearch
config.es.nodes=192.168.43.88:9300
使用數據庫中的商品數據創建索引
1.控制層和業務層代碼實現
控制層:
@Controller
@RestController
@RequestMapping("/es")
public class ElasticsearchController {
@Autowired
private ElasticsearchService elasticsearchService;
@RequestMapping("/createIndex")
public SysResult createIndex(String indexName,String type){
return elasticsearchService.createIndexByNameAndType(indexName,type);
}
}
業務邏輯層:
package liwen.zhao.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import liwen.zhao.common.jd.pojo.Article;
import liwen.zhao.common.jd.vo.SysResult;
import liwen.zhao.mappers.ArticleMapper;
import liwen.zhao.utils.ESUtil;
import org.elasticsearch.client.transport.TransportClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class ElasticsearchServiceImpl implements ElasticsearchService {
@Autowired
ArticleMapper articleMapper;
@Autowired
private ESUtil esUtil;
private ObjectMapper mapper=new ObjectMapper();
@Override
public SysResult createIndexByNameAndType(String indexName,String type) {
try{
TransportClient client = esUtil.indexIsExists(indexName);
//創建索引成功
//將document數據寫入到索引中 type
//讀取數據庫數 0,100
List<Article> articles = articleMapper.selectArticleByPage(0, 100);
for(Article a:articles){
//每次拿到一個商品對象
//String aJson = mapper.writeValueAsString(p);
Map<String ,String> articleMap=new HashMap<String ,String>();
articleMap.put("title",a.getTitle());
articleMap.put("content",a.getContent());
articleMap.put("img",a.getImg());
client.prepareIndex(indexName,type,a.getId()+"")
.setSource(articleMap).get();
}
return SysResult.ok();
}catch (Exception e){
e.printStackTrace();
return SysResult.build(500,"索引創建失敗!",null);
}
}
}
2.完成商品索引的創建
在瀏覽器輸入:http://localhost/es/createIndex?indexName=jd&type=shop,將創建索引名稱爲jd,類型爲shop的索引。並將數據庫的100條數據錄入到Elasticsearch。
ps:這個操作直接暴露在外不太安全和正規,應該在後臺進行操作並添加認證授權流程。還是那句話,實現京東商城的每個細節不太現實,本次目標主要是爲了通過Elasticsearch實現一下搜索功能。
使用Vue對查詢的商品列表進行展示
準備商品數據查詢的接口:
前端代碼-數據獲取:
<script type="application/javascript">
var vm = new Vue({
el:"#vue",
data:{
text:"",
page:1,
rows:10,
shops:[]
},
methods:{
submit:function () {
var ts=this;
const url="/es/search";
let datas = {params:{"text":this.text,"page":this.page,"rows":this.rows}};
axios.get(url,datas).then(function(resp){
console.log(resp.data);
ts.shops=resp.data;
});
}
}
})
</script>
前端代碼-數據綁定:
<!-- 商品列表 start-->
<div class="goodslist mt10">
<ul>
<li v-for="shop in shops">
<dl>
<dt><a href=""><img v-bind:src="shop.img" alt="" /></a></dt>
<dd><a href="">{{shop.title}}</a></dt>
<dd><strong>¥{{shop.price}}</strong></dt>
<dd><a href=""><em>已有10人評價</em></a></dt>
</dl>
</li>
</ul>
</div>
效果展示
搜索“手機”:
我們看到,前兩個商品,和手機密切相關,搜出來了,權重最高,排在了前面。第三個商品,機油裏有“機”,次相關。後面兩個商品,手錶裏有“手”,也是次相關。
總結
本案例,通過elasticsearch實現了商品搜索功能。 除建立索引用到了數據庫,其它過程都是Elasticsearch實現的。 這樣既實現了精準搜索,又減小了數據庫訪問壓力。