使用 springboot 整合 elasticSearch7.8 完成商品检索
架构介绍
后端使用springboot2.X,将商品数据存储于mysql,然后使用elasticSearch(7.8)检索商品
文档中使用的是7.8版本,不适用7.8以下版本
环境搭建
下载相关软件
elasticSearch 7.8 下载地址
https://www.elastic.co/cn/downloads/past-releases/elasticsearch-7-8-0
ik分词器 7.8 下载地址(分词器版本要与es版本一致)
(若以上链接不可用,可在该链接下查找)
https://github.com/medcl/elasticsearch-analysis-ik/releases?after=v7.10.0
解压配置
将elasticSearch解压至合适的位置,然后将ik分词器解压至elasticSearch的plugins目录
使用docker镜像
若使用docker无需以上步骤,直接拉取docker镜像
为了方便使用,此镜像已配置ik分词器
registry.cn-hangzhou.aliyuncs.com/zhongzq/es-ik
相关文档
elasticSearch官方文档:https://www.elastic.co/cn/elasticsearch/
springboot项目搭建
创建项目
略
加入相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<!-- <version>2.4.0</version>-->
</dependency>
application.yml
spring:
elasticsearch:
rest:
uris: http://localhost:9200
业务代码
商品上架、下架、修改(将商品信息存入es)
goodsTO.java
@Data
@Document(indexName = "goods")
public class GoodsTo {
@Id
private Integer id;
// @Field(type = FieldType.Text,analyzer = "ik_smart")
@Field(type = FieldType.Text,analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.Integer)
private Integer salesvolume;
@Field(type = FieldType.Double)
private Double price;
@Field(type = FieldType.Text,index = false)
private String description;
@Field(type = FieldType.Text,index = false)
private String image;
@Field(type = FieldType.Integer,index = false)
private Integer status;
@Field(type = FieldType.Date)
private Date uptime;
@Field(type = FieldType.Keyword)
private String catalog1;
@Field(type = FieldType.Keyword)
private String catalog2;
}
创建dao层接口
@Repository
public interface GoodsDao extends ElasticsearchRepository<GoodsTo,Integer> {
}
service主要代码
GoodsTo为商品数据,可自行编写代码从mysql等数据库查询
public class ElasticSearchService{
@Autowired
private GoodsDao goodsDao;
/**
* 上架商品(将商品保存到elasticSearch)
* @param goodsSpuTo
* @return
*/
public Res upGoods(GoodsTo goodsTo){
goodsDao.save(goodsTo);
return Res.success("上架成功");
}
/**
* 下架商品(将商品从elasticSearch删除)
* @param id
* @return
* @throws IOException
*/
@Override
public Res downGoods(Integer id) throws IOException {
goodsDao.deleteById(id);
return Res.success("下架成功");
}
/**
* 修改商品信息
* @param goodsSpuTo
* @return
*/
@Override
public Res updateGoods(GoodsSpuTo goodsSpuTo){
goodsDao.save(goodsTo);
return Res.success("修改成功");
}
}
检索商品
SearchParam.java (检索参数)
@Data
public class SearchParam {
private String keyWord;
private Integer minPrice;
private Integer maxPrice;
private String catalog2;
private String catalog1;
private String sort;
private String order;
private Integer pageNum=1;
private Integer pageSize=8;
}
SearchResult.java(检索结果)
@Data
public class SearchResult {
private List<GoodsSpuVo> spuList;
private List<Catalog2Vo> catalog2s;
private Long totalPage;
private Long totalNum;
}
service主要代码
public class ElasticSearchService{
@Autowired
private GoodsDao goodsDao;
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 保存商品
* @param goodsTo
* @return
*/
public Res upGoods(GoodsTo goodsTo){
goodsDao.save(goodsTo);
return Res.success("上架成功");
}
/**
* 检索商品
* @param searchParam 检索参数
* @return
* @throws IOException
*/
public SearchResult searchGoods(SearchParam searchParam) throws IOException {
/**
* 将检索关键词发送到rabbitmq中
*/
// String keyWord = searchParam.getKeyWord();
// if (keyWord != null) {
// rabbitTemplate.convertAndSend("mall.search-exchange","mall.searchWord",keyWord);
// }
SearchResult searchResult = new SearchResult();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
/**
* 商品关键词
*/
if (searchParam.getKeyWord() != null&&searchParam.getKeyWord().trim().length()>0) {
// FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("name", searchParam.getKeyWord());
// boolQueryBuilder.must(fuzzyQueryBuilder);
QueryStringQueryBuilder stringQueryBuilder = QueryBuilders.queryStringQuery(searchParam.getKeyWord()).defaultField("name");
boolQueryBuilder.must(stringQueryBuilder);
}
/**
* 分类筛选
*/
BoolQueryBuilder filer=QueryBuilders.boolQuery();
if (!StringUtils.isEmpty(searchParam.getCatalog2())){
filer.filter(QueryBuilders.termQuery("catalog2",searchParam.getCatalog2()));
}
/**
* 价格区间
*/
if (searchParam.getMinPrice()!=null||searchParam.getMaxPrice()!=null){
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price");
if (searchParam.getMinPrice()!=null) {
rangeQueryBuilder.gte(searchParam.getMinPrice());
}
if (searchParam.getMaxPrice()!=null) {
rangeQueryBuilder.lte(searchParam.getMaxPrice());
}
filer.filter(rangeQueryBuilder);
}
/**
* 排序
*/
FieldSortBuilder sortBuilder = null;
if (searchParam.getSort() != null) {
if ("asc".equals(searchParam.getOrder())){
sortBuilder=SortBuilders.fieldSort(searchParam.getSort()).order(SortOrder.ASC);
}
if ("desc".equals(searchParam.getOrder())){
sortBuilder=SortBuilders.fieldSort(searchParam.getSort()).order(SortOrder.DESC);
}
}
//
/**
* 分类信息聚合
*/
TermsAggregationBuilder catalog2Agg = AggregationBuilders.terms("catalog2_agg");
catalog2Agg.field("catalog2");
/**
* 高亮
*/
HighlightBuilder highlightBuilder=new HighlightBuilder();
highlightBuilder.field("name");
// highlightBuilder.preTags("<span style=\"color:red\">");
// highlightBuilder.postTags("</span>");
highlightBuilder.preTags("<font style=\"color:#F56C6C\">");
highlightBuilder.postTags("</font>");
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.withHighlightBuilder(highlightBuilder)
.addAggregation(catalog2Agg)
.withFilter(filer)
.withPageable(PageRequest.of(searchParam.getPageNum()-1, searchParam.getPageSize()));
if (sortBuilder != null) {
nativeSearchQueryBuilder.withSort(sortBuilder);
}
NativeSearchQuery query = nativeSearchQueryBuilder.build();
SearchHits<GoodsTo> search = elasticsearchRestTemplate.search(query, GoodsTo.class);
Aggregations aggregations = search.getAggregations();
ParsedStringTerms catalog2_agg = aggregations.get("catalog2_agg");
List<Catalog2Vo> catalog2List = catalog2_agg.getBuckets().stream().map(bucket -> {
Catalog2Vo catalog2Vo = new Catalog2Vo();
catalog2Vo.setName(bucket.getKeyAsString());
return catalog2Vo;
}).collect(Collectors.toList());
searchResult.setCatalog2s(catalog2List);
List<GoodsSpuVo> goodsSpuVoList = search.getSearchHits().stream().map(goodsToSearchHit -> {
GoodsSpuVo goodsSpuVo = new GoodsSpuVo();
GoodsTo goodsTo = goodsToSearchHit.getContent();
BeanUtils.copyProperties(goodsTo, goodsSpuVo);
List<String> name = goodsToSearchHit.getHighlightField("name");
if (name != null && name.size() > 0) {
String lightName = name.stream().collect(Collectors.joining());
goodsSpuVo.setName(lightName);
}
return goodsSpuVo;
}).collect(Collectors.toList());
searchResult.setSpuList(goodsSpuVoList);
searchResult.setTotalNum(search.getTotalHits());
log.info("检索结果:{}",searchResult);
return searchResult;
}
}