话不多说直接开始

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

service-goods(商品)

service-search(搜索)

service-wms(仓储)

es层级标签搜索_elasticsearch

 

 加入依赖

<!--elasticsearch版本 7.4.2 -->
    <properties>
        <elasticsearch.version>7.4.2</elasticsearch.version>
    </properties>

    <!--引入依赖: elasticsearch-rest-high-level-client -->
    <dependencies>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>7.4.2</version>
    </dependency>

创建几个包

es层级标签搜索_es层级标签搜索_02

 

创建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;


    /**
     * @param index 索引
     * @param bachList 保存的数据
     * @param <T>
     * @return
     */
    public <T extends Object> boolean saveBatch(String index,List<T> bachList){
        BulkRequest bulkRequest=new BulkRequest();

        //封装保存的数据
        for(int i=0;i<bachList.size();i++){
            T t= bachList.get(i);
            String jsonString= JSON.toJSONString(t);
            IndexRequest indexRequest = new IndexRequest();
            indexRequest.index(index);
            indexRequest.source(jsonString,XContentType.JSON);
            bulkRequest.add(indexRequest);
        }

        try {
            restHighLevelClient.bulk(bulkRequest,ElasticSearchConfig.COMMON_OPTIONS);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * ES保存数据
     *
     * @param index      索引
     * @param id         主键
     * @param jsonString 新增的数据
     * @return
     */
    public boolean saveOne(String index, String id, String jsonString) {
        //封装保存的数据
        IndexRequest indexRequest = new IndexRequest();
        indexRequest.index(index);
        indexRequest.id(id);
        indexRequest.source(jsonString, XContentType.JSON);
        try {
            //执行新增操作
            IndexResponse indexResponse =
                    restHighLevelClient.index(indexRequest, ElasticSearchConfig.COMMON_OPTIONS);
            log.info("新增的结果:" + indexResponse);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 根据ID删除单条记录
     *
     * @param index
     * @param id
     * @return
     */
    public boolean deleteById(String index, String id) {
        DeleteRequest deleteRequest = new DeleteRequest();
        deleteRequest.index(index);
        deleteRequest.id(id);
        try {
            DeleteResponse deleteResponse =
                    restHighLevelClient.delete(deleteRequest,
                            ElasticSearchConfig.COMMON_OPTIONS);
            log.info("删除的结果:" + deleteResponse);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 更新数据
     *
     * @param index
     * @param id
     * @param jsonString
     * @return
     */
    public boolean updateById(String index, String id, String jsonString) {
        UpdateRequest updateRequest = new UpdateRequest();
        updateRequest.index(index);
        updateRequest.id(id);
        updateRequest.doc(jsonString, XContentType.JSON);

        try {
            UpdateResponse response =
                    restHighLevelClient.update(updateRequest, ElasticSearchConfig.COMMON_OPTIONS);
            log.info("更新的结果:" + response);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据ID从ES中查询数据
     * @param index
     * @param id
     * @param targetClass
     * @return
     *
     */
    public <T extends  Object> T select(String index,String id, Class<T> 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;
    }

    /**
     *
     * @param index
     * @param targetClass
     * @param searchSourceBuilder  查选条件的构建器
     * @param <T>
     * @return
     */
    public <T extends  Object>  List<T> select(String index,Class<T> targetClass,
                                            SearchSourceBuilder searchSourceBuilder){
        SearchRequest searchRequest=new SearchRequest(); //查询请求
        searchRequest.indices(index);
        searchRequest.source(searchSourceBuilder); //请求中添加中查询条件
        List<T> 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;
    }

    /**
     * 使用from+size的方式实现 ES的分页查询
     * @param index  索引
     * @param searchSourceBuilder   查询条件
     * @param targetClass   目标类
     * @param from      从第几条开始
     * @param size      显示多少条记录
     * @param <T>
     * @return  Map: 当前页的数据   总页数
     */
    public <T extends Object> Map<String,Object> page(String index,
                                        SearchSourceBuilder searchSourceBuilder,
                                        Class<T> targetClass,int from,int size){
        SearchRequest searchRequest=new SearchRequest();
        searchRequest.indices(index);
        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);
        Map<String,Object> result=new HashMap<>();
        List<T> 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;
    }


    /**
     * 使用scroll分页
     * @param index
     * @param searchSourceBuilder
     * @param targetClass
     * @param size
     * @param scrollId
     * @param <T>
     * @return  Map 当前页的数据   scrollId   page
     */
    public <T extends Object> Map<String,Object> page(String index,
                                SearchSourceBuilder searchSourceBuilder,
                                Class<T> 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<String,Object> 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 <T extends Object> Map<String,Object>
                      searchResponseToMap(SearchResponse searchResponse,int size,Class<T> targetClass){
        SearchHits hits= searchResponse.getHits(); //查询的结果

        double count=hits.getTotalHits().value; //获得总记录数
        int page=  (int)Math.ceil(count/size);  //算出总页数
        Map<String,Object> map=new HashMap<>();  //返回的结果
        List<T> 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

启动类

es层级标签搜索_spring boot_03

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

添加

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

        List<Company> 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<Company> 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<String,Object> map=  esUtil.page("company-index",
                      searchSourceBuilder,Company.class,2,"DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAFSu4WMlRKYnQzcW1RMDJBeVhnODF6ekxTQQ==");

        int page=(int)map.get("page");
        List<Company> companyList=(List<Company>)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层级标签搜索_spring boot_04

 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<SearchQueryParam.Attr> 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<QueryBuilder> 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<String, Object> page = esUtil.page("goods-index", searchSourceBuilder, SkuEsDTO.class, from, size);

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


        //这里开始方法 封装 ES代码到这里结束!!!!
        SearchSourceBuilder searchSourceBuilder1 = new SearchSourceBuilder();
        List<SkuEsDTO> select = esUtil.select("goods-index", SkuEsDTO.class, searchSourceBuilder1);
        System.out.println("select :" + select.size());
        QueryResult queryResult = new QueryResult();
        HashSet<Attr> 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;
    }

 

es层级标签搜索_spring boot_05

 swagger 测试

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

es层级标签搜索_spring boot_06

 展示成功!