前言
Elasticsearch搜索引擎整合SpringBoot,官方的RestClient,封装了ES操作,API层次分明,上手简单。此处为Elasticsearch-Rest-Client在实际项目中的一种应用。
本文章建立在Elasticsearch-Rest-Client整合springboot的前提下
Elasticsearch-Rest-Client整合springboot
一、feign服务调用(cloud使用,boot项目直接忽略)
此处搜索引擎的搜索对象为商品,在SpringCloud中需使用服务调用来调用商品模块的方法
1.引入依赖
service模块中引入依赖
<!--服务调用-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.调用方增加注解
service_search模块启动类上增加注解
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
3.调用端-创建调用接口
package com.ly.search.client;
import com.ly.commonutils.R;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "service-shop")
public interface UserClient {
@GetMapping("/shopservice/clothes/allGoods") //所有商品信息
public R AllGoods();
}
二、使用步骤
1.创建搜索的对象类
//商品搜索实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Clothes implements Serializable {
@ApiModelProperty(value = "衣服id")
private String id;
@ApiModelProperty(value = "店铺id")
private String businessId;
@ApiModelProperty(value = "标题")
private String title;
@ApiModelProperty(value = "图片")
private String photo;
@ApiModelProperty(value = "价格")
private BigDecimal price;
@ApiModelProperty(value = "销售数量")
private Long buyCount;
@ApiModelProperty(value = "浏览次数")
private Long viewCount;
@ApiModelProperty(value = "类型")
private String type;
@ApiModelProperty(value = "状态,0:已下架,1:已上架,2:审核中,3:违规未通过")
private Integer status;
}
2.编写service及其实现类
service:
public interface SearchService {
//获取索引内容
List<Clothes> getIndex(String string);
//Api方式创建索引及其数据
void getByApi();
//定时更新索引
void task();
}
实现类:
实现类加上@EnableScheduling注解开启定时任务
@Service
@EnableScheduling //开启定时任务
public class SeachServiceImpl implements SearchService {
添加索引及定时更新索引,使用服务调用,将商品模块的方法获得的数据作为索引数据
@Autowired
private RestHighLevelClient client;
@Autowired
private UserClient userClient; //商品模块调用接口
@PostConstruct //Spring启动后对象自动注入完成后执行
@Override
public void getByApi() {
//获取消息R中的数据
R info=userClient.AllGoods();
Map<String, Object> map=info.getData();
//此处编译通过,但集合遍历报java.util.LinkedHashMap cannot be cast to xxx数据转换异常错误
List<Clothes> list= (List<Clothes>) map.get("items");
//可利用alibaba的fastjson工具包先将数据转换为json格式,再转为我们需要的格式
String jsonString=JSON.toJSONString(list);
List<Clothes> datas=JSONObject.parseArray(jsonString,Clothes.class);
datas.forEach(System.out::println);
//1.创建索引
IndexRequest indexRequest=new IndexRequest("goods");
int i=0;
//2.执行操作
for (Clothes clothes:datas) {
i+=1;
indexRequest.id(String.valueOf(i));
indexRequest.type("clothes");
String json=JSON.toJSONString(clothes);
indexRequest.source(json, XContentType.JSON);//要保存的内容
IndexResponse index = null;
try {
index = client.index(indexRequest, ElasticSearchConfig.COMMON_OPTIONS);
System.out.println("数据添加成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
@Scheduled(fixedRate = 1000*60) //一分钟执行一次
public void task() {
//删除索引
DeleteIndexRequest indexRequest=new DeleteIndexRequest("goods");
try {
client.indices().delete(indexRequest,ElasticSearchConfig.COMMON_OPTIONS);
System.out.println("索引删除成功");
} catch (IOException e) {
e.printStackTrace();
}
//执行创建索引方法
this.getByApi();
System.err.println("执行更新索引定时任务时间: " + LocalDateTime.now());
}
搜索方法的实现
@Override
public List<Clothes> getIndex(String str) {
//1.创建检索请求
SearchRequest searchRequest=new SearchRequest();
//指定索引
searchRequest.indices("shops");
//指定DSL,检索条件
SearchSourceBuilder sourceBuilder=new SearchSourceBuilder();
//1.1构造检索条件,使用matchPhrase对查询内容str整体匹配
sourceBuilder.query(QueryBuilders.matchPhraseQuery("title",str));
//ES折叠,根据字段数据去重,此处类型必须为keyword
sourceBuilder.collapse(new CollapseBuilder("id.keyword"));
searchRequest.source(sourceBuilder);
//2.创建搜索实体类集合接收数据
List<Clothes> list=new ArrayList<Clothes>();
//3.执行索引
SearchResponse searchResponse=null;
try {
searchResponse=client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
//4.获取所有查到的数据
SearchHits hits=searchResponse.getHits();
SearchHit[] searchHits=hits.getHits();
//5.转换为搜索实体类数据
for (SearchHit hit:searchHits
) {
String string=hit.getSourceAsString();
Clothes clothes=JSON.parseObject(string, Clothes.class);
list.add(clothes);
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
controller类:
@RestController
@CrossOrigin
@RequestMapping("searchservice")
public class SearchController {
@Autowired
private SearchService searchService;
@ApiOperation("商品搜索")
@PostMapping("search/{string}")
public R search(
@ApiParam(name = "string",value = "搜索内容",required = true)
@PathVariable String string
){
if(string!=null &&string!="") {
List<Clothes> list = searchService.getIndex(string);
return R.ok().data("items", list);
}else{
return R.error();
}
}
}
总结
1.索引的创建与删除不能经过controller,经过的话意味着用户的每请求一次就要重新创建索引、删除索引;而在高并发情况下,多个用户同时请求也会造成同时间创建(或删除)大量索引,导致Elasticsearch客户端宕机。
2.为了保证索引的数据正确性,设置定时任务,定时更新索引,且更新间隔不应太长。