微服务SpringCloudAlibaba springboot整合ElasticSearch实现商品搜索功能

微服务SpringCloudAlibaba springboot整合ElasticSearch实现商品搜索功能,第1张

微服务SpringCloudAlibaba springboot整合ElasticSearch实现商品搜索功能 话不多说直接开始

首先在模块中添加search模块(搜索)

service-goods(商品)

service-search(搜索)

service-wms(仓储)

 

 加入依赖

 
    
        7.4.2
    

    
    
        
            org.elasticsearch.client
            elasticsearch-rest-high-level-client
            7.4.2
    

创建几个包

 

创建ES的配置类ElasticSearchConfig
@Configuration
@ConfigurationProperties(prefix = "es")
@Setter
public class ElasticSearchConfig {
    private String hostname;
    private int port;
    private String protocol;

    //请求的一些选项
    public static final RequestOptions COMMON_OPTIONS;

    static{
        RequestOptions.Builder builder=RequestOptions.DEFAULT.toBuilder();
        COMMON_OPTIONS=builder.build();
    }

    @Bean
    public RestHighLevelClient restHighLevelClient(){
        RestClientBuilder restClientBuilder=
                RestClient.builder(new HttpHost(hostname,port,protocol));

        RestHighLevelClient restHighLevelClient=
                new RestHighLevelClient(restClientBuilder);

        return restHighLevelClient;
    }

}
在创建ES的工具类 可以直接调用其中的我封装好的方法
  1. 索引ES保存数据
  2. 主键新增的数据
  3. 根据ID删除单条记录
  4. 更新数据
  5. 根据ID从ES中查询数据
  6. 查选条件构造器
  7. 使用form+size的方式实现ES分页查询
  8. 使用scroll实现ES分页查询
@Component
@Slf4j
public class ESUtil {

    @Resource
    private RestHighLevelClient restHighLevelClient;


    
    public  boolean saveBatch(String index,List bachList){
        BulkRequest bulkRequest=new BulkRequest();

        //封装保存的数据
        for(int i=0;i T select(String index,String id, Class targetClass){

        SearchRequest searchRequest=new SearchRequest();
        searchRequest.indices(index);
        //构建查询条件
        SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.termQuery("_id",id));
        searchRequest.source(searchSourceBuilder);

        try {
           SearchResponse searchResponse= restHighLevelClient.search(searchRequest,ElasticSearchConfig.COMMON_OPTIONS);
           SearchHits hits= searchResponse.getHits();
           SearchHit [] searchHits= hits.getHits();  //查询结果
           if(searchHits==null||searchHits.length==0){  //如果数组为空,表示没有查询结果
               return null;
           }
           String jsonString= searchHits[0].getSourceAsString();
           T t= JSON.parseObject(jsonString,targetClass);
           return t;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    
    public   List select(String index,Class targetClass,
                                            SearchSourceBuilder searchSourceBuilder){
        SearchRequest searchRequest=new SearchRequest(); //查询请求
        searchRequest.indices(index);
        searchRequest.source(searchSourceBuilder); //请求中添加中查询条件
        List list=new ArrayList<>();
        try {
            //执行查询,获得查询结果
            SearchResponse searchResponse=
                    restHighLevelClient.search(searchRequest,ElasticSearchConfig.COMMON_OPTIONS);
            SearchHits hits= searchResponse.getHits();  //封装结果
            SearchHit[] searchHits= hits.getHits();
            for(SearchHit searchHit:searchHits){
               String jsonString= searchHit.getSourceAsString();
               T t=JSON.parseObject(jsonString,targetClass);
               list.add(t);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return list;
    }

    
    public  Map page(String index,
                                        SearchSourceBuilder searchSourceBuilder,
                                        Class targetClass,int from,int size){
        SearchRequest searchRequest=new SearchRequest();
        searchRequest.indices(index);
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);
        Map result=new HashMap<>();
        List resultList=new ArrayList<>();
        int page=0;
        try {
          SearchResponse searchResponse=
                  restHighLevelClient.search(searchRequest,ElasticSearchConfig.COMMON_OPTIONS);
          SearchHits hits=  searchResponse.getHits();
          long totalValue=hits.getTotalHits().value; //获得总记录数    3.0/2=1.5    Math.ceil(1.5)   2.0;
          page  = (int) Math.ceil((double)totalValue/size); //总页数
          SearchHit [] searchHits=  hits.getHits();
          for(SearchHit searchHit:searchHits){
             String jsonString= searchHit.getSourceAsString();
             T t= JSON.parseObject(jsonString,targetClass);
             resultList.add(t);
          }
        } catch (IOException e) {
            e.printStackTrace();
        }
        result.put("page",page);
        result.put("list",resultList);
        return result;
    }


    
    public  Map page(String index,
                                SearchSourceBuilder searchSourceBuilder,
                                Class targetClass,int size,String scrollId){
        SearchRequest searchRequest=new SearchRequest();
        searchRequest.indices(index);

        Scroll scroll=new Scroll(Timevalue.timevalueMinutes(1)); //指定scroll镜像的时间为1分钟
        searchSourceBuilder.size(size); //每页显示多少条记录
        Map map=new HashMap<>();
        SearchResponse searchResponse=null;
        try {
            if(StringUtils.isBlank(scrollId)){ //scroll方式的第一次查询
               searchRequest.scroll(scroll); //查询是scroll查询 镜像的时间为1分钟
               searchRequest.source(searchSourceBuilder);  //查询请求中添加查询条件
               searchResponse=restHighLevelClient.search(searchRequest,ElasticSearchConfig.COMMON_OPTIONS);
            }else{   //scroll方式的后面查询   请求:GET /_search/scroll
                SearchScrollRequest searchScrollRequest=new SearchScrollRequest();
                searchScrollRequest.scroll(scroll);
                searchScrollRequest.scrollId(scrollId);
                searchResponse=restHighLevelClient.scroll(searchScrollRequest,ElasticSearchConfig.COMMON_OPTIONS);
            }
            //封装查询结果
            map= searchResponseToMap(searchResponse,size,targetClass);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }

    //当前页的数据    scrollId    总页数
    private  Map
                      searchResponseToMap(SearchResponse searchResponse,int size,Class targetClass){
        SearchHits hits= searchResponse.getHits(); //查询的结果

        double count=hits.getTotalHits().value; //获得总记录数
        int page=  (int)Math.ceil(count/size);  //算出总页数
        Map map=new HashMap<>();  //返回的结果
        List list=new ArrayList<>();   //当前页的数据
        SearchHit [] searchHits=  hits.getHits(); //获得hits中的数据


        for(SearchHit temp:searchHits){
           String jsonString= temp.getSourceAsString();
           T t=JSON.parseObject(jsonString,targetClass);
           list.add(t);
        }
        map.put("page",page);
        map.put("scrollId",searchResponse.getScrollId());
        map.put("list",list);
        return map;
    }
}
实体类
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Company {
    private String name;
    private String job;
    private String logo;
    private Double payment;

    @Override
    public String toString() {
        return "name:"+name+",job:"+job+",logo:"+logo+",payment:"+payment;
    }
}
yml文件
es:
  hostname: 外网IP/本地IP
  port: 9200
  protocol: http

启动类

 写完之后我们可以先用测试类测试

添加
    @Test
    public void testSave(){
        Company company=
                Company.builder().name("广坤").job("东北F4")
                            .logo("DBF4").payment(20000.0).build();

        List companyList=new ArrayList<>();
        companyList.add(company);
        companyList.add(company);
        companyList.add(company);

        esUtil.saveBatch("company-index",companyList);
    }
删除
 @Test
    public void testDelete(){
        esUtil.deleteById("company-index","5");
    }
修改
   @Test
    public void testUpdate(){
       Company company= Company.builder().name("广坤").job("东北F4")
                       .logo("DBF4").payment(20000.0).build();
       String jsonString=JSONObject.toJSonString(company);
       esUtil.updateById("company-index","4",jsonString);
    }
按ID查询
    @Test
    public void testQueryId(){
      Company company=  esUtil.select("company-index","1000",Company.class);
        System.out.println(company+"!!!!!!!!!!!");
    }
查询(from+size)
    @Test
    public void testQuery(){
       SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
//       searchSourceBuilder.query(QueryBuilders.matchQuery("name","scott"));
       List companyList=
               esUtil.select("company-index",Company.class,searchSourceBuilder);

       for(Company temp:companyList){
           System.out.println(temp.getName());
       }
    }
查询(scrollID)
    @Test
    public void testPage2(){
        SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();

        Map map=  esUtil.page("company-index",
                      searchSourceBuilder,Company.class,2,"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAFSu4WMlRKYnQzcW1RMDJBeVhnODF6ekxTQQ==");

        int page=(int)map.get("page");
        List companyList=(List)map.get("list");
        String scrollId=(String)map.get("scrollId");
        System.out.println("总页数:"+page);
        System.out.println("每页的数据:"+companyList.size());
        System.out.println(scrollId);
    }

全部测试完 没有问题 下面可以去kibanna写DSL语句

创建索引 修改映射

PUT goods-index
{
    "mappings" : {
      "properties" : {
        "brandId" : {
          "type" : "long"
        },
        "brandImg" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "brandName" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "catalogId" : {
          "type" : "long"
        },
        "categoryName" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "hasStock" : {
          "type" : "boolean"
        },
        "hotScore" : {
          "type" : "long"
        },
        "list" : {
          "type" : "nested",
          "properties" : {
            "attrName" : {
              "type" : "keyword",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "attrValue" : {
              "type" : "keyword",
              "fields" : {
                "keyword" : {
                  "type" : "keyword",
                  "ignore_above" : 256
                }
              }
            },
            "skuId" : {
              "type" : "long"
            }
          }
        },
        "saleCount" : {
          "type" : "long"
        },
        "skuId" : {
          "type" : "long"
        },
        "skuImg" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "skuPrice" : {
          "type" : "long"
        },
        "skuTitle" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "spuId" : {
          "type" : "long"
        }
      }
    }
  }
}

有些类型需要改成keyword

查看映射

GET /goods-index/_mapping

查数据 写出DSL语句才好在java中实现

GET /goods-index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "华为",
            "fields": [
              "brandName",
              "skuTitle"
            ]
          }
        }
      ],
      "filter": [
        {
          "term": {
            "catalogId": "225"
          }
        },
        {
          "term": {
            "brandId": "5"
          }
        },
        {
      "nested": {
      "path": "list",
      "query": {
        "bool": {
         "must": [
           {
             "match": {
               "list.attrName": "颜色"
             }
           },
           {
             "match": {
               "list.attrValue": "星河银"
             }
           }
         ]
        }
      }
    }
        }
      ]
    }
  },
  "from": 0,
  "size": 2,
  "sort": [
    {
      "hotScore": {
        "order": "desc"
      }
    }
  ]
}

解释:

  1. 使用ESmulti_match查询 query: "华为"  查询的字段是fields:brandName,skuTitle根据这两个字段查询出带有华为的数据
  2. filter term过滤出 catalogId = 225 term brandId=5
  3. nested 是类型是对象数据类型的专用版本,它允许对象数组以可以彼此独立查询的方式进行索引。path:集合字段名。一个match相当于list集合中一个字段名list.attrName ="颜色"list.attrValue="星河银"
  4. from 从第几条开始 size一页展示多少条
  5. sort排序 根据什么字段进行排序,order=desc 倒排

 ES中拿到数据 我们现在去java开始编写代码

@PostMapping("/search")
    public QueryResult queryResultSearch(@RequestBody SearchQueryParam searchQueryParam) {
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //转类型
        String sortName = searchQueryParam.getSortName();
        String sortValue = searchQueryParam.getSortValue();
        String keyWord = searchQueryParam.getKeyWord();
        Integer brandId = searchQueryParam.getBrandId();
        Integer categoryId = searchQueryParam.getCategoryId();
        List attrList = searchQueryParam.getAttrList();
        //排序sort
        if (!StringUtils.isNotEmpty(sortName) && (!StringUtils.isNotEmpty(sortValue) && searchQueryParam.getSortValue().equals("asc"))) {
            searchSourceBuilder.sort(searchQueryParam.getSortName(), SortOrder.ASC);
        }
        if (!StringUtils.isNotEmpty(sortName) && (!StringUtils.isNotEmpty(sortValue) && searchQueryParam.getSortValue().equals("desc"))) {
            searchSourceBuilder.sort(searchQueryParam.getSortName(), SortOrder.DESC);
        }
        int size = 2;
        int from = (searchQueryParam.getCurrentPage() - 1) * size;

        //搜索框
        if (!StringUtils.isNotEmpty(keyWord)) {
            boolQueryBuilder.must(QueryBuilders.multiMatchQuery(keyWord, "brandName", "skuTitle"));
        }
        //filter 属性
        if (brandId != 0 || brandId != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brandId", brandId));
        }
        if (categoryId != 0 || categoryId != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", categoryId));
        }
        //list类型 nested
        for (SearchQueryParam.Attr attr : attrList) {
            BoolQueryBuilder boolQueryBuilder1 = QueryBuilders.boolQuery();
            List must = boolQueryBuilder1.must();
            must.add(QueryBuilders.matchQuery("list.attrName", attr.getAttrName()));
            must.add(QueryBuilders.matchQuery("list.attrValue", attr.getAttrValue()));

            boolQueryBuilder.filter(QueryBuilders.nestedQuery("list", boolQueryBuilder1, ScoreMode.None));
        }
        searchSourceBuilder.query(boolQueryBuilder);

        Map page = esUtil.page("goods-index", searchSourceBuilder, SkuEsDTO.class, from, size);

        List list = (List) page.get("list");
        System.out.println("元素个数:" + list.size());


        //这里开始方法 封装 ES代码到这里结束!!!!
        SearchSourceBuilder searchSourceBuilder1 = new SearchSourceBuilder();
        List select = esUtil.select("goods-index", SkuEsDTO.class, searchSourceBuilder1);
        System.out.println("select :" + select.size());
        QueryResult queryResult = new QueryResult();
        HashSet attrHashSet = new HashSet<>();
        //BrandVo添加数据
        for (SkuEsDTO skuEsDTO : select) {
            Attr attr1 = new Attr();
            queryResult = new QueryResult();
            BrandVo brandVo = new BrandVo();

            String brandName = skuEsDTO.getBrandName();
            Long brandIdOne = skuEsDTO.getBrandId();
            //brandVo添加数据
            brandVo.setId(brandIdOne.intValue());
            brandVo.setName(brandName);
            queryResult.getBrandVo().add(brandVo);

            //Catalog添加数据
            CategoryVo categoryVo = new CategoryVo();
            Long catalogId = skuEsDTO.getCatalogId();
            String categoryName = skuEsDTO.getCategoryName();
            categoryVo.setCatalogId(catalogId.intValue());
            categoryVo.setCategoryName(categoryName);
            queryResult.getCategoryVoList().add(categoryVo);
            //list 颜色
            for (SkuEsDTO.Attr attr : skuEsDTO.getList()) {
                String attrName = attr.getAttrName();
                String attrValue = attr.getAttrValue();
                attr1.setAttrValue(attrValue);
                attrHashSet.add(attr1);
                queryResult.getAttrsMap().put(attrName, attrHashSet);
            }
        }

        queryResult.setTotalPage(select.size());
        queryResult.setSkuEsDTOList(list);

        return queryResult;
    }

 

 swagger 测试

{
  "attrList": [
    {
      "attrName": "颜色",
      "attrValue": "星河银",
      "skuId": 1
    }
  ],
  "brandId": 5,
  "categoryId": 225,
  "currentPage": 1,
  "keyWord": "华为",
  "sortName": "hotScore",
  "sortValue": "desc"
}

 展示成功!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5676847.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存