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 + ",\nsource:" + 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 = "{\n" + " \"query\": {\n" + " \"match\": {\n" + " \"name\": {\n" + " \"query\": \"" + name + "\"\n" + " , \"analyzer\": \"ik_smart\"\n" + " }\n" + " }\n" + " },\n" + " \"highlight\": {\n" + " \"fields\": {\"name\": {}}\n" + " }\n" + "}"; 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;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM