使用 springboot 整合 elasticSearch7.8 完成商品检索

发布时间:2021-10-02 23:42:16
修改时间:2022-07-20 14:54:00
总阅读数:1144
今日阅读数:0
昨日日阅读数:0
字数:11273

使用 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-releases.githubusercontent.com/2993595/37e17f00-b20f-11ea-81b1-415a52b06347?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20211002%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20211002T153414Z&X-Amz-Expires=300&X-Amz-Signature=673b1f2bd5cfb0114586c96f5e6f777395ae7a21c9c8971cb5af0a44d4efe37c&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=2993595&response-content-disposition=attachment%3B%20filename%3Delasticsearch-analysis-ik-7.8.0.zip&response-content-type=application%2Foctet-stream

(若以上链接不可用,可在该链接下查找)

https://github.com/medcl/elasticsearch-analysis-ik/releases?after=v7.10.0

解压配置

将elasticSearch解压至合适的位置,然后将ik分词器解压至elasticSearch的plugins目录


使用docker镜像

若使用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;
        }
}