使用Java High Level REST Client API编写的ElasticSearch工具类

最近需要部署elasticsearch(版本为6.4.0),存储一些日志信息。这里结合官网例子,使用提供的Java High Level REST Client API,自己编写了几个常见用例的工具类。废话不多说,下面是代码,不足之处请大家多多指教。

/**
 * elasticSearh 操作类
 * @author Stephen
 * @version 1.0
 * @date 2018/09/17 11:12:08
 */
public class ElasticClient implements Closeable {
    
    private static final String INDEX_KEY = "index";
    private static final String TYPE_KEY = "type";   
    private static final String INDEX = "spider";
    private static final String TYPE = "doc";
    private static final String TIMESTAMP = "timestamp";
    
    private static final Logger LOGGER = LoggerFactory.getLogger(ElasticClient.class);
    
    private String[] hosts;
    
    protected RestHighLevelClient client;
    
    public ElasticClient(String[] hosts) {
        this.hosts = hosts;
    }

    @Override
    public void close() throws IOException {
        if (Objects.nonNull(client)) {
            client.close();
        }
    }
    
    /**
     * 初始化配置
     */
    public void configure() {
        Validate.noNullElements(hosts, "Elastic连接地址不能为空!");
        HttpHost[] httpHosts = Arrays.stream(hosts).map(host -> {
            String[] hostParts = host.split(":");
            String hostName = hostParts[0];
            int port = Integer.parseInt(hostParts[1]);
            return new HttpHost(hostName, port, Const.SCHEMA);
        }).filter(Objects::nonNull).toArray(HttpHost[]::new);       
        client = new RestHighLevelClient(RestClient.builder(httpHosts));
    }
    
    /**
     * 创建索引(默认分片数为5和副本数为1)
     * @param indexName
     * @throws IOException
     */
    public void createIndex(String indexName) throws IOException {
        if (checkIndexExists(indexName)) {
            LOGGER.error("\"index={}\"索引已经存在!", indexName);
            return;
        }
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        request.mapping(TYPE, generateBuilder());        
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        // 指示是否所有节点都已确认请求
        boolean acknowledged = response.isAcknowledged();
        // 指示是否在超时之前为索引中的每个分片启动了必需的分片副本数
        boolean shardsAcknowledged = response.isShardsAcknowledged();
        if (acknowledged || shardsAcknowledged) {
            LOGGER.info("创建索引成功!索引名称为{}", indexName);
        }
    }
    
    /**
     * 创建索引(指定 index 和 type)
     * @param index
     * @param type
     * @throws IOException
     */
    public void createIndex(String index, String type) throws IOException {
        if (checkIndexExists(index)) {
            LOGGER.error("\"index={}\"索引已存在!", index);
            return;
        }
        CreateIndexRequest request = new CreateIndexRequest(index);
        request.mapping(type, generateBuilder());        
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        boolean acknowledged = response.isAcknowledged();
        boolean shardsAcknowledged = response.isShardsAcknowledged();
        if (acknowledged || shardsAcknowledged) {
            LOGGER.info("索引创建成功!索引名称为{}", index);
        }
    }
    
    /**
     * 创建索引(传入参数:分片数、副本数)
     * @param indexName
     * @param shards
     * @param replicas
     * @throws IOException
     */
    public void createIndex(String indexName, int shards, int replicas) throws IOException {
        if (checkIndexExists(indexName)) {
            LOGGER.error("\"index={}\"索引已存在!", indexName);
            return;
        }
        Builder builder = Settings.builder().put("index.number_of_shards", shards).put("index.number_of_replicas", replicas);
        CreateIndexRequest request = new CreateIndexRequest(indexName).settings(builder);
        request.mapping(TYPE, generateBuilder());
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        if (response.isAcknowledged() || response.isShardsAcknowledged()) {
            LOGGER.info("创建索引成功!索引名称为{}", indexName);
        }
    }
    
    /**
     * 删除索引
     * @param indexName
     * @throws IOException
     */
    public void deleteIndex(String indexName) throws IOException {
        try {
            DeleteIndexResponse response = client.indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT);
            if (response.isAcknowledged()) {
                LOGGER.info("{} 索引删除成功!", indexName);
            }
        } catch (ElasticsearchException ex) {
            if (ex.status() == RestStatus.NOT_FOUND) {
                LOGGER.error("{} 索引名不存在", indexName);
            }
            LOGGER.error("删除失败!");
        }
    }
    
    /**
     * 判断索引是否存在
     * @param indexName
     * @return
     * @throws IOException
     */
    public boolean checkIndexExists(String indexName) {
        GetIndexRequest request = new GetIndexRequest().indices(indexName);
        try {
            return client.indices().exists(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            LOGGER.error("判断索引是否存在,操作异常!");
        }
        return false;
    }
    
    /**
     * 开启索引
     * @param indexName
     * @throws IOException
     */
    public void openIndex(String indexName) throws IOException{
        if (!checkIndexExists(indexName)) {
            LOGGER.error("索引不存在!");
            return;
        }
        OpenIndexRequest request = new OpenIndexRequest(indexName);
        OpenIndexResponse response = client.indices().open(request, RequestOptions.DEFAULT);
        if (response.isAcknowledged() || response.isShardsAcknowledged()) {
            LOGGER.info("{} 索引开启成功!", indexName);
        }
    }
    
    /**
     * 关闭索引
     * @param indexName
     * @throws IOException
     */
    public void closeIndex(String indexName) throws IOException {
        if (!checkIndexExists(indexName)) {
            LOGGER.error("索引不存在!");
            return;
        }
        CloseIndexRequest request = new CloseIndexRequest(indexName); 
        CloseIndexResponse response = client.indices().close(request, RequestOptions.DEFAULT);
        if (response.isAcknowledged()) {
            LOGGER.info("{} 索引已关闭!", indexName);
        }
    }
    
    /**
     * 设置文档的静态映射(主要是为 message字段 设置ik分词器)
     * @param index
     * @param type
     * @throws IOException
     */
    public void setFieldsMapping(String index, String type) {
        PutMappingRequest request = new PutMappingRequest(index).type(type);
        try {
            request.source(generateBuilder());
            PutMappingResponse response = client.indices().putMapping(request, RequestOptions.DEFAULT);
            if (response.isAcknowledged()) {
                LOGGER.info("已成功对\"index={}, type={}\"的文档设置类型映射!", index, type);
            }
        } catch (IOException e) {
            LOGGER.error("\"index={}, type={}\"的文档设置类型映射失败,请检查参数!", index, type);
        }
    }
    private XContentBuilder generateBuilder() throws IOException {
        XContentBuilder builder = XContentFactory.jsonBuilder();        
        builder.startObject();
            builder.startObject("properties");           
                builder.startObject("message");               
                    builder.field("type", "text");
                    // 为message字段,设置分词器为 ik_smart(最粗粒度)
                    builder.field("analyzer", "ik_smart");                
                builder.endObject();
                builder.startObject(TIMESTAMP);               
                    builder.field("type", "date");
                    // 设置 日志时间的格式为  毫秒数的long类型
                    builder.field("format", "epoch_millis");                
                builder.endObject();
            builder.endObject();        
        builder.endObject();        
        return builder;
    }
   
    /**
     * 增加文档
     * @param indexName
     * @param typeName
     * @param id
     * @param jsonStr
     */
    public void addDocByJson(String indexName, String typeName, String id, String jsonString) throws IOException{
        if (!JsonValidator.validate(jsonString)) {
            LOGGER.error("非法的json字符串,操作失败!");
            return;
        }
        if (!checkIndexExists(indexName)) {
            createIndex(indexName, typeName);
        }       
        IndexRequest request = new IndexRequest(indexName, typeName, id).source(jsonString, XContentType.JSON);
        // request的opType默认是INDEX(传入相同id会覆盖原document,CREATE则会将旧的删除)
        // request.opType(DocWriteRequest.OpType.CREATE)
        IndexResponse response = null;
        try {
            response = client.index(request, RequestOptions.DEFAULT);
            
            String index = response.getIndex();
            String type = response.getType();
            String documentId = response.getId();
            if (response.getResult() == DocWriteResponse.Result.CREATED) {
                LOGGER.info("新增文档成功! index: {}, type: {}, id: {}", index , type, documentId);
            } else if (response.getResult() == DocWriteResponse.Result.UPDATED) {
                LOGGER.info("修改文档成功! index: {}, type: {}, id: {}", index , type, documentId);
            }
            // 分片处理信息
            ReplicationResponse.ShardInfo shardInfo = response.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
               LOGGER.error("文档未写入全部分片副本!"); 
            }
            // 如果有分片副本失败,可以获得失败原因信息
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                    String reason = failure.reason(); 
                    LOGGER.error("副本失败原因:{}", reason);
                }
            }
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.CONFLICT) {
                LOGGER.error("版本异常!");
            }
            LOGGER.error("文档新增失败!");
        }
    }
    
    /**
     * 查找文档
     * @param index
     * @param type
     * @param id
     * @return
     * @throws IOException
     */
    public Map getDocument(String index, String type, String id) throws IOException{
        Map resultMap = new HashMap<>();        
        GetRequest request = new GetRequest(index, type, id);
        // 实时(否)
        request.realtime(false);
        // 检索之前执行刷新(是)
        request.refresh(true);
        
        GetResponse response = null;
        try {
            response = client.get(request, RequestOptions.DEFAULT);
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.NOT_FOUND) {
                LOGGER.error("文档未找到,请检查参数!" );
            }
            if (e.status() == RestStatus.CONFLICT) {
                LOGGER.error("版本冲突!" );
            }
            LOGGER.error("查找失败!");
        }
        
        if(Objects.nonNull(response)) {
            if (response.isExists()) { // 文档存在      
                resultMap = response.getSourceAsMap();                               
            } else {
                // 处理未找到文档的方案。 请注意,虽然返回的响应具有404状态代码,但仍返回有效的GetResponse而不是抛出异常。
                // 此时此类响应不持有任何源文档,并且其isExists方法返回false。
                LOGGER.error("文档未找到,请检查参数!" );
            }
        }
        return resultMap;
    }
    
    /**
     * 删除文档
     * @param index
     * @param type
     * @param id
     * @throws IOException
     */
    public void deleteDocument(String index, String type, String id) throws IOException {
        DeleteRequest request = new DeleteRequest(index, type, id);
        DeleteResponse response = null;
        try {
            response = client.delete(request, RequestOptions.DEFAULT);
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.CONFLICT) {
                LOGGER.error("版本冲突!" );
            }
            LOGGER.error("删除失败!");
        }
        if (Objects.nonNull(response)) {
            if (response.getResult() == DocWriteResponse.Result.NOT_FOUND) {
                LOGGER.error("不存在该文档,请检查参数!");
            }
            LOGGER.info("文档已删除!");
            ReplicationResponse.ShardInfo shardInfo = response.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                LOGGER.error("部分分片副本未处理");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                    String reason = failure.reason();
                    LOGGER.error("失败原因:{}", reason);
                }
            }
        }
    }
    
    /**
     * 通过一个脚本语句(如:"ctx._source.posttime=\"2018-09-18\"")更新文档
     * @param index
     * @param type
     * @param id
     * @param script
     */
    public void updateDocByScript(String index, String type, String id, String script) throws IOException{
        Script inline = new Script(script);
        UpdateRequest request = new UpdateRequest(index, type, id).script(inline);
        try {
            UpdateResponse response  = client.update(request, RequestOptions.DEFAULT);            
            if (response.getResult() == DocWriteResponse.Result.UPDATED) {
                LOGGER.info("文档更新成功!");
            } else if (response.getResult() == DocWriteResponse.Result.DELETED) {
                LOGGER.error("\"index={},type={},id={}\"的文档已被删除,无法更新!", response.getIndex(), response.getType(), response.getId());
            } else if(response.getResult() == DocWriteResponse.Result.NOOP) {
                LOGGER.error("操作没有被执行!");
            }
            
            ReplicationResponse.ShardInfo shardInfo = response.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                LOGGER.error("部分分片副本未处理");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                    String reason = failure.reason();
                    LOGGER.error("未处理原因:{}", reason);
                }
            }
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.NOT_FOUND) {
                LOGGER.error("不存在这个文档,请检查参数!" );
            } else if (e.status() == RestStatus.CONFLICT) {
                LOGGER.error("版本冲突异常!" );
            }
            LOGGER.error("更新失败!");
        }
    }
    
    /**
     * 通过一个JSON字符串更新文档(如果该文档不存在,则根据参数创建这个文档)
     * @param index
     * @param type
     * @param id
     * @param jsonString
     * @throws IOException
     */
    public void updateDocByJson(String index, String type, String id, String jsonString) throws IOException {
        if (!JsonValidator.validate(jsonString)) {
            LOGGER.error("非法的json字符串,操作失败!");
            return;
        }
        if (!checkIndexExists(index)) {
            createIndex(index, type);
        }
        UpdateRequest request = new UpdateRequest(index, type, id);       
        request.doc(jsonString, XContentType.JSON);
        // 如果要更新的文档不存在,则根据传入的参数新建一个文档
        request.docAsUpsert(true);
        try {            
            UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
            String indexName = response.getIndex();
            String typeName = response.getType();
            String documentId = response.getId();
            if (response.getResult() == DocWriteResponse.Result.CREATED) {
                LOGGER.info("文档新增成功!index: {}, type: {}, id: {}", indexName, typeName, documentId);
            } else if (response.getResult() == DocWriteResponse.Result.UPDATED) {
                LOGGER.info("文档更新成功!");
            } else if (response.getResult() == DocWriteResponse.Result.DELETED) {
                LOGGER.error("\"index={},type={},id={}\"的文档已被删除,无法更新!", indexName, typeName, documentId);
            } else if (response.getResult() == DocWriteResponse.Result.NOOP) {
                LOGGER.error("操作没有被执行!");
            }
            
            ReplicationResponse.ShardInfo shardInfo = response.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                LOGGER.error("分片副本未全部处理");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                    String reason = failure.reason();
                    LOGGER.error("未处理原因:{}", reason);
                }
            }
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.NOT_FOUND) {
                LOGGER.error("不存在这个文档,请检查参数!" );
            } else if (e.status() == RestStatus.CONFLICT) {
                LOGGER.error("版本冲突异常!" );
            }
            LOGGER.error("更新失败!");
        }
    }
    
    /**
     * 批量增加文档
     * @param params
     * @throws IOException
     */
    public void bulkAdd(List> params) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (Map dataMap : params) {
            String index = dataMap.getOrDefault(INDEX_KEY, INDEX);
            String type = dataMap.getOrDefault(TYPE_KEY, TYPE);
            String id = dataMap.get("id");
            String jsonString = dataMap.get("json");
            if (StringUtils.isNotBlank(id) && JsonValidator.validate(jsonString)) {
                IndexRequest request = new IndexRequest(index, type, id).source(jsonString, XContentType.JSON);
                bulkRequest.add(request);
            }            
        }
        // 超时时间(2分钟)
        bulkRequest.timeout(TimeValue.timeValueMinutes(2L));
        // 刷新策略
        bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        
        if (bulkRequest.numberOfActions() == 0) {           
            LOGGER.error("参数错误,批量增加操作失败!");
            return;
        } 
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        // 全部操作成功
        if (!bulkResponse.hasFailures()) {
            LOGGER.info("批量增加操作成功!");
        } else {
            for (BulkItemResponse bulkItemResponse : bulkResponse) {
                if (bulkItemResponse.isFailed()) {
                    BulkItemResponse.Failure failure = bulkItemResponse.getFailure();                   
                    LOGGER.error("\"index={}, type={}, id={}\"的文档增加失败!", failure.getIndex(), failure.getType(), failure.getId());
                    LOGGER.error("增加失败详情: {}", failure.getMessage());
                } else {
                    LOGGER.info("\"index={}, type={}, id={}\"的文档增加成功!", bulkItemResponse.getIndex(), bulkItemResponse.getType(), bulkItemResponse.getId()); 
                }
            }
        }
    }
    
    /**
     * 批量更新文档
     * @param params
     * @throws IOException
     */
    public void bulkUpdate(List> params) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (Map dataMap : params) {
            String index = dataMap.getOrDefault(INDEX_KEY, INDEX);
            String type = dataMap.getOrDefault(TYPE_KEY, TYPE);
            String id = dataMap.get("id");
            String jsonString = dataMap.get("json");
            if (StringUtils.isNotBlank(id) && JsonValidator.validate(jsonString)) {
                UpdateRequest request = new UpdateRequest(index, type, id).doc(jsonString, XContentType.JSON);
                request.docAsUpsert(true);
                bulkRequest.add(request);
            }           
        }
        if (bulkRequest.numberOfActions() == 0) {           
            LOGGER.error("参数错误,批量更新操作失败!");
            return;
        }
        bulkRequest.timeout(TimeValue.timeValueMinutes(2L));
        bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);        
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        if (!bulkResponse.hasFailures()) {
            LOGGER.info("批量更新操作成功!");
        } else {
            for (BulkItemResponse bulkItemResponse : bulkResponse) {
                if (bulkItemResponse.isFailed()) {
                    BulkItemResponse.Failure failure = bulkItemResponse.getFailure();                   
                    LOGGER.error("\"index={}, type={}, id={}\"的文档更新失败!", failure.getIndex(), failure.getType(), failure.getId());
                    LOGGER.error("更新失败详情: {}", failure.getMessage());
                } else {
                    LOGGER.info("\"index={}, type={}, id={}\"的文档更新成功!", bulkItemResponse.getIndex(), bulkItemResponse.getType(), bulkItemResponse.getId());  
                }
            }
        }
    }
    
    /**
     * 批量删除文档
     * @param params
     * @throws IOException
     */
    public void bulkDelete(List> params) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (Map dataMap : params) {
            String index = dataMap.getOrDefault(INDEX_KEY, INDEX);
            String type = dataMap.getOrDefault(TYPE_KEY, TYPE);
            String id = dataMap.get("id");
            if (StringUtils.isNotBlank(id)){
                DeleteRequest request = new DeleteRequest(index, type, id);
                bulkRequest.add(request);
            }
        }
        if (bulkRequest.numberOfActions() == 0) {           
            LOGGER.error("操作失败,请检查参数!");
            return;
        }
        bulkRequest.timeout(TimeValue.timeValueMinutes(2L));
        bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        if (!bulkResponse.hasFailures()) {
            LOGGER.info("批量删除操作成功!");
        } else {
            for (BulkItemResponse bulkItemResponse : bulkResponse) {
                if (bulkItemResponse.isFailed()) {
                    BulkItemResponse.Failure failure = bulkItemResponse.getFailure();                   
                    LOGGER.error("\"index={}, type={}, id={}\"的文档删除失败!", failure.getIndex(), failure.getType(), failure.getId());
                    LOGGER.error("删除失败详情: {}", failure.getMessage());
                } else {
                    LOGGER.info("\"index={}, type={}, id={}\"的文档删除成功!", bulkItemResponse.getIndex(), bulkItemResponse.getType(), bulkItemResponse.getId()); 
                }
            }
        }
    }
    
    /**
     * 批量查找文档
     * @param params
     * @return
     * @throws IOException
     */
    public List> multiGet(List> params) throws IOException {
        List> resultList = new ArrayList<>();

        MultiGetRequest request = new MultiGetRequest();
        for (Map dataMap : params) {
            String index = dataMap.getOrDefault(INDEX_KEY, INDEX);
            String type = dataMap.getOrDefault(TYPE_KEY, TYPE);
            String id = dataMap.get("id");
            if (StringUtils.isNotBlank(id)) {
                request.add(new MultiGetRequest.Item(index, type, id));
            }
        }
        request.realtime(false);
        request.refresh(true);
        MultiGetResponse response = client.mget(request, RequestOptions.DEFAULT);
        List> list = parseMGetResponse(response);
        if (!list.isEmpty()) {
            resultList.addAll(list);
        }                      
        return resultList;
    }
    private List> parseMGetResponse(MultiGetResponse response) {       
        List> list = new ArrayList<>();
        MultiGetItemResponse[] responses = response.getResponses();
        for (MultiGetItemResponse item : responses) {
            GetResponse getResponse = item.getResponse();
            if (Objects.nonNull(getResponse)) {
                if (!getResponse.isExists()) {
                    LOGGER.error("\"index={}, type={}, id={}\"的文档查找失败,请检查参数!", getResponse.getIndex(), getResponse.getType(), getResponse.getId());
                } else {
                    list.add(getResponse.getSourceAsMap()); 
                }
            } else {
                MultiGetResponse.Failure failure = item.getFailure();
                ElasticsearchException e = (ElasticsearchException) failure.getFailure();
                if (e.status() == RestStatus.NOT_FOUND) {
                    LOGGER.error("\"index={}, type={}, id={}\"的文档不存在!", failure.getIndex(), failure.getType(), failure.getId());
                } else if (e.status() == RestStatus.CONFLICT) {
                    LOGGER.error("\"index={}, type={}, id={}\"的文档版本冲突!", failure.getIndex(), failure.getType(), failure.getId());
                }
            }                                                                                                        
        }
        return list;
    }
    
    /**
     * 根据条件搜索日志内容(参数level和messageKey不能同时为空)
     * @param level 日志级别,可以为空
     * @param messageKey 日志信息关键字,可以为空
     * @param startTime 日志起始时间,可以为空
     * @param endTime 日志结束时间,可以为空
     * @param size 返回记录数,可以为空,默认最大返回10条。该值必须小于10000,如果超过10000请使用  {@link #queryAllByConditions}
     * @return
     * @throws IOException
     */
    public List> queryByConditions(String level, String messageKey, Long startTime, Long endTime, Integer size) throws IOException {
        List> resultList = new ArrayList<>();        
        if (StringUtils.isBlank(level) && StringUtils.isBlank(messageKey)) {
            LOGGER.error("参数level(日志级别)和messageKey(日志信息关键字)不能同时为空!");
            return resultList;
        }
        
        QueryBuilder query = generateQuery(level, messageKey, startTime, endTime);
        FieldSortBuilder order = SortBuilders.fieldSort(TIMESTAMP).order(SortOrder.DESC);        
        SearchSourceBuilder searchBuilder = new SearchSourceBuilder();
        searchBuilder.timeout(TimeValue.timeValueMinutes(2L));
        searchBuilder.query(query);
        searchBuilder.sort(order);        
        if (Objects.nonNull(size)) {
            searchBuilder.size(size);
        }
        
        SearchRequest request = new SearchRequest(INDEX).types(TYPE);
        request.source(searchBuilder);       
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        int failedShards = response.getFailedShards();
        if (failedShards > 0) {
            LOGGER.error("部分分片副本处理失败!");
            for (ShardSearchFailure failure : response.getShardFailures()) {
                String reason = failure.reason();
                LOGGER.error("分片处理失败原因:{}", reason);
            }
        }
        List> list = parseSearchResponse(response);
        if (!list.isEmpty()) {
            resultList.addAll(list);
        }
        return resultList;
    }
    private QueryBuilder generateQuery(String level, String messageKey, Long startTime, Long endTime) {
        // term query(检索level)
        TermQueryBuilder levelQuery = null;
        if (StringUtils.isNotBlank(level)) {
            levelQuery = QueryBuilders.termQuery("level", level.toLowerCase());
        }
        // match query(检索message)
        MatchQueryBuilder messageQuery = null;
        if (StringUtils.isNotBlank(messageKey)) {
            messageQuery = QueryBuilders.matchQuery("message", messageKey);
        }
        // range query(检索timestamp)
        RangeQueryBuilder timeQuery = QueryBuilders.rangeQuery(TIMESTAMP);
        timeQuery.format("epoch_millis");
        if (Objects.isNull(startTime)) {
            if (Objects.isNull(endTime)) {
                timeQuery = null;
            } else {
                timeQuery.lte(endTime);
            }
        } else {
            if (Objects.isNull(endTime)) {
                timeQuery.gte(startTime);
            } else {
                timeQuery.gte(startTime).lte(endTime);
            }
        }
        // 将上述三个query组合
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if (Objects.nonNull(levelQuery)) {
            boolQuery.must(levelQuery);
        }
        if (Objects.nonNull(messageQuery)) {
            boolQuery.must(messageQuery);
        }
        if (Objects.nonNull(timeQuery)) {
            boolQuery.must(timeQuery);
        }
        return boolQuery;
    }
    private List> parseSearchResponse(SearchResponse response){
        List> resultList = new ArrayList<>();
        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit hit : hits) {
            resultList.add(hit.getSourceAsMap());
        }
        return resultList;
    }
    
    /**
     * 根据条件,搜索全部符合的记录(参数level和messageKey不能同时为空)
     * @param level 日志级别,可以为空
     * @param messageKey 日志信息关键字,可以为空
     * @param startTime 日志起始时间,可以为空
     * @param endTime 日志结束时间,可以为空
     * @return
     */
    public List> queryAllByConditions(String level, String messageKey, Long startTime, Long endTime) throws IOException {
        List> resultList = new ArrayList<>();        
        if (StringUtils.isBlank(level) && StringUtils.isBlank(messageKey)) {
            LOGGER.error("参数level(日志级别)和messageKey(日志信息关键字)不能同时为空!");
            return resultList;
        }
        
        QueryBuilder query = generateQuery(level, messageKey, startTime, endTime);
        FieldSortBuilder order = SortBuilders.fieldSort(TIMESTAMP).order(SortOrder.DESC);
        SearchSourceBuilder searchBuilder = new SearchSourceBuilder();
        searchBuilder.query(query).sort(order);
        searchBuilder.size(500);
        
        // 初始化 scroll 上下文
        SearchRequest request = new SearchRequest(INDEX).types(TYPE);
        final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
        request.source(searchBuilder).scroll(scroll);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT); 
        String scrollId = response.getScrollId();
        SearchHit[] searchHits = response.getHits().getHits();
        // 把第一次scroll的数据添加到结果List中
        for (SearchHit searchHit : searchHits) {
            resultList.add(searchHit.getSourceAsMap());
        }
        // 通过传递scrollId循环取出所有相关文档
        while (searchHits != null && searchHits.length > 0) { 
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); 
            scrollRequest.scroll(scroll);
            response = client.scroll(scrollRequest, RequestOptions.DEFAULT);
            scrollId = response.getScrollId();
            searchHits = response.getHits().getHits();
            // 循环添加剩下的数据
            for (SearchHit searchHit : searchHits) {
                resultList.add(searchHit.getSourceAsMap());
            }
        }
        // 清理 scroll 上下文
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); 
        clearScrollRequest.addScrollId(scrollId);
        client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
        return resultList;
    }
    
    /**
     * 根据条件做分页查询(参数level和messageKey不能同时为空)
     * @param level 日志级别,可以为空
     * @param messageKey 日志信息关键字,可以为空
     * @param startTime 日志起始时间,可以为空
     * @param endTime 日志结束时间,可以为空
     * @param pageNum 当前页码,可以为空(默认设为1)
     * @param pageSize 页记录数,可以为空(默认设为10)
     * @return
     * @throws IOException
     */
    public Page> queryPageByConditions(String level, String messageKey, Long startTime, Long endTime, Integer pageNum, Integer pageSize) throws IOException {
        if (StringUtils.isBlank(level) && StringUtils.isBlank(messageKey)) {
            LOGGER.error("参数level(日志级别)、messageKey(日志信息关键字)不能同时为空!");
            return null;
        }
        
        if (Objects.isNull(pageNum)) {
            pageNum = 1;
        }
        if (Objects.isNull(pageSize)) {
            pageSize = 10;
        }       
        QueryBuilder query = generateQuery(level, messageKey, startTime, endTime);
        FieldSortBuilder order = SortBuilders.fieldSort(TIMESTAMP).order(SortOrder.DESC);        
        SearchSourceBuilder searchBuilder = new SearchSourceBuilder();
        searchBuilder.timeout(TimeValue.timeValueMinutes(2L));        
        searchBuilder.query(query);
        searchBuilder.sort(order);
        searchBuilder.from(pageNum - 1).size(pageSize);
        
        SearchRequest request = new SearchRequest(INDEX).types(TYPE);
        request.source(searchBuilder);       
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        int totalRecord = (int) hits.getTotalHits();
        List> results = new ArrayList<>();
        for (SearchHit hit : hits.getHits()) {
            results.add(hit.getSourceAsMap());
        }
        
        Page> page = new Page<>();
        page.setPageNum(pageNum);
        page.setPageSize(pageSize);
        page.setTotalRecord(totalRecord);
        page.setResults(results);
        return page;
    }
}

 

你可能感兴趣的:(ElasticSearch)