效果演示视频和教学讲解视频地址:演示地址
1.首先在Kibana中创建索引和类型。
PUT /campus_market
{
"mappings": {
"product" : {
"properties" : {
"name" : {
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"suggest" : {
"type" : "completion",
"analyzer": "ik_max_word"
}
}
},
"info": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
2.然后在Kibana中导入文档数据,或者在SpringBoot中项目导出也是可以。本人是采取后者的做法,创建一个定时任务,定时从MySQL中取出数据然后更新ES中的数据。
@Scheduled(cron = "*/20 * * * * ?") //20s执行一次
// @Scheduled(cron = "0 */10 * * * ?") //10分钟执行一次
private void configureESTasks() {
logger.info("开始更新ES中数据....");
ResponseDTO<List<ProductDTO>> productDTOList = productService.getProductList(new ProductDTO());
List<Product> productList = CopyUtil.copyList(productDTOList.getData(), Product.class);
List<Product> allProductList = productMapper.selectByExample(new ProductExample());
//先清空ElasticSearch中的数据
for(Product product : allProductList) {
productRepository.delete(product);
}
//从数据库中查询出数据添加到ES中
for(Product product : productList){
productRepository.save(product);
}
logger.info("更新ES中数据完成....");
}
写定时任务之前,别忘了先给对应实体类创建Repository接口并且要在配置文件中进行相应配置。
Repository接口
public interface ProductRepository extends ElasticsearchRepository<Product,String> {
}
config配置文件配置
@Configuration
public class ESRestClientConfig extends AbstractElasticsearchConfiguration {
@Override
@Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("127.0.0.1:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
}
3.接下来就是正戏了,写业务逻辑层代码!!!进行SuggestBuilder创建,然后获取查询出来的结果(代码中的keyword就是查询匹配的结果内容),返回给前端。
@Qualifier("elasticsearchClient")
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 智能匹配搜索
* @param productDTO
* @return
*/
@Override
public ResponseDTO<List<String>> suggestCompletionProduct(ProductDTO productDTO) {
List<String> resultList = new ArrayList<>();
CompletionSuggestionBuilder suggestion = SuggestBuilders
.completionSuggestion("name.suggest").prefix(productDTO.getName()).skipDuplicates(true);
SuggestBuilder suggestBuilder = new SuggestBuilder();
suggestBuilder.addSuggestion("product", suggestion);
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.suggest(suggestBuilder);
searchRequest.indices("campus_market").types("product").source(searchSourceBuilder);
try {
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
Suggest suggest = response.getSuggest();
//SearchResponse response = elasticsearchRestTemplate.suggest(suggestBuilder, Product.class);
int maxSuggestNum = 0; // 最多5个
if (suggest != null) {
List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> entries
= suggest.getSuggestion("product").getEntries();
for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> entry : entries) {
for (Suggest.Suggestion.Entry.Option option : entry.getOptions()) {
String keyword = option.getText().string();
if (maxSuggestNum < 5) {
resultList.add(keyword);
maxSuggestNum++;
} else {
break;
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
return ResponseDTO.errorByMsg(CodeMsg.SYSTEM_ERROR);
}
return ResponseDTO.success(resultList);
}
4.前端Vue要对用户输入的内容进行监听,然后调用接口获取数据。
<div class="search-input">
<el-input v-model="searchName" placeholder="搜一搜好货" @input="changeInput" clearable>
<el-select v-model="searchCategoryId" slot="prepend">
<el-option label="全部类目" value="0">el-option>
<el-option v-for="(item, index) in categoryList" :key="index" :label="item.name" :value="item.id">el-option>
el-select>
el-input>
<div class="suggest-content">
<div class="suggest-content-item" @click="toProductDetail(item.id)" v-for="(item, index) in suggestList" :key="index">
{{item.name}}
div>
div>
div>
changeInput(e) {
let _this = this;
// 函数节流,防止数据频繁更新,每300毫秒才搜索一次
if (!this.timer) {
this.timer = setTimeout(function(){
_this.getSuggestList(e);
_this.timer = null;
},300)
}
},
getSuggestList(e) {
let _this = this;
this.$ajax.post(process.env.VUE_APP_SERVER + "/product/suggest", {name: e}).then((response)=>{
let resp = response.data;
let resultList = [];
if(resp.code === 0){
let data = resp.data;
_this.productList.forEach(item => {
data.forEach(name => {
if(item.name === name) {
let val = {
id: item.id,
name: item.name
};
resultList.push(val);
}
})
});
}
_this.suggestList = resultList;
});
}
.suggest-content {
position: absolute;
background: #fff;
width: 480px;
margin-left: 110px;
border: 1px solid #444444;
z-index: 1;
height: auto;
max-height: 300px;
overflow-y: auto;
display: block;
}
.suggest-content-item {
width: 98%;
margin: 5px 5px;
cursor: pointer;
}
.search-input .el-select {
width: 110px;
}
.search-input {
width: 590px;
}