package com.ruoyi.framework.config;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.constant.DefineConstant;
import com.ruoyi.common.enums.AggsType;
import com.ruoyi.framework.config.properties.ElasticSearchConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.CountRequest;
import org.elasticsearch.client.core.CountResponse;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedAvg;
import org.elasticsearch.search.aggregations.metrics.ParsedMax;
import org.elasticsearch.search.aggregations.metrics.ParsedMin;
import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.ValueCount;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * ES客户端
 */
@Component
@Slf4j
public class ElasticSearchClient {

    @Autowired
    ElasticSearchConfig elasticSearchConfig;

    public static RestHighLevelClient client;

    private static final String keyword = ".keyword";

    /**
     * 设置创建索引的相关参数
     */
    @PostConstruct
    public void init() {
        try {
            if (client != null) {
                client.close();
            }
            //创建
            HttpHost httpHost = new HttpHost(elasticSearchConfig.getIp(),
                    elasticSearchConfig.getPort(), elasticSearchConfig.getType());
            client = new RestHighLevelClient(RestClient.builder(httpHost));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 如果注释以下代码，默认访问127.0.0.1:9200 或者添加下面代码，访问的是yml文件配置的IP，端口，类型
     *
     * @return
     */
    @Bean
    public RestHighLevelClient client() {
        return new RestHighLevelClient(RestClient.builder(new HttpHost(elasticSearchConfig.getIp(),
                elasticSearchConfig.getPort(), elasticSearchConfig.getType())));
    }

    /**
     * 创建索引
     *
     * @param index 索引名
     * @return
     */
    public boolean createIndex(String index) throws IOException {
        if (isIndexExist(index)) {
            log.error("Index is  exits!");
            return false;
        }
        //1.创建索引请求
        CreateIndexRequest request = new CreateIndexRequest(index);
        //2.执行客户端请求
        CreateIndexResponse response = client.indices()
                .create(request, RequestOptions.DEFAULT);
        return response.isAcknowledged();
    }

    /**
     * 删除索引
     *
     * @param index
     * @return
     */
    public boolean deleteIndex(String index) throws IOException {
        if (!isIndexExist(index)) {
            log.error("Index is not exits!");
            return false;
        }
        DeleteIndexRequest request = new DeleteIndexRequest(index);
        AcknowledgedResponse delete = client.indices()
                .delete(request, RequestOptions.DEFAULT);
        return delete.isAcknowledged();
    }

    /**
     * 新增单条文档数据
     *
     * @param indexName 索引名称
     * @param id
     * @throws Exception
     */
    public void createDocument(String indexName, String id, Map<String, Object> paramMap) throws Exception {
        // 指定单条文档数据，最终会转化成Json格式
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        {
            if (!CollectionUtils.isEmpty(paramMap)) {
                paramMap.forEach((key, value) -> {
                    try {
                        builder.field(key, value);
                    } catch (IOException ignored) {
                    }
                });
            }
        }
        builder.endObject();
        // 创建新增文档数据的请求
        IndexRequest indexRequest = new IndexRequest(indexName).id(id).source(builder);
        // 手动指定路由的key，文档查询时可提高性能
        indexRequest.routing("route_" + indexName);
        // 等待主分片保存的超时时长
        indexRequest.timeout(TimeValue.timeValueSeconds(elasticSearchConfig.getMasterShardTimeOut()));
        // 刷新策略，WAIT_UNTIL设置则表示刷新使此请求的内容对搜索可见为止
        indexRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        // 操作类型为新增
        indexRequest.opType(DocWriteRequest.OpType.CREATE);

        // 异步执行新增文档数据请求
        client.indexAsync(indexRequest, RequestOptions.DEFAULT, new ActionListener<IndexResponse>() {
            @Override
            public void onResponse(IndexResponse indexResponse) {
                log.info("新增单条文档数据，结果：{}", indexResponse.toString());
            }

            @Override
            public void onFailure(Exception e) {
                e.printStackTrace();
            }
        });
    }


    /**
     * 更新文档内容
     *
     * @param indexName 索引名称
     * @param id        id
     * @param paramMap  paramMap
     */
    public void updateDocument(String indexName, String id, Map<String, Object> paramMap) {

        // 创建更新文档请求并设置参数
        UpdateRequest updateRequest = new UpdateRequest(indexName, id);
        updateRequest.doc(paramMap);
        // 主分片执行更新的超时时长
        updateRequest.timeout(TimeValue.timeValueSeconds(elasticSearchConfig.getMasterShardTimeOut()));
        // 刷新策略
        updateRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        // 重试更新操作多少次
        updateRequest.retryOnConflict(3);

        // 异步执行更新文档的请求
        client.updateAsync(updateRequest, RequestOptions.DEFAULT, new ActionListener<UpdateResponse>() {
            @Override
            public void onResponse(UpdateResponse updateResponse) {
                log.info("更新文档内容,结果：{}", updateResponse.toString());
            }

            @Override
            public void onFailure(Exception e) {
                e.printStackTrace();
            }
        });
    }

    /**
     * 删除文档数据
     *
     * @param indexName 索引名称
     * @param id        id
     */
    public void deleteDocument(String indexName, String id) {
        DeleteRequest deleteRequest = new DeleteRequest(indexName, id);
        // 主分片执行删除的超时时长
        deleteRequest.timeout(TimeValue.timeValueMinutes(2));
        // 刷新策略
        deleteRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        // 异步执行删除
        client.deleteAsync(deleteRequest, RequestOptions.DEFAULT, new ActionListener<DeleteResponse>() {
            @Override
            public void onResponse(DeleteResponse deleteResponse) {
                log.info("删除成功:" + deleteResponse.toString());
            }

            @Override
            public void onFailure(Exception e) {
                log.info("删除失败:" + e.getMessage());
            }
        });
    }


    /**
     * 文档数据批量插入
     *
     * @param mapList   行数据
     * @param indexName 索引
     * @param idName    主键名称
     * @return
     * @throws Exception
     */
    public void importDocument(List<Map<String, Object>> mapList, String indexName, String idName) {
        IndexRequest indexRequest;
        BulkRequest bulkRequest = new BulkRequest();
        try {
            for (Map<String, Object> mp : mapList) {
                indexRequest = new IndexRequest(indexName).id(mp.get(idName).toString()).source(mp);
                bulkRequest.add(indexRequest);
            }
            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            log.info("批量插入结果：{}", bulkResponse.toString());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 根据经纬度查询范围查找location 经纬度字段，distance 距离中心范围KM，lat  lon 圆心经纬度
     *
     * @param index
     * @param longitude
     * @param latitude
     * @param distance
     * @return
     */
    public List<Map<String, Object>> geoDistanceQuery(String index, Float longitude, Float latitude, String distance) throws IOException {
        if (longitude == null || latitude == null) {
            return null;
        }
        //拼接条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        QueryBuilder isdeleteBuilder = QueryBuilders.termQuery("isdelete", false);
        // 以某点为中心，搜索指定范围
        GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("location");
        distanceQueryBuilder.point(latitude, longitude);
        //查询单位：km
        distanceQueryBuilder.distance(distance, DistanceUnit.KILOMETERS);
        boolQueryBuilder.filter(distanceQueryBuilder);
//        boolQueryBuilder.must(isdeleteBuilder);

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(boolQueryBuilder);

        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.source(searchSourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        // 返回结果
        SearchHits searchHitArray = searchResponse.getHits();
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (SearchHit searchHit : searchHitArray.getHits()) {
            Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
            mapList.add(sourceAsMap);
        }
        return mapList;
    }

    /**
     * 通过id查询数据
     *
     * @param indexName
     * @param id
     * @param fields    需要显示的字段，逗号分隔（缺省为全部字段）
     * @return
     */
    public Map<String, Object> getDocById(String indexName, String id, String fields) {
        Map<String, Object> getDocByIdMap = new HashMap<>();
        GetRequest getRequest = new GetRequest(indexName, id);
        if (StrUtil.isNotBlank(fields)) {
            //只查询特定字段。如果需要查询所有字段则不设置该项。
            getRequest.fetchSourceContext(new FetchSourceContext(true, fields.split(","), Strings.EMPTY_ARRAY));
        }
        GetResponse getResponse;
        try {
            getResponse = client.get(getRequest, RequestOptions.DEFAULT);
            if (getResponse.isExists()) {
                getDocByIdMap.put("msg", "操作成功！");
                getDocByIdMap.put("data", getResponse.getSource());
                getDocByIdMap.put("code", 200);
            } else {
                getDocByIdMap.put("msg", "数据不存在");
                getDocByIdMap.put("code", -1);
                getDocByIdMap.put("data", null);
            }
            return getDocByIdMap;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @param indexName       (索引名)也可以类似的说成表名
     * @param equalsCondition 关键字等值条件
     *                        若是一个字符串以%结尾，则匹配以去掉%的字符串开头的记录
     *                        若是一个字符串以*开头或结尾，则模糊匹配去掉*的记录  类似于sql中的like '%str%'
     *                        若传入的是一个普通的字符串，则等值查询
     *                        若传入的是一个集合，则使用的是in条件查询
     * @param rangeCondition  条件范围查询
     *                        字段，字段对应值的区间，区间格式[,]/(,)/[,)/(,]，逗号的左右可以没值
     * @param orderBy         排序字段
     *                        若是字段以中划线-开头，则使用降序排序，类似于sql中的desc
     *                        若正常字段排序，则使用增序排序，类似于sql中的asc
     * @param pageNum         页数
     * @param pageSize        每页大小
     * @return
     * @Description ElasticSearch条件查询
     */
    public Map<String, Object> searchDocument(Map<String, Object> equalsCondition,
                                              Map<String, Object> rangeCondition,
                                              List<String> orderBy,
                                              Integer pageNum,
                                              Integer pageSize,
                                              String... indexName) {
        Map<String, Object> resultMap = new HashMap<>(8);
        List<Map<String, Object>> queryResult = new ArrayList<>();
        long totalNum = 0;
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // and 等值查询
        // 某一field=具体的值； 也可以某一field 的值 in 具体定义集合里的值
        if (null != equalsCondition && !equalsCondition.isEmpty()) {
            for (Map.Entry<String, Object> entry : equalsCondition.entrySet()) {
                String key = entry.getKey();
                //由于我创建索引的时候使用字符串不分词使用的.keyword类型
                if (key.endsWith("_s")) {
                    queryValueBuild(boolQueryBuilder, key + ".keyword", entry.getValue());
                } else {
                    queryValueBuild(boolQueryBuilder, key, entry.getValue());
                }
            }
        }
        //范围查询
        if (null != rangeCondition && !rangeCondition.isEmpty()) {
            rangeValueBuild(boolQueryBuilder, rangeCondition);
        }
        sourceBuilder.query(boolQueryBuilder);
        //排序
        if (null != orderBy && !orderBy.isEmpty()) {
            buildSort(sourceBuilder, orderBy);
        }
        //分页(es分页查询默认是查询返回10条记录，而深度分页，默认是10000条数据,也就是一次性最多返回10000条,设置size就可以实现，但是如果实际数据量特别大，可以使用scroll游标查询，此处主要常规分页查询)
        if (ObjectUtil.isNotEmpty(pageNum)) {
            pageSize = pageSize == null ? 10 : pageSize;
            if (pageNum > 0) {
                sourceBuilder.from(pageSize * (pageNum - 1));
            } else {
                sourceBuilder.from(0);
            }
            sourceBuilder.size(pageSize);
        }

        //执行查询
        SearchResponse response = executeSearch(sourceBuilder, indexName);
        SearchHits searchHits = response.getHits();
        SearchHit[] hits = searchHits.getHits();
        totalNum = searchHits.getTotalHits().value;
        for (SearchHit hit : hits) {
            Map<String, Object> sourceMap = hit.getSourceAsMap();
            sourceMap.put("id_s", hit.getId());
            queryResult.add(sourceMap);
        }
        resultMap.put("pageList", queryResult);
        resultMap.put("totalNum", totalNum);
        resultMap.put("pageNum", pageNum);
        resultMap.put("pageSize", pageSize);
        return resultMap;
    }

    /**
     * 聚合函数查询
     *
     * @param indexName       索引名
     * @param aggsType
     * @param bucketName      分组字段
     * @param metricName      需要统计分析的字段
     * @param equalsCondition
     * @param rangeCondition
     * @return
     */
    public Map<Object, Object> aggs(String indexName,
                                    AggsType aggsType,
                                    String bucketName,
                                    String metricName,
                                    Map<String, Object> equalsCondition,
                                    Map<String, Object> rangeCondition) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        String by = "by_" + bucketName.replaceAll(keyword, "");
        String me = aggsType.toString() + "_" + metricName.replaceAll(keyword, "");
        TermsAggregationBuilder aggregation = AggregationBuilders.terms(by).field(bucketName);
        //默认按照聚合结果降序排序
        aggregation.order(BucketOrder.aggregation(me, false));
        if (AggsType.count == aggsType) {
            aggregation.subAggregation(AggregationBuilders.count(me).field(metricName)).size(10000);
        } else if (AggsType.min == aggsType) {
            aggregation.subAggregation(AggregationBuilders.min(me).field(metricName)).size(10000);
        } else if (AggsType.max == aggsType) {
            aggregation.subAggregation(AggregationBuilders.max(me).field(metricName)).size(10000);
        } else if (AggsType.sum == aggsType) {
            aggregation.subAggregation(AggregationBuilders.sum(me).field(metricName)).size(10000);
        } else if (AggsType.avg == aggsType) {
            aggregation.subAggregation(AggregationBuilders.avg(me).field(metricName)).size(10000);
        }
        sourceBuilder.aggregation(aggregation);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 某一field=具体的值； 也可以某一field 的值 in 具体定义集合里的值
        if (null != equalsCondition && !equalsCondition.isEmpty()) {
            for (Map.Entry<String, Object> entry : equalsCondition.entrySet()) {
                String key = entry.getKey();
                //由于我创建索引的时候使用字符串不分词使用的.keyword类型
                if (key.endsWith("_s")) {
                    queryValueBuild(boolQueryBuilder, key + ".keyword", entry.getValue());
                } else {
                    queryValueBuild(boolQueryBuilder, key, entry.getValue());
                }
            }
        }
        //范围查询
        if (null != rangeCondition && !rangeCondition.isEmpty()) {
            rangeValueBuild(boolQueryBuilder, rangeCondition);
        }
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.size(0);

        // 执行查询
        SearchResponse response = executeSearch(sourceBuilder, indexName);
        // 解析结果
        Aggregations aggregations = response.getAggregations();
        Terms terms = aggregations.get(by);
        Map<Object, Object> map = new LinkedHashMap<>();
        for (Terms.Bucket bucket : terms.getBuckets()) {
            if (AggsType.count == aggsType) {
                ValueCount count = bucket.getAggregations().get(me);
                long value = count.getValue();
                map.put(bucket.getKey(), value);
            } else if (AggsType.min == aggsType) {
                ParsedMin min = bucket.getAggregations().get(me);
                double value = min.getValue();
                map.put(bucket.getKey(), value);
            } else if (AggsType.max == aggsType) {
                ParsedMax max = bucket.getAggregations().get(me);
                double value = max.getValue();
                map.put(bucket.getKey(), value);
            } else if (AggsType.sum == aggsType) {
                ParsedSum sum = bucket.getAggregations().get(me);
                double value = sum.getValue();
                map.put(bucket.getKey(), value);
            } else if (AggsType.avg == aggsType) {
                ParsedAvg avg = bucket.getAggregations().get(me);
                double value = avg.getValue();
                map.put(bucket.getKey(), value);
            }
        }
        return map;
    }

    /**
     * 日期直方图聚合
     *
     * @param indexName
     * @param aggsType
     * @param bucketName
     * @param metricName
     * @param equalsCondition
     * @param rangeCondition
     * @param interval
     * @return
     */
    public Map<Object, Object> dateHistogramAggs(String indexName,
                                                 AggsType aggsType,
                                                 String bucketName,
                                                 String metricName,
                                                 Map<String, Object> equalsCondition,
                                                 Map<String, Object> rangeCondition,
                                                 DateHistogramInterval interval) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        String by = "by_" + bucketName.replaceAll(keyword, "");
        String me = aggsType.toString() + "_" + metricName.replaceAll(keyword, "");
        AggregationBuilder aggregation = AggregationBuilders.dateHistogram(by).field(bucketName).dateHistogramInterval(interval);

        if (AggsType.count == aggsType) {
            aggregation.subAggregation(AggregationBuilders.count(me).field(metricName));
        } else if (AggsType.min == aggsType) {
            aggregation.subAggregation(AggregationBuilders.min(me).field(metricName));
        } else if (AggsType.max == aggsType) {
            aggregation.subAggregation(AggregationBuilders.max(me).field(metricName));
        } else if (AggsType.sum == aggsType) {
            aggregation.subAggregation(AggregationBuilders.sum(me).field(metricName));
        } else if (AggsType.avg == aggsType) {
            aggregation.subAggregation(AggregationBuilders.avg(me).field(metricName));
        }
        sourceBuilder.aggregation(aggregation);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 某一field=具体的值； 也可以某一field 的值 in 具体定义集合里的值
        if (null != equalsCondition && !equalsCondition.isEmpty()) {
            for (Map.Entry<String, Object> entry : equalsCondition.entrySet()) {
                String key = entry.getKey();
                //由于我创建索引的时候使用字符串不分词使用的.keyword类型
                if (key.endsWith("_s")) {
                    queryValueBuild(boolQueryBuilder, key + ".keyword", entry.getValue());
                } else {
                    queryValueBuild(boolQueryBuilder, key, entry.getValue());
                }
            }
        }
        //范围查询
        if (null != rangeCondition && !rangeCondition.isEmpty()) {
            rangeValueBuild(boolQueryBuilder, rangeCondition);
        }
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.size(0);

        // 执行查询
        SearchResponse response = executeSearch(sourceBuilder, indexName);
        // 解析结果
        Aggregations aggregations = response.getAggregations();
        ParsedDateHistogram agg = aggregations.get(by);
        Map<Object, Object> map = new LinkedHashMap<>();
        for (Histogram.Bucket bucket : agg.getBuckets()) {
            if (AggsType.count == aggsType) {
                ValueCount count = bucket.getAggregations().get(me);
                long value = count.getValue();
                map.put(bucket.getKey(), value);
            } else if (AggsType.min == aggsType) {
                ParsedMin min = bucket.getAggregations().get(me);
                double value = min.getValue();
                map.put(bucket.getKey(), value);
            } else if (AggsType.max == aggsType) {
                ParsedMax max = bucket.getAggregations().get(me);
                double value = max.getValue();
                map.put(bucket.getKey(), value);
            } else if (AggsType.sum == aggsType) {
                ParsedSum sum = bucket.getAggregations().get(me);
                double value = sum.getValue();
                map.put(bucket.getKey(), value);
            } else if (AggsType.avg == aggsType) {
                ParsedAvg avg = bucket.getAggregations().get(me);
                double value = avg.getValue();
                map.put(bucket.getKey(), value);
            }
        }
        return map;
    }

    /**
     * 查询数量(跨索引)
     *
     * @param equalsCondition
     * @param rangeCondition
     * @param indexName
     * @return
     */
    public long count(Map<String, Object> equalsCondition,
                      Map<String, Object> rangeCondition,
                      String... indexName) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 某一field=具体的值； 也可以某一field 的值 in 具体定义集合里的值
        if (null != equalsCondition && !equalsCondition.isEmpty()) {
            for (Map.Entry<String, Object> entry : equalsCondition.entrySet()) {
                String key = entry.getKey();
                //由于我创建索引的时候使用字符串不分词使用的.keyword类型
                if (key.endsWith("_s")) {
                    queryValueBuild(boolQueryBuilder, key + ".keyword", entry.getValue());
                } else {
                    queryValueBuild(boolQueryBuilder, key, entry.getValue());
                }
            }
        }
        //范围查询
        if (null != rangeCondition && !rangeCondition.isEmpty()) {
            rangeValueBuild(boolQueryBuilder, rangeCondition);
        }
        // 执行查询
        CountResponse response = executeCountSearch(boolQueryBuilder, indexName);
        return response.getCount();
    }

    /**
     * @param boolQueryBuilder
     * @param key
     * @param value
     * @Description 查询条件组装
     */
    private static void queryValueBuild(BoolQueryBuilder boolQueryBuilder, String key, Object value) {
        TermQueryBuilder termQueryBuilder;
        if (null != value && !"".equals(value)) {
            if (value instanceof String) {
                String strValue = (String) value;
                if (strValue.endsWith("%")) {
                    PrefixQueryBuilder prefixQueryBuilder = QueryBuilders.prefixQuery(key, strValue.replace("%", ""));
                    boolQueryBuilder.must(prefixQueryBuilder);
                } else if (strValue.startsWith("*") && strValue.endsWith("*")) {
                    WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery(key, strValue);
                    boolQueryBuilder.must(wildcardQueryBuilder);
                } else if (strValue.startsWith("*") || strValue.endsWith("*")) {
                    MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(key, strValue.replace("*", ""));
                    boolQueryBuilder.must(matchQueryBuilder);
                } else {
                    termQueryBuilder = QueryBuilders.termQuery(key, strValue);
                    boolQueryBuilder.must(termQueryBuilder);
                }
            } else if (value instanceof Collection) {
                Collection<?> collectionValue = (Collection<?>) value;
                //此处使用了多值条件
                boolQueryBuilder.must(QueryBuilders.termsQuery(key, collectionValue));
            } else {
                termQueryBuilder = QueryBuilders.termQuery(key, value);
                boolQueryBuilder.must(termQueryBuilder);
            }
        }
    }

    /**
     * @param boolQueryBuilder
     * @param rangeCondition
     * @Description 范围条件查询组装
     */
    private static void rangeValueBuild(BoolQueryBuilder boolQueryBuilder, Map<String, Object> rangeCondition) {
        for (Map.Entry<String, Object> entry : rangeCondition.entrySet()) {
            Map<String, Object> range = intervalParse((String) entry.getValue());
            String key = entry.getKey();
            RangeQueryBuilder rangeQueryBuilder;
            if (key.endsWith("_s")) {
                rangeQueryBuilder = QueryBuilders.rangeQuery(key + ".keyword");
            } else {
                rangeQueryBuilder = QueryBuilders.rangeQuery(key);
            }
            if (!ObjectUtil.isEmpty(range.get("leftValue"))) {
                if (DefineConstant.INTERVAL_OPEN_VALUE.equals(range.get("leftType"))) {
                    rangeQueryBuilder.from(range.get("leftValue"), false);
                } else if (DefineConstant.INTERVAL_CLOSE_VALUE.equals(range.get("leftType"))) {
                    rangeQueryBuilder.from(range.get("leftValue"), true);
                }
            }
            if (!ObjectUtil.isEmpty(range.get("rightValue"))) {
                if (DefineConstant.INTERVAL_OPEN_VALUE.equals(range.get("rightType"))) {
                    rangeQueryBuilder.to(range.get("rightValue"), false);
                } else if (DefineConstant.INTERVAL_CLOSE_VALUE.equals(range.get("rightType"))) {
                    rangeQueryBuilder.to(range.get("rightValue"), true);
                }
            }
            boolQueryBuilder.must(rangeQueryBuilder);
        }
    }

    /**
     * @param interval
     * @return
     * @Description 区间解析：[,]/(,)/[,)/(,]
     */
    private static Map<String, Object> intervalParse(String interval) {
        Map<String, Object> range = new HashMap<>();
        if (interval.startsWith(DefineConstant.INTERVAL_CLOSE_LEFT)) {
            range.put("leftType", DefineConstant.INTERVAL_CLOSE_VALUE);
        } else if (interval.startsWith(DefineConstant.INTERVAL_OPEN_LEFT)) {
            range.put("leftType", DefineConstant.INTERVAL_OPEN_VALUE);
        } else {
            log.error("区间参数格式错误：{}", interval);
            //若实际业务相关需要，抛出异常处理throw new Exception();
        }
        if (interval.endsWith(DefineConstant.INTERVAL_CLOSE_RIGHT)) {
            range.put("rightType", DefineConstant.INTERVAL_CLOSE_VALUE);
        } else if (interval.startsWith(DefineConstant.INTERVAL_OPEN_RIGHT)) {
            range.put("rightType", DefineConstant.INTERVAL_OPEN_VALUE);
        } else {
            log.error("区间参数格式错误：{}", interval);
            //若实际业务相关需要，抛出异常处理throw new Exception();
        }
        int strLen = interval.length();
        String[] lr = interval.substring(1, strLen - 1).split(DefineConstant.COMMAN_SIGN, 2);
        if (lr.length > 0) {
            range.put("leftValue", lr[0]);
        }
        if (lr.length > 1) {
            range.put("rightValue", lr[1]);
        }
        return range;
    }

    /**
     * @param sourceBuilder
     * @param orderBy
     * @Description 查询排序
     */
    private static void buildSort(SearchSourceBuilder sourceBuilder, List<String> orderBy) {
        SortBuilder<FieldSortBuilder> sortBuilder;
        for (String sortField : orderBy) {
            if (sortField.startsWith("-")) {
                //降序排序
                if (sortField.endsWith("_s")) {
                    sortBuilder = SortBuilders.fieldSort(sortField.replace("-", "") + ".keyword").order(SortOrder.DESC);
                } else {
                    sortBuilder = SortBuilders.fieldSort(sortField.replace("-", "")).order(SortOrder.DESC);
                }
            } else {
                //升序排序
                if (sortField.endsWith("_s")) {
                    sortBuilder = SortBuilders.fieldSort(sortField.replace("-", "") + ".keyword").order(SortOrder.ASC);
                } else {
                    sortBuilder = SortBuilders.fieldSort(sortField.replace("-", "")).order(SortOrder.ASC);
                }
            }
            sourceBuilder.sort(sortBuilder);
        }
    }

    /**
     * @param sourceBuilder
     * @param indexName     对应的es的index名
     * @return
     * @Description 执行查询
     */
    private static SearchResponse executeSearch(SearchSourceBuilder sourceBuilder, String... indexName) {
        // 获取不同系统的换行符
        String lineSeparator = System.lineSeparator();
        log.info(lineSeparator + "ES查询：index:" + Arrays.toString(indexName) + lineSeparator + "search:" + sourceBuilder.toString() + lineSeparator);
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchResponse response = null;
        searchRequest.source(sourceBuilder);
        try {
            response = client.search(searchRequest, RequestOptions.DEFAULT);
            log.info("search status:{}, totalNum:{}", response.status(), response.getHits().getTotalHits());
        } catch (IOException e) {
            //异常处理，实际业务中是需要根据需求具体处理，自定义异常捕获
            log.error(e.getMessage());
        }
        return response;
    }

    private static CountResponse executeCountSearch(QueryBuilder sourceBuilder, String... indexName) {
        // 获取不同系统的换行符
        String lineSeparator = System.lineSeparator();
        log.info(lineSeparator + "ES查询：index:" + Arrays.toString(indexName) + lineSeparator + "search:" + sourceBuilder.toString() + lineSeparator);
        CountRequest countRequest = new CountRequest(indexName);
        CountResponse response = null;
        countRequest.query(sourceBuilder);
        try {
            response = client.count(countRequest, RequestOptions.DEFAULT);
            log.info("search status:{}, totalNum:{}", response.status(), response.getCount());
        } catch (IOException e) {
            //异常处理，实际业务中是需要根据需求具体处理，自定义异常捕获
            log.error(e.getMessage());
        }
        return response;
    }

    /**
     * 索引创建前，判断索引是否存在
     */
    public static Boolean isIndexExist(String checkIndex) {
        boolean flag = false;
        try {
            GetIndexRequest getIndexRequest = new GetIndexRequest(checkIndex);
            getIndexRequest.local(false);
            getIndexRequest.humanReadable(true);
            getIndexRequest.includeDefaults(false);
            flag = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    /**
     * 通过ID判断文档是否存在
     *
     * @param index 索引，类似数据库
     * @param id    数据ID
     * @return
     */
    public boolean existsById(String index, String id) throws IOException {
        GetRequest request = new GetRequest(index, id);
        //不获取返回的_source的上下文
        request.fetchSourceContext(new FetchSourceContext(false));
        request.storedFields("_none_");
        return client.exists(request, RequestOptions.DEFAULT);
    }


}
