• springboot2.0 集成elasticsearch,实现检索、分页、排序


    springboot整合es的方式:

    • transport方式(7.0弃用,8.0移除)
    • spring-data(完全当做数据库来用,无法全部支持es,内部也是基于transport,包装后使用非常简单,和JPA基本类似)
    • rest(low-level和high-level,low-level非常完善,支持所有版本,需要自己组装request和解析response,high-level是对low-level的包装,必须跟着大版本走)
    • 根据官方的建议,我们一般采用high-level的方式来对ES操作,high-level方式无法实现的用low-level实现。
    • 本文用low-level方式实现获取所有索引列表GET /_cat/indices?v,这个在high-level里没有封装,基本的检索、分页、排序用high-level实现。

    一、依赖

            <dependency>
                <groupId>org.elasticsearch.client</groupId>
                <artifactId>elasticsearch-rest-high-level-client</artifactId>
                <version>6.5.1</version>
            </dependency>
    
            <!--如果不指定,springboot会自动引入5.x版本,所以需要强行引入高版本,
                elasticsearch-rest-client版本是同步,不需要强行指定,如果发现不同步也可以指定-->
            <dependency>
                <groupId>org.elasticsearch</groupId>
                <artifactId>elasticsearch</artifactId>
                <version>6.5.1</version>
            </dependency>

    引入库情况:

     

    二、application.yml

    可以配置多个数据节点
    elasticsearch: ip:
    192.168.31.10:9200,192.168.1.101:9200

    三、config

    @Configuration
    public class ESConfig {
        private Logger logger = LoggerFactory.getLogger(ESConfig.class);
        private static final int ADDRESS_LENGTH = 2;
        private static final String HTTP_SCHEME = "http";
    
        /**
         * 使用冒号隔开ip和端口
         */
        @Value("${elasticsearch.ip}")
        String[] ipAddress;
    
        @Bean
        public RestClientBuilder restClientBuilder() {
            HttpHost[] hosts = Arrays.stream(ipAddress)
                    .map(this::makeHttpHost)
                    .filter(Objects::nonNull)
                    .toArray(HttpHost[]::new);
            logger.info("ES hosts:{}", Arrays.toString(hosts));
            return RestClient.builder(hosts);
        }
    
    //low-level @Bean
    public RestClient restClient(){ return restClientBuilder().build(); }
      //high-level @Bean(name
    = "highLevelClient") public RestHighLevelClient highLevelClient(@Autowired RestClientBuilder restClientBuilder) { restClientBuilder.setMaxRetryTimeoutMillis(60000); return new RestHighLevelClient(restClientBuilder); } private HttpHost makeHttpHost(String s) { String[] address = s.split(":"); if (address.length == ADDRESS_LENGTH) { String ip = address[0]; int port = Integer.parseInt(address[1]); return new HttpHost(ip, port, HTTP_SCHEME); } else { return null; } } }

    四、low-level实例

    @Service
    public class ESIndexServiceImpl implements ESIndexService {
    
        @Resource
        RestClient restClient;
    
        @Override
        public List<ESIndexObject> getAllESIndex() {
            return getESIndexByName("");
        }
    
        private <T> List<T> getDataByQuery(String method,String query)
        {
            Request request = new Request(method.toUpperCase(),query);
            try {
                Response response = restClient.performRequest(request);
                RequestLine requestLine = response.getRequestLine();
                HttpHost host = response.getHost();
                int statusCode = response.getStatusLine().getStatusCode();
                Header[] headers = response.getHeaders();
                System.out.println(requestLine);
                System.out.println(host);
                System.out.println(statusCode);
                System.out.println(headers);
                String responseBody = EntityUtils.toString(response.getEntity());
                ObjectMapper mapper = new ObjectMapper();
    
                List<T> list = mapper.readValue(responseBody,new TypeReference<List<T>>(){});
    
                return list;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
      
    //获取指定或者所有索引
    //cat api不支持分页,所以数据返回后在client分页,做在前端,数据量也不大 @Override
    public List<ESIndexObject> getESIndexByName(String index) { if(index==null) index = ""; String strQuery = "/_cat/indices/*"+index+"*?v&h=uuid,health,status,index,docsCount,storeSize,cds&s=cds:desc&format=json"; List<ESIndexObject> list = getDataByQuery("GET",strQuery); return list; } }

    实体:

    //实体和response返回字段一致,可以用反序列化直接生成对象
    public class ESIndexObject implements Serializable {
        private String uuid;
        private String index;
        private String health;
        private String status;
        private int docsCount;
        private String storeSize;
        private String cds; //creation.date.string
    
        。。。省略getter,setter
    }

    五、high-level方式

    数据:

    PUT customer
    {
      "settings": {
        "number_of_replicas": 0,
        "number_of_shards": 1,
        "index":{
          "analysis.analyzer.default.type":"ik_max_word",
          "analysis.search_analyzer.default.type":"ik_smart"
        }
      },
      "mappings": {
        "doc":{
          "properties":{
            
          }
        }
      }
    }
    类似这样,多弄几个数据
    POST /customer/doc/510228192101063619
    {
      "name":"燕子李三",
      "id":"510228192101063619",
      "addr":"天津市前卫营850号",
      "tel":"13700102347"
    }

    实体:

    public class ESCustomer implements Serializable {
        private long id;
        private String name;
        private String addr;
        private String tel;
    
        ...省略getter,setter
    }

    实现检索,支持排序和分页:

    @Service
    public class ESCustomerServiceImpl implements ESCustomerService {
        @Resource
        RestClient restClient;
    
        @Resource(name = "highLevelClient")
        RestHighLevelClient restHighLevelClient;
    
        private ObjectMapper mapper = new ObjectMapper();
    
        private String indexName = "customer";
    
        //包装SearchResponse返回数据到List
        private <T> List<T> wrapperData(SearchResponse response) throws Exception {
            SearchHits hits = response.getHits();
            long totalHits = hits.getTotalHits();
            System.out.println("Customer search totalHits:" + totalHits);
            List<T> list = new ArrayList<>();
            SearchHit[] searchHits = hits.getHits();
            //SearchHits实现了Iterable接口,可以直接进行迭代
            //根据测试这里可以用hits变量替换searchHits变量
            for (SearchHit hit : searchHits) {
                String index = hit.getIndex(); //获取文档的index
                String type = hit.getType(); //获取文档的type
                String id = hit.getId(); //获取文档的id
                Map<String, Object> sourceMap = hit.getSourceAsMap(); //获取文档内容,封装为map
                System.out.println("index:" + index + ",type:" + type + ",id:" + id + ",
    source:" + sourceMap);
                String sourceString = hit.getSourceAsString(); //获取文档内容,转换为json字符串。
                T object = mapper.readValue(sourceString, new TypeReference<T>() {
                });
                list.add(object);
            }
            return list;
        }
    
        //包装SearchSourceBuilder,用pageable完成分页和排序的设置
        //排序字段必须建立keyword字段
        private SearchSourceBuilder wrapperBuilder(SearchSourceBuilder builder, Pageable pageable) {
            builder.from(pageable.getPageNumber() * pageable.getPageSize());
            builder.size(pageable.getPageSize());
            Sort sort = pageable.getSort();
            Iterator iterator = sort.iterator();
            while (iterator.hasNext()) {
                Sort.Order order = (Sort.Order) iterator.next();
                //用keyword字段来排序,所以在建立索引的时候,就必须同步建立keyword字段
                builder.sort(order.getProperty() + ".keyword", order.getDirection() == Sort.Direction.ASC ? SortOrder.ASC : SortOrder.DESC);
            }
            return builder;
        }
    
        @Override
        public Page<ESCustomer> searchAllInPage(Pageable pageable) {
           return searchAllByAllField("",pageable);
        }
    
        //后期代码需重构,通过反射获取字段,然后构建数组来实现
      //query有值就按按值做全字段全文检索,对于类似身份证和电话等字段采用通配wildcard检索方式,query无值则返回所有数据
    //按Pageable分页和排序,暂时只能排序一个字段  
    @Override public Page<ESCustomer> searchAllByAllField(String query, Pageable pageable) { SearchRequest searchRequest = new SearchRequest(this.indexName); SearchSourceBuilder builder = wrapperBuilder(new SearchSourceBuilder(),pageable); if(query==null||query=="") { builder.query(QueryBuilders.matchAllQuery()); }else { BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery() .should(QueryBuilders.matchQuery("name",query)) .should(QueryBuilders.matchQuery("addr",query)) .should(QueryBuilders.wildcardQuery("tel","*"+query+"*")) .should(QueryBuilders.wildcardQuery("id","*"+query+"*")); builder.query(boolQueryBuilder); } searchRequest.source(builder); try { SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT); List<ESCustomer> esCustomerList = wrapperData(searchResponse); Page<ESCustomer> esCustomerPage = new PageImpl<>(esCustomerList, pageable, searchResponse.getHits().getTotalHits()); return esCustomerPage; }catch (Exception e) { e.printStackTrace(); return null; } } @Override public List<ESCustomer> searchAll() { SearchRequest searchRequest = new SearchRequest(indexName); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.query(QueryBuilders.matchAllQuery()); searchRequest.source(searchSourceBuilder); try { SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); List<ESCustomer> esCustomerList = wrapperData(searchResponse); return esCustomerList; } catch (Exception e) { e.printStackTrace(); return null; } } // 使用low-level方式实现检索 // 需要自己实现返回的json数据封装,演示,暂时未实现 @Deprecated public List<ESCustomer> searchByNameByRaw(String name) { String query = "{ " + " "query": { " + " "match": { " + " "name": { " + " "query": "" + name + "" " + " , "analyzer": "ik_smart" " + " } " + " } " + " }, " + " "highlight": { " + " "fields": {"name": {}} " + " } " + "}"; Request request = new Request("GET", "/user*/_search"); HttpEntity httpEntity = new NStringEntity(query, ContentType.APPLICATION_JSON); request.setEntity(httpEntity); try { Response response = restClient.performRequest(request); String responseBody = EntityUtils.toString(response.getEntity()); System.out.println(response); System.out.println(responseBody); JSONObject jsonObject = JSON.parseObject(responseBody); Object hits = jsonObject.get("hits"); System.out.println(hits); ObjectMapper mapper = new ObjectMapper(); Map mapResponseBody = mapper.readValue(responseBody, Map.class); Object source = mapResponseBody.get("hits"); System.out.println(source); // List<ESCustomer> list = mapper.readValue(responseBody,new TypeReference<List<ESCustomer>>(){}); // return list; return null; } catch (Exception e) { e.printStackTrace(); return null; } } }

     Controller:

    @Controller
    @RequestMapping("/esCustomer")
    public class ESCustomerController {
        @Resource
        ESCustomerService esCustomerService;
    
        @RequestMapping("/list")
        public String list() {
            return "/ESCustomer/customerList";
        }
    
        @RequestMapping("/getList")
        @ResponseBody
        public Object getESCustomerList(HttpServletRequest request) {
    
            Pageable pageable = ControllerUtils.getPageInfo(request);
            String searchText = ControllerUtils.getSearchText(request);
            Page<ESCustomer> page = esCustomerService.searchAllByAllField(searchText,pageable);
    
            Map<String, Object> map = new HashMap<>();
            map.put("total", page.getTotalElements());
            map.put("rows", page.getContent());
    
            return map;
        }
    }
  • 相关阅读:
    linux系统swap分区容量扩展
    linux系统lv_root分区容量扩展
    linux系统创建新LV,挂载新分区。
    linux 服务器重启后lvm 变成inactive状态解决
    Linux下使用fdisk扩大分区容量
    go语言 调用飞书群消息机器人接口
    SpringCloud Sentinel 学习笔记
    Git 笔记整理
    SpringBoot 整合 RabbitMQ 学习笔记
    js递归生成树形结构-vue
  • 原文地址:https://www.cnblogs.com/asker009/p/10179544.html
Copyright © 2020-2023  润新知