EsTemplate

@Slf4j
@Component
public class EsTemplate {

    @Autowired
    private RestHighLevelClient client;

    private static final String TIMESTAMP = "timestamp";

    /**
     * 创建索引(默认分片数为5和副本数为1)
     *
     * @param indexName
     * @param esMapTypeClass
     * @throws IOException
     */
    public boolean createIndex(String indexName, Class<? extends IESMappingType> esMapTypeClass) throws IOException {
        if (checkIndexExists(indexName)) {
            log.error("\"index={}\"索引已经存在!", indexName);
            return false;
        }
        CreateIndexRequest request = new CreateIndexRequest(indexName);
        XContentBuilder builder = buildXContentBuilder(indexName, esMapTypeClass);
        request.mapping(esMapTypeClass.getSimpleName(), builder);

        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        // 指示是否所有节点都已确认请求
        boolean acknowledged = response.isAcknowledged();
        // 指示是否在超时之前为索引中的每个分片启动了必需的分片副本数
        boolean shardsAcknowledged = response.isShardsAcknowledged();
        if (acknowledged || shardsAcknowledged) {
            log.info("创建索引成功!索引名称为{}", indexName);
            return true;
        }

        log.error("创建索引失败");
        return false;

    }

    /**
     * 创建索引
     *
     * @param index
     * @throws IOException
     */
    public boolean createIndex(String index) throws IOException {
        if (checkIndexExists(index)) {
            log.error("\"index={}\"索引已存在!", index);
            return false;
        }
        CreateIndexRequest request = new CreateIndexRequest(index);
        CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);
        boolean acknowledged = response.isAcknowledged();
        boolean shardsAcknowledged = response.isShardsAcknowledged();
        if (acknowledged || shardsAcknowledged) {
            log.info("创建索引成功!索引名称为{}", index);
            return true;
        } else {
            log.error("创建索引失败");
            return false;
        }
    }

    /**
     * 创建索引(传入参数:分片数、副本数)
     *
     * @param indexName
     * @param shards
     * @param replicas
     * @throws IOException
     */
    public boolean createIndex(String indexName, int shards, int replicas) throws IOException {
        if (checkIndexExists(indexName)) {
            log.error("\"index={}\"索引已存在!", indexName);
            return false;
        }
        Settings.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()) {
            log.info("创建索引成功!索引名称为{}", indexName);
            return true;
        } else {
            log.error("创建索引失败");
            return false;
        }
    }

    /**
     * 删除索引
     *
     * @param indexName
     * @throws IOException
     */
    public Boolean deleteIndex(String indexName) throws IOException {
        try {
            AcknowledgedResponse response = client.indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT);
            if (response.isAcknowledged()) {
                log.info("{} 索引删除成功!", indexName);
                return true;
            }
        } catch (ElasticsearchException ex) {
            if (ex.status() == RestStatus.NOT_FOUND) {
                log.error("{} 索引名不存在", indexName);
                return false;
            }
            log.error("删除失败!");
        }
        return false;
    }


    /**
     * 判断索引是否存在
     *
     * @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) {
            log.error("判断索引是否存在,操作异常!");
        }
        return false;
    }


    /**
     * 开启索引
     *
     * @param indexName
     * @throws IOException
     */
    public void openIndex(String indexName) throws IOException {
        if (!checkIndexExists(indexName)) {
            log.error("索引不存在!");
            return;
        }
        OpenIndexRequest request = new OpenIndexRequest(indexName);
        OpenIndexResponse response = client.indices().open(request, RequestOptions.DEFAULT);
        if (response.isAcknowledged() || response.isShardsAcknowledged()) {
            log.info("{} 索引开启成功!", indexName);
        }
    }

    /**
     * 关闭索引
     *
     * @param indexName
     * @throws IOException
     */
    public void closeIndex(String indexName) throws IOException {
        if (!checkIndexExists(indexName)) {
            log.error("索引不存在!");
            return;
        }
        CloseIndexRequest request = new CloseIndexRequest(indexName);
        AcknowledgedResponse response = client.indices().close(request, RequestOptions.DEFAULT);
        if (response.isAcknowledged()) {
            log.info("{} 索引已关闭!", indexName);
        }
    }


    /**
     * 查找文档
     *
     * @param index
     * @param type
     * @param id
     * @return
     * @throws IOException
     */
    public Map<String, Object> getDocument(String index, String type, String id) throws IOException {
        Map<String, Object> 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) {
                log.error("文档未找到,请检查参数!");
            }
            if (e.status() == RestStatus.CONFLICT) {
                log.error("版本冲突!");
            }
            log.error("查找失败!");
        }

        if (Objects.nonNull(response)) {
            if (response.isExists()) { // 文档存在
                resultMap = response.getSourceAsMap();
            } else {
                // 处理未找到文档的方案。 请注意,虽然返回的响应具有404状态代码,但仍返回有效的GetResponse而不是抛出异常。
                // 此时此类响应不持有任何源文档,并且其isExists方法返回false。
                log.error("文档不存在!");
            }
        }
        return resultMap;
    }

    /**
     * 判断文档是否存在
     * @param
     * @return true:存在  false:不存在
     * @author hucm
     * @since 2021/5/31
     */
    public boolean existDoc(String index, String type, String id) throws IOException {
        GetRequest request = new GetRequest(index, type,id);
        // 不需要获取source内容
        request.fetchSourceContext(new FetchSourceContext(false));
        return client.exists(request, RequestOptions.DEFAULT);
    }

    /**
     * 删除文档
     *
     * @param indexName
     * @param typeName
     * @param docId
     * @throws IOException
     */
    public void deleteDocument(String indexName, String typeName, String docId) throws IOException {
        DeleteRequest request = new DeleteRequest(indexName, typeName, docId);
        DeleteResponse response = null;
        try {
            response = client.delete(request, RequestOptions.DEFAULT);
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.CONFLICT) {
                log.error("版本冲突!");
            }
            log.error("删除失败!");
        }
        if (Objects.nonNull(response)) {
            if (response.getResult() == DocWriteResponse.Result.NOT_FOUND) {
                log.error("不存在该文档,请检查参数!");
            }
            log.info("文档已删除!");
            ReplicationResponse.ShardInfo shardInfo = response.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                log.error("部分分片副本未处理");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                    String reason = failure.reason();
                    log.error("失败原因:{}", reason);
                }
            }
        }
    }

    /**
     * 通过一个脚本语句(如:"ctx._source.posttime=\"2018-09-18\"")更新文档
     * 针对单个字段更新或object类型字段更新
     * @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) {
                //log.info("文档更新成功!");
            } else if (response.getResult() == DocWriteResponse.Result.DELETED) {
                log.error("\"index={},type={},id={}\"的文档已被删除,无法更新!", response.getIndex(), response.getType(), response.getId());
            } else if (response.getResult() == DocWriteResponse.Result.NOOP) {
                log.error("操作没有被执行!");
            }

            ReplicationResponse.ShardInfo shardInfo = response.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                log.error("部分分片副本未处理");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                    String reason = failure.reason();
                    log.error("未处理原因:{}", reason);
                }
            }
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.NOT_FOUND) {
                log.error("不存在这个文档,请检查参数!");
            } else if (e.status() == RestStatus.CONFLICT) {
                log.error("版本冲突异常!");
            }
            log.error("更新失败!:{},脚本:{},id:{}",e.getMessage(),script,id);
        }
    }

    /**
     * 通过一个脚本语句(针对于object和nested数据类型字段更新)更新文档
     * @param index
     * @param type
     * @param id
     * @param script
     * @param params
     * @throws IOException
     */
    public void updateDocByScript(String index, String type, String id, String script,Map<String, Object> params) throws IOException {
        Script inline = new Script(ScriptType.INLINE,Script.DEFAULT_SCRIPT_LANG,script,params);
        UpdateRequest request = new UpdateRequest(index, type, id).script(inline);
        try {
            UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
            if (response.getResult() == DocWriteResponse.Result.UPDATED) {
                //log.info("文档更新成功!");
            } else if (response.getResult() == DocWriteResponse.Result.DELETED) {
                log.error("\"index={},type={},id={}\"的文档已被删除,无法更新!", response.getIndex(), response.getType(), response.getId());
            } else if (response.getResult() == DocWriteResponse.Result.NOOP) {
                log.error("操作没有被执行!");
            }

            ReplicationResponse.ShardInfo shardInfo = response.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                log.error("部分分片副本未处理");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure : shardInfo.getFailures()) {
                    String reason = failure.reason();
                    log.error("未处理原因:{}", reason);
                }
            }
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.NOT_FOUND) {
                log.error("不存在这个文档,请检查参数!");
            } else if (e.status() == RestStatus.CONFLICT) {
                log.error("版本冲突异常!");
            }
            log.error("更新失败!:{},脚本:{},id:{}",e.getMessage(),script,id);
        }
    }

    /**
     * 批量增加文档
     *
     * @param indexName
     * @param typeName
     * @param esMappingTypes 添加的文档列表
     * @throws IOException
     */
    public List<String> bulkAdd(String indexName, String typeName, List<? extends IESMappingType> esMappingTypes) throws IOException {
        List<String> failureList = new ArrayList<>();
        BulkRequest bulkRequest = new BulkRequest();
        if (CollectionUtils.isNotEmpty(esMappingTypes)) {
            for (IESMappingType mappingType : esMappingTypes) {
                IndexRequest request = new IndexRequest(indexName, typeName, mappingType.generateDocId()).source(JSON.toJSONString(mappingType), XContentType.JSON);
                bulkRequest.add(request);
            }
        }
        // 超时时间(2分钟)
        bulkRequest.timeout(TimeValue.timeValueMinutes(2L));
        // 刷新策略
        bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);

        if (bulkRequest.numberOfActions() == 0) {
            log.error("参数错误,批量增加操作失败!");
            return new ArrayList<>();
        }
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        // 全部操作成功
        if (!bulkResponse.hasFailures()) {
//            log.info("批量增加操作成功!");
        } else {
            for (BulkItemResponse bulkItemResponse : bulkResponse) {
                if (bulkItemResponse.isFailed()) {
                    BulkItemResponse.Failure failure = bulkItemResponse.getFailure();
                    log.error("\"index={}, type={}, id={}\"的文档增加失败!", failure.getIndex(), failure.getType(), failure.getId());
                    log.error("增加失败详情: {}", failure.getMessage());
                    failureList.add(failure.getId());
                    log.error("新增失败的货品id:{}",failureList);
                    return failureList;
                } else {
//                    log.info("\"index={}, type={}, id={}\"的文档增加成功!", bulkItemResponse.getIndex(), bulkItemResponse.getType(), bulkItemResponse.getId());
                }
            }
        }
        return new ArrayList<>();
    }

    /**
     * 根据条件删除es数据
     *
     * @param indexName           索引名称
     * @param bigBoolQueryBuilder
     * @return 删除条数
     * @author shixiaorui
     * @date 2020/8/31  18:27
     */
    public Long delByEsQuery(String indexName, BoolQueryBuilder bigBoolQueryBuilder) {
        DeleteByQueryRequest request =
                new DeleteByQueryRequest(indexName);
        request.setQuery(bigBoolQueryBuilder);
        try {
            BulkByScrollResponse bulkResponse =
                    client.deleteByQuery(request, RequestOptions.DEFAULT);
            return bulkResponse.getDeleted();
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.CONFLICT) {
                log.error("版本冲突!");
            }
            log.error("删除失败!:{}", e.getMessage());
        } catch (IOException ioExp) {
            log.error("删除失败:{}", ioExp.getMessage());
        }
        return 0L;

    }

    /**
     * 批量更新文档
     *
     * @param indexName
     * @param typeName
     * @param esMappingTypes
     * @throws IOException
     */
    public List<String> bulkUpdate(String indexName, String typeName, List<? extends IESMappingType> esMappingTypes) throws IOException {
        List<String> failureList = new ArrayList<>();
        BulkRequest bulkRequest = new BulkRequest();
        if (CollectionUtils.isNotEmpty(esMappingTypes)) {
            for (IESMappingType mappingType : esMappingTypes) {
                UpdateRequest request=new UpdateRequest(indexName, typeName, mappingType.generateDocId())
                        .doc(JSON.toJSONString(mappingType), XContentType.JSON);
                // true,表明如果文档不存在,则新更新的文档内容作为新的内容插入文档,
                 //这个和scriptedUpsert的区别是:更新文档的两种不同方式,有的使用doc方法更新有的使用脚本更新
                request.docAsUpsert(true);

                // 是否将文档内容作为结果返回,默认是禁止的
                request.fetchSource(false);
                bulkRequest.add(request);
            }
        }



        if (bulkRequest.numberOfActions() == 0) {
            log.error("参数错误,批量更新操作失败!");
            return new ArrayList<>();
        }
        bulkRequest.timeout(TimeValue.timeValueMinutes(2L));
        bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        if (!bulkResponse.hasFailures()) {
//            log.info("批量更新操作成功!");
        } else {
            for (BulkItemResponse bulkItemResponse : bulkResponse) {
                if (bulkItemResponse.isFailed()) {
                    BulkItemResponse.Failure failure = bulkItemResponse.getFailure();
                    log.error("\"index={}, type={}, id={}\"的文档更新失败!", failure.getIndex(), failure.getType(), failure.getId());
                    log.error("更新失败详情: {}", failure.getMessage());
                    failureList.add(failure.getId());
                    log.error("更新失败的货品id: {}", failureList);
                    return failureList;
                }/* else {
                    log.info("\"index={}, type={}, id={}\"的文档更新成功!", bulkItemResponse.getIndex(), bulkItemResponse.getType(), bulkItemResponse.getId());
                }*/
            }
        }
        return failureList;
    }

    /**
     * 批量删除文档
     *
     * @param indexName
     * @param typeName
     * @param docIds
     * @throws IOException
     */
    public boolean bulkDelete(String indexName, String typeName, List<String> docIds) throws IOException {

        BulkRequest bulkRequest = new BulkRequest();
        if (CollectionUtils.isNotEmpty(docIds)) {
            for (String docId : docIds) {
                DeleteRequest request = new DeleteRequest(indexName, typeName, docId);
                bulkRequest.add(request);
            }
        }
        if (bulkRequest.numberOfActions() == 0) {
            log.error("操作失败,请检查参数!");
            return false;
        }
        bulkRequest.timeout(TimeValue.timeValueMinutes(2L));
        bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        if (!bulkResponse.hasFailures()) {
            log.info("批量删除操作成功!");
            return true;
        } else {
            for (BulkItemResponse bulkItemResponse : bulkResponse) {
                if (bulkItemResponse.isFailed()) {
                    BulkItemResponse.Failure failure = bulkItemResponse.getFailure();
                    log.error("\"index={}, type={}, id={}\"的文档删除失败!", failure.getIndex(), failure.getType(), failure.getId());
                    log.error("删除失败详情: {}", failure.getMessage());
                    return false;
                }
            }
            return false;
        }
    }

    /**
     * 批量查找文档
     *
     * @param indexName
     * @return
     * @throws IOException
     */
    public List<Map<String, Object>> multiGet(String indexName, String typeName, List<String> docIds) throws IOException {
        List<Map<String, Object>> resultList = new ArrayList<>();
        MultiGetRequest request = new MultiGetRequest();
        if (CollectionUtils.isNotEmpty(docIds)) {
            for (String docId : docIds) {
                request.add(new MultiGetRequest.Item(indexName, typeName, docId));
            }
        }
        request.realtime(false);
        request.refresh(true);
        MultiGetResponse response = client.mget(request, RequestOptions.DEFAULT);
        List<Map<String, Object>> list = parseMGetResponse(response);
        if (!list.isEmpty()) {
            resultList.addAll(list);
        }
        return resultList;
    }

    private List<Map<String, Object>> parseMGetResponse(MultiGetResponse response) {
        List<Map<String, Object>> list = new ArrayList<>();
        MultiGetItemResponse[] responses = response.getResponses();
        for (MultiGetItemResponse item : responses) {
            GetResponse getResponse = item.getResponse();
            if (Objects.nonNull(getResponse)) {
                if (!getResponse.isExists()) {
                    log.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) {
                    log.error("\"index={}, type={}, id={}\"的文档不存在!", failure.getIndex(), failure.getType(), failure.getId());
                } else if (e.status() == RestStatus.CONFLICT) {
                    log.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<Map<String, Object>> queryByConditions(String indexName, String typeName, String level, String messageKey, Long startTime, Long endTime, Integer size) throws IOException {
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (StringUtils.isBlank(level) && StringUtils.isBlank(messageKey)) {
            log.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(indexName).types(typeName);
        request.source(searchBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        int failedShards = response.getFailedShards();
        if (failedShards > 0) {
            log.error("部分分片副本处理失败!");
            for (ShardSearchFailure failure : response.getShardFailures()) {
                String reason = failure.reason();
                log.error("分片处理失败原因:{}", reason);
            }
        }
        List<Map<String, Object>> 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("name", 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<Map<String, Object>> parseSearchResponse(SearchResponse response) {
        List<Map<String, Object>> resultList = new ArrayList<>();
        SearchHit[] hits = response.getHits().getHits();
        for (SearchHit hit : hits) {
            resultList.add(hit.getSourceAsMap());
        }
        return resultList;
    }

    /**
     * 查询所有文档id
     *
     * @return
     */
    public List<String> queryAllIdByIndexName(String indexName, String typeName) throws IOException {
        List<String> resultList = new ArrayList<>();
        SearchSourceBuilder searchBuilder = new SearchSourceBuilder();
        searchBuilder.size(500);

        // 初始化 scroll 上下文
        SearchRequest request = new SearchRequest(indexName).types(typeName);
        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.getId());
        }
        // 通过传递scrollId循环取出所有相关文档
        while (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.getId());
            }
        }
        // 清理 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    日志结束时间,可以为空
     * @return
     */
    public List<Map<String, Object>> queryAllByConditions(String indexName, String typeName, String level, String messageKey, Long startTime, Long endTime) throws IOException {
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (StringUtils.isBlank(level) && StringUtils.isBlank(messageKey)) {
            log.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(indexName).types(typeName);
        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 SearchPageBean<Map<String, Object>> queryPageByConditions(String indexName, String typeName, String level, String messageKey, Long startTime, Long endTime, Integer pageNum, Integer pageSize) throws IOException {
        if (StringUtils.isBlank(level) && StringUtils.isBlank(messageKey)) {
            log.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(indexName).types(typeName);
        request.source(searchBuilder);
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        int totalRecord = (int) hits.getTotalHits();
        List<Map<String, Object>> results = new ArrayList<>();
        for (SearchHit hit : hits.getHits()) {
            results.add(hit.getSourceAsMap());
        }

        SearchPageBean<Map<String, Object>> page = new SearchPageBean<>();
        page.setPageNo(pageNum);
        page.setPageSize(pageSize);
        page.setTotalPages(totalRecord);
        page.setData(results);
        return page;
    }


    /**
     * 通用查询
     * 复杂查询可以自定义实现RestHighLevelClient
     *
     * @param indexName           索引
     * @param eSMappingType       返回的对象类型
     * @param searchSourceBuilder 查询条件
     * @param <T>                 泛型
     * @return pageResult
     * @author zhangxh
     * @since 2019/07/04
     */
    public <T> PageResult<T> search(String indexName, Class<T> eSMappingType, SearchSourceBuilder searchSourceBuilder) {
        if (eSMappingType == null) {
            throw new IllegalArgumentException("eSMappingType can not be null");
        }
        if (indexName == null) {
            throw new ServiceException("indexName 不能为空");
        }

        SearchRequest searchRequest = new SearchRequest()
                .indices(indexName)
                .types(eSMappingType.getSimpleName())
                .source(searchSourceBuilder);

        try {
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = search.getHits();
            SearchHit[] hitsArray = hits.getHits();
            PageResult<T> pageResult = new PageResult<>();
            pageResult.setRecordCount((int) hits.getTotalHits());
            List<T> list = new ArrayList<>();
            for (SearchHit hit : hitsArray) {
                list.add(JsonHelper.parseBean(hit.getSourceAsString(), eSMappingType));
            }
            pageResult.setResult(list);
//            log.info("es查询语句:{}" , searchSourceBuilder.toString());
            return pageResult;
        } catch (Exception e) {
            log.error("查询文档:{},失败:{}", indexName, e.getMessage());
            e.printStackTrace();
        }
        return new PageResult<>();
    }


    public <T> PageResult<T> searchMore(String indexName, Class<T> eSMappingType, SearchSourceBuilder searchSourceBuilder) {
        if (eSMappingType == null) {
            throw new IllegalArgumentException("eSMappingType can not be null");
        }
        if (indexName == null) {
            throw new ServiceException("indexName 不能为空");
        }

        searchSourceBuilder.from(0).size(10000);

        SearchRequest searchRequest = new SearchRequest()
                .indices(indexName)
                .types(eSMappingType.getSimpleName())
                .source(searchSourceBuilder);

        try {
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = search.getHits();
            SearchHit[] hitsArray = hits.getHits();
            PageResult<T> pageResult = new PageResult<>();
            pageResult.setRecordCount((int) hits.getTotalHits());
            List<T> list = new ArrayList<>();
            for (SearchHit hit : hitsArray) {
                list.add(JsonHelper.parseBean(hit.getSourceAsString(), eSMappingType));
            }
            pageResult.setResult(list);
//            log.info("es查询语句:{}" , searchSourceBuilder.toString());
            return pageResult;
        } catch (Exception e) {
            log.error("查询文档:{},失败:{}", indexName, e.getMessage());
            e.printStackTrace();
        }
        return new PageResult<>();
    }


    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;
    }




    /**
     * 根据IESMappingType 的实现类,生成创建mappingType的 XContentBuilder
     *
     * @param esMapTypeClass
     * @return
     * @throws IOException
     */
    public XContentBuilder buildXContentBuilder(String index, Class<? extends IESMappingType> esMapTypeClass) throws IOException {
        XContentBuilder builder = XContentFactory.jsonBuilder().startObject();

        // 处理对象属性类型
        dealObj(builder, esMapTypeClass);
        builder.endObject();
        return builder;
    }

    /**
     * 根据对象的es属性注解判断是否进行属性处理
     *
     * @param builder
     * @param esMapTypeClass
     * @throws IOException
     */
    private void dealObj(XContentBuilder builder, Class<? extends IESMappingType> esMapTypeClass) throws IOException {
        // 子属性放到该索引下
        builder.startObject("properties");
        for (Field field : esMapTypeClass.getDeclaredFields()) {
            ESField fieldProp = field.getAnnotation(ESField.class);
            if (fieldProp != null) {
                dealField(builder, field, fieldProp);
            }
        }

        //判断对象是否有 @Suggestable 注解,有则添加 suggest 字段
        EnableSuggest suggest = esMapTypeClass.getAnnotation(EnableSuggest.class);
        if (suggest != null) {
            String analyzer = suggest.analyzerType().name();
            String suggestName = suggest.suggestName();
            builder.startObject(suggestName)
                    .field("type", "completion")
                    .field("index_analyzer", analyzer)
                    .field("search_analyzer", analyzer)
                    .field("payloads", "true")
                    .field("preserve_position_increments", false)
                    .field("preserve_separators", false)
                    .endObject();
        }
        builder.endObject();
    }




    /**
     * 处理对象的属性类型
     *
     * @param builder
     * @param field     属性
     * @param fieldProp 属性注解信息
     * @throws IOException
     */
    private void dealField(XContentBuilder builder, Field field, ESField fieldProp) throws IOException {
        try {
            if (List.class.isAssignableFrom(field.getType()) || field.getType().isArray()) {
                //list 类型的  嵌套类型和对象数组
                builder.startObject(field.getName());// 这里如果是startArray就会有问题.
                if (fieldProp.fieldType() == ESFieldType.NESTED) {
                    //嵌套类型(要查询对象信息)
                    builder.field("type", ESFieldType.NESTED.getTypeValue());
                }else{
                    //对象数组(只是保存对象信息)
                    builder.field("type", ESFieldType.OBJECT.getTypeValue());
                }

                String className = "";
                Type fc = field.getGenericType();
                if (fc instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) fc;
                    className = pt.getActualTypeArguments()[0].toString().replace("class ", "");
                } else if (field.getType().isArray()) {
                    className = field.getGenericType().toString().replace("class [L", "")
                            .replace("/", ".").replace(";", "");
                }

                Class clazz = Class.forName(className);

                if (IESMappingType.class.isAssignableFrom(clazz) || clazz.getAnnotation(ESDocObject.class) != null) {
                    dealObj(builder, clazz);
                } else if (clazz.isPrimitive() || isSimpleType(clazz)) {
                    builder.field("type", ESFieldType.STRING.getTypeValue())
                            .field("index", ESAnalyzer.not_analyzed.name()).field("store", fieldProp.isStore());
                }
                builder.endObject();
            } else if (Map.class.isAssignableFrom(field.getType())) {
                System.out.println("Map:" + field.getName());
            } else {
                // 处理简单对象
                if (isSimpleType(field.getType())) {
                    dealSimpleObjField(builder, field.getName(), fieldProp);
                    return;
                }

                // 如果是复杂的组合类型,继承于ESMapTypeI,则进行递归处理
                String className = field.getGenericType().toString().replace("class ", "");
                Class complexClazz = Class.forName(className);
                if (IESMappingType.class.isAssignableFrom(complexClazz)) {
                    builder.startObject(field.getName());
                    if (fieldProp.fieldType() == ESFieldType.NESTED) {
                        builder.field("type", ESFieldType.NESTED.getTypeValue());
                    }
                    dealObj(builder, complexClazz);
                    builder.endObject();
                }
            }
        } catch (Exception e) {
            log.error("创建mapping出错...", e);
        }
    }

    /**
     * 判断是否是简单的对象.
     *
     * @param cls
     * @return
     */
    private static boolean isSimpleType(Class cls) {
        if (cls == String.class || cls == Integer.class || cls == BigDecimal.class || cls == Date.class || cls == int.class || cls == long.class || cls == Long.class) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 处理对象类型的域值,处理已经是最简单对象的field
     */
    public void dealSimpleObjField(XContentBuilder mapbuilder, String fieldName, ESField eSMapType) throws IOException {

        mapbuilder.startObject(fieldName).field("store", eSMapType.isStore()).field("type", eSMapType.fieldType().getTypeValue());
        ESAnalyzer esAnalyzer = eSMapType.analyzerType();
        if (esAnalyzer != ESAnalyzer.not_analyzed) {
            if (esAnalyzer == ESAnalyzer.analyzed) {
                mapbuilder.field("index", "true");
            } else {
                mapbuilder.field("index", "true").
                        field("search_analyzer", esAnalyzer.name())
                        .field("analyzer", esAnalyzer.name());
            }

        } else if (esAnalyzer == ESAnalyzer.not_analyzed) {
            mapbuilder.field("index", "false");
        }
        mapbuilder.endObject();

    }

    /**
     * es聚合分组求和
     *
     * @param indexName 索引名称
     * @param groupKey  分组的key
     * @param sumKey    求和的key
     * @return Map
     * @author shixiaorui
     * @date 2020/11/12  10:29
     */
    public  Map<String, Long> groupKeySum(String indexName, String eSMappingTypeName, SearchSourceBuilder searchSourceBuilder, String groupKey, String sumKey) {
        try {
            SearchRequest searchRequest = new SearchRequest()
                    .indices(indexName)
                    .types(eSMappingTypeName)
                    .source(searchSourceBuilder);

            String aggName = groupKey + "_key";//分组字段别名
            String aggField = sumKey + "_key";//求和字段别名


            //分组groupKey,求和sumKey
            TermsAggregationBuilder field = AggregationBuilders.terms(aggName).field(groupKey);
            field.subAggregation(AggregationBuilders.sum(aggField).field(sumKey));
            searchSourceBuilder.aggregation(field);

            searchRequest.source(searchSourceBuilder);
            //执行查询
            SearchResponse response = client.search(searchRequest);

            //获取搜索的文档结果
            Map<String, Aggregation> aggMap = response.getAggregations().getAsMap();
            Terms gradeTerms = (Terms) aggMap.get(aggName);

            Map<String, Long> returnMap = new HashMap<>();
            Map<String, Long> valueResult = new LinkedHashMap<>();
            if(null!=gradeTerms.getBuckets()&&gradeTerms.getBuckets().size()>0){
                for (Terms.Bucket bucket : gradeTerms.getBuckets()) {
                    double num = ((Sum) bucket.getAggregations().get(aggField)).getValue();
                    returnMap.put(bucket.getKeyAsString(), new Double(num).longValue());
                }

                //排序
                returnMap.entrySet().stream()
                        .sorted(Map.Entry
                                .comparingByValue(Comparator.reverseOrder()))
                        .forEachOrdered(b->valueResult.put(b.getKey(), b.getValue()));
                return valueResult;
            }
            return new HashMap<>();

        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }



    /**
     * 查询,包含下钻聚合数据
     *
     * @param indexName           索引
     * @param eSMappingType       返回的对象类型
     * @param searchSourceBuilder 查询条件
     * @return pageResult
     * @author hucm
     * @since 2021/08/17
     */
    public IndexSearchResponse searchGroupBy(String indexName, Class eSMappingType, SearchSourceBuilder searchSourceBuilder, MallEsSearchFormDTO form) {
        if (eSMappingType == null) {
            throw new IllegalArgumentException("eSMappingType can not be null");
        }
        if (indexName == null) {
            throw new ServiceException("indexName 不能为空");
        }

        SearchRequest searchRequest = new SearchRequest()
                .indices(indexName)
                .types(eSMappingType.getSimpleName())
                .source(searchSourceBuilder);

        try {
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = search.getHits();
            SearchHit[] hitsArray = hits.getHits();
            // 封装返回结果
            IndexSearchResponse response = new IndexSearchResponse();
            // 放入源数据
            response.setOriginHits(hits);

            List<String> sources = new ArrayList<>();
            for (SearchHit hit : hits.getHits()) {
                sources.add(hit.getSourceAsString());
            }
            response.putReslutData(sources);
            // 聚合结果
            response.putResultAggs(agg2Map(search.getAggregations()));
            // 总数据
            response.setPageNum(form.getPageNo());
            response.setPageSize(form.getPageSize());
            response.setTotalCount((int) hits.getTotalHits());
            return response;
        } catch (Exception e) {
            log.error("查询文档:{},失败:{}", indexName, e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 处理一个聚合对象数据{@link Aggregations}
     *
     * @param aggregations
     * @return Map{key:一个聚合的名称,value:聚合后的桶数据}
     */
    public Map<String, Object> agg2Map(Aggregations aggregations) {
        Map<String, Object> resultMap = new HashMap<>();
        if (aggregations == null) {
            return resultMap;
        }
        for (Aggregation aggregation : aggregations) {

            resultMap.put(aggregation.getName(), dealOneAggregation(aggregation));
        }
        return resultMap;
    }

    /**
     * 处理一个聚合对象
     *
     * @param aggregation {@link Aggregation}
     * @return 如果只有一个桶数据, 则返回对象, 如果有多个桶数据, 则返回数组
     */
    public Object dealOneAggregation(Aggregation aggregation) {
        if (aggregation instanceof ParsedStringTerms) {
            Collection<? extends Terms.Bucket> buckets = ((ParsedStringTerms) aggregation).getBuckets();
            return dealBunkets(buckets);
        } else if (aggregation instanceof ParsedDoubleTerms) {
            Collection<? extends Terms.Bucket> buckets = ((ParsedDoubleTerms) aggregation).getBuckets();
            return dealBunkets(buckets);
        } else if (aggregation instanceof ParsedLongTerms) {
            Collection<? extends Terms.Bucket> buckets = ((ParsedLongTerms) aggregation).getBuckets();
            return dealBunkets(buckets);
        } else if (aggregation instanceof UnmappedTerms) {
            Collection<? extends Terms.Bucket> buckets = ((UnmappedTerms) aggregation).getBuckets();
            return dealBunkets(buckets);
        } else if (aggregation instanceof ParsedNested) {
            Aggregations aggregations = ((ParsedNested) aggregation).getAggregations();
            return agg2list(aggregations);
        } else if (aggregation instanceof ParsedAvg) {
            return aggregation.getName();
        } else {
            throw new IllegalArgumentException("未知聚合类型,不可处理");
        }
    }

    /**
     * 处理一个聚合下边的一个或多个桶数据
     *
     * @param buckets
     * @return 如果有一个桶, 那么就返回一个对象, 而不是列表 如果有多个桶,则返回列表 如果没有值,则返回一个空对象
     */
    public Object dealBunkets(Collection<? extends Terms.Bucket> buckets) {
        List<Object> list = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            list.add(dealOneBunket(bucket));
        }
        return list.isEmpty() ? new Object() : list.size() == 1 ? list.get(0) : list;
    }

    /**
     * 处理一个bunkey
     *
     * @param bucket
     * @return 如果没有子查询返回bunkey中的值[String]
     * 如果有子查询,返回一个对应的map对象[HashMap]--{key:bunketKey,value:子查询返回的map}
     */
    public Object dealOneBunket(Terms.Bucket bucket) {
        Object params = null;
        if (bucket.getAggregations().iterator().hasNext()) {
            params = agg2list(bucket.getAggregations());
        }
        if (params == null) {
            return bucket.getKey();// 没有子查询
        } else if (params instanceof List) {
            List<Object> resultList = (List) params;
            if (resultList.size() == 1) {
                return resultList.get(0);
            } else if (resultList.size() > 1) {
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put(bucket.getKey().toString(), resultList);
                return resultMap;
            } else {
                return resultList;
            }
        } else {
            // 没有子查询
            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put(bucket.getKey().toString(), params);
            return resultMap;
        }
    }

    /**
     * 处理子聚合的方法
     *
     * @param aggregations
     * @return
     * @author of1081
     */
    public Object agg2list(Aggregations aggregations) {
        List<Object> resultList = new ArrayList<>();
        for (Aggregation aggregation : aggregations) {
            if (!(aggregation instanceof InternalAvg)) {
                resultList.add(dealOneAggregation(aggregation));
            }
        }
        return resultList.isEmpty() ? new Object() : resultList.size() == 1 ? resultList.get(0) : resultList;
    }

}

IndexSearchResponse

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import org.elasticsearch.search.SearchHits;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author liangck
 * @version 1.0
 * @since 15/8/12 11:02
 */
public class IndexSearchResponse {

    /**
     * result中的聚合结果
     **/
    private static final String AGGS_RESULT = "aggs";
    /**
     * result中的查询结果数据
     **/
    private static final String DATA_RESULT = "data";

    /**
     * 结果总数
     */
    private int totalCount;

    /**
     * 当前页码
     */
    private int pageNum;

    /**
     * 每页数据条数
     */
    private int pageSize;

    /**
     * 数据结果集
     */
    private Map<String, Object> result = new HashMap<>();

    /**
     * 查询的源结果
     */
    private SearchHits originHits;

    /**
     * 无参数构造器
     */
    public IndexSearchResponse() {
    }

    /**
     * 根据结果纪录总数,当前查询页码,每页数据条数 构造返回结果集
     *
     * @param totalCount 查询到的数据总条数
     * @param pageNum    当前页码
     * @param pageSize   每页数据条数
     */
    public IndexSearchResponse(int totalCount, int pageNum, int pageSize) {
        this(totalCount, pageNum, pageSize, null);
    }

    /**
     * 根据结果纪录总数,当前查询页码,每页数据条数 构造返回结果集
     *
     * @param totalCount 查询到的数据总条数
     * @param pageNum    当前页码
     * @param pageSize   每页数据条数
     * @param result     数据集
     */
    public IndexSearchResponse(int totalCount, int pageNum, int pageSize,
                               Map<String, Object> result) {
        this.totalCount = totalCount;
        this.pageNum = pageNum;
        this.pageSize = pageSize;
        this.result = result;
    }

    /**
     * 放入聚合结果
     *
     * @param aggResult 聚合结果
     */
    public void putResultAggs(Object aggResult) {
        result.put(AGGS_RESULT, aggResult);
    }

    /**
     * 放入查询结果数据
     *
     * @param data 查询结果数据
     */
    public void putReslutData(Object data) {
        result.put(DATA_RESULT, data);
    }

    /**
     * 获取聚合结果
     *
     * @return
     */
    public Object getResultAgg() {
        return result.get(AGGS_RESULT);
    }

    /**
     * 获取查询结果数据
     *
     * @return
     */
    public Object getResultData() {
        return result.get(DATA_RESULT);
    }

    /**
     * 返回结果数据的json字符串,方便进行反序列化
     *
     * @return
     */
    public String getResultDataJsonString() {
        return (result.get(DATA_RESULT) == null) ? null
                : JSON.toJSONString(result.get(DATA_RESULT))
                .replace("[\"{", "[{").replace("}\"]", "}]")
                .replace("\\", "").replace("}\"", "}")
                .replace("\"{", "{");
    }

    /**
     * 返回指定类型的查询数据结果
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> getResultDataBeans(Class<T> clazz) {
        return getResultDataJsonString() == null ? new ArrayList() : JSONArray
                .parseArray(getResultDataJsonString(), clazz);
    }

    public int getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(int totalCount) {
        this.totalCount = totalCount;
    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public Map<String, Object> getResult() {
        return result;
    }

    public void setResult(Map<String, Object> result) {
        this.result = result;
    }

    public SearchHits getOriginHits() {
        return originHits;
    }

    public void setOriginHits(SearchHits originHits) {
        this.originHits = originHits;
    }

    @Override
    public String toString() {
        return "[{totalCount:" + totalCount + "},{pageNum:" + pageNum
                + "},{pageSize:" + pageSize + "}],{result:" + result + "}]";
    }
}

ESDocObject

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ESDocObject {
}

ESField

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ESField {
    /**
     * 字段名称
     *
     * @return
     */
    public String fieldName() default "";

    /**
     * 字段类型,默认为string
     *
     * @return
     */
    public ESFieldType fieldType() default ESFieldType.TEXT;

    /**
     * 分词器,默认不进行分词
     *
     * @return
     */
    ESAnalyzer analyzerType() default ESAnalyzer.not_analyzed;

    /**
     * 是否存储,默认为是
     *
     * @return
     */
    public boolean isStore() default true;
}

EnableSuggest

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EnableSuggest {

    String suggestName() default "suggest";

    /**
     * 分词器
     *
     * @return {@link ESAnalyzer}
     */
    ESAnalyzer analyzerType() default ESAnalyzer.ik_smart;

}

IESMappingType

/**
 * <p>
 * es 文档数据类型,所有定义为es文档的bean 必须实现该接口
 * </p>
 * 
 * @author liangck
 * @version 1.0
 * @since 15/8/9 12:20
 */
public interface IESMappingType {

    /**
     * 生成文档ID
     * @return 文档ID
     */
    String generateDocId();

    /**
     * 返回该文档对象进行索引的field, 用于反射调用生成MapType
     * 
     * @return {@link Field}
     */
    public Field[] foundTypeField();
}

ESAnalyzer

/**
 * of1081_yxd on 2015/1/7. Description: ES 内置分析器
 */
public enum ESAnalyzer {

    /**
     * standard tokenizer, standard filter, lower case filter, stop filter
     */
    standard(),

    /**
     * lower case tokenizer
     */
    simple(),
    /**
     * ik_max_word
     */
    ik_max_word(),

    /**
     * ik_max_word
     */
    ik_smart(),

    /**
     * lower case tokenizer, stop filter
     */
    stop(),

    /**
     * 不分词,内容整体作为一个token(not_analyzed)
     */
    keyword(),

    /**
     * 正则表达式分词,默认匹配\W+
     */
    whitespace(),

    /**
     * 各种语言
     */
    // lang(),

    /**
     * standard tokenizer, standard filter, lower case filter, stop filter,
     * snowball filter
     */
    snowball(),

    /**
     * 不进行索引
     */
    not_analyzed(),

    /**
     * 进行索引
     */
    analyzed(),


    /**
     * Ansj搜索条件分词
     */
    ansj_query(),

    /**
     * Ansj索引文档分词
     */
    ansj_index(),

    /**
     * Ansj智能分词,即索引文档时使用ansj_index,搜索文档时使用ansj_query分词
     */
    ansj_auto(),


    /**
     * 一个Tokenizer, 零个或多个Token Filter, 零个或多个Char Filter
     */
    ESAnalyzer() {
    }
}

ESFieldType

/**
 * of1081_yxd on 2015/1/6. Description:索引-映射结构中字段类型.
 */
public enum ESFieldType {

    /**
     * string 数据类型
     */
    STRING("string"),

    /**
     * string 数据类型
     */
    TEXT("text"),

    /**
     * keyword 全局查询文本类型
     */
    KEYWORD("keyword"),

    /**
     * double 数据类型
     */
    DOUBLE("double"),
    /**
     * nested类型
     */
    NESTED("nested"),

    /**
     * object类型
     */
    OBJECT("object"),

    /**
     * boolean 数据类型
     */
    BOOLEAN("boolean"),

    /**
     * integer 数据类型
     */
    INTEGER("integer"),

    /**
     * date 数据类型
     */
    DATE("date"),

    /**
     * long 数据类型
     */
    LONG("long");

    /**
     * ES中数据类型标识
     */
    private String typeValue;

    ESFieldType(String typeValue) {
        this.typeValue = typeValue;
    }

    public String getTypeValue() {
        return typeValue;
    }
}

IkAnalzyerUtil

@Slf4j
public class IkAnalzyerUtil {

    /**
     * 使用IK分词器 进行分词
     *
     * @param phrase
     * @return
     */
    public static List<String> segmentPhraseByIk(String phrase) {
        if (phrase == null)
            throw new NullPointerException("待分词短语不能为空!");

        // 构建IK分词器,使用smart分词模式
        Analyzer analyzer = null;
        try {


            analyzer = new IKAnalyzer(true);
        } catch (Exception e) {
//            e.printStackTrace();\
            log.error("use IK has error {}", e.getLocalizedMessage());
            return new ArrayList<>();
        }

        //TODO ik分詞有坑
        return segmentPhraseByAnalyzer(analyzer, phrase);
    }

    /**
     * 标准分词器分词 ,中文会被切分为单字
     *
     * @param phrase
     * @return
     */
    public static List<String> segmentPhraseByStandardAnalyzer(String phrase) {
        Analyzer analyzer = new StandardAnalyzer();
        return segmentPhraseByAnalyzer(analyzer, phrase);
    }

    /**
     * 指定分词器分词
     *
     * @param analyzer
     * @param phrase
     * @return
     */
    public static List<String> segmentPhraseByAnalyzer(Analyzer analyzer, String phrase) {

        // 获取Lucene的TokenStream对象
        TokenStream ts = null;

        //最终返回的分词结果
        List<String> terms = new ArrayList<>();
        try {
            ts = analyzer.tokenStream("keywords", new StringReader(phrase));
            // 获取词元位置属性
            OffsetAttribute offset = ts.addAttribute(OffsetAttribute.class);
            // 获取词元文本属性
            CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);
            // 获取词元文本属性
            TypeAttribute type = ts.addAttribute(TypeAttribute.class);

            // 重置TokenStream(重置StringReader)
            ts.reset();
            // 迭代获取分词结果
            while (ts.incrementToken()) {
                log.debug(offset.startOffset() + " - " + offset.endOffset() + " : " + term.toString() + " | " + type.type());
//                System.out.println(offset.startOffset() + " - " + offset.endOffset() + " : " + term.toString() + " | " + type.type());
                //放入词
                terms.add(term.toString());
            }
            // 关闭TokenStream(关闭StringReader)
            ts.end();

        } catch (Exception e) {
            log.error(e.getLocalizedMessage(), e);
            return new ArrayList<>();
        } finally {
            // 释放TokenStream的所有资源
            if (ts != null) {
                try {
                    ts.close();
                } catch (IOException e) {
                    log.error(e.getLocalizedMessage(), e);
                }
            }
        }
        //放入该短语
//        terms.add(phrase);
        return terms;
    }

}
/**
     * 查询条件封装
     * @param form
     * @return
     */
    private SearchSourceBuilder  searchMallEsCondition(MallEsSearchFormDTO form) {
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 关键词查询[商品名称/货品名称/品牌名称]
//        if (StringUtils.isNotBlank(form.getKeywords())) {
//            List<String> analyzedWords = IkAnalzyerUtil.segmentPhraseByIk(form.getKeywords());
//            for (String word : analyzedWords) {
//                boolQuery.must(queryStringQuery(String.format("*%s*", word.trim())).field("goodsName").field("goodsInfoName"));
//            }
//        }
        if (StringUtils.isNotBlank(form.getKeywords())) {
            boolQuery.must(QueryBuilders.multiMatchQuery(form.getKeywords(),"goodsInfoName","goodsName").operator(Operator.AND));
        }
        // 指定货品编码参数组合查询
//        List<String> goodsInfoItemNos = form.getGoodsInfoItemNos();
//        if (!ListHelper.isObjectNullOrEmpty(goodsInfoItemNos) && goodsInfoItemNos.size() > 0) {
//            BoolQueryBuilder includeBoolQuery = QueryBuilders.boolQuery();
//            for (String goodsInfoItemNo : goodsInfoItemNos) {
//                BoolQueryBuilder inBoolQuery = QueryBuilders.boolQuery();
//                inBoolQuery.must(matchQuery("goodsInfoItemNo", goodsInfoItemNo));
//                includeBoolQuery.should(inBoolQuery);
//            }
//            boolQuery.must(includeBoolQuery);
//        }

        //商品名称模糊查询
        if (StringUtils.isNotEmpty(form.getGoodsName())) {
            //boolQuery.must(queryStringQuery(String.format("*%s*", form.getGoodsName())).field("goodsName"));
            boolQuery.must(queryStringQuery(String.format("\"*%s*\" OR *%s*", form.getGoodsName(), form.getGoodsName())).field("goodsName"));
        }

        //货品名称模糊查询
        if (StringUtils.isNotEmpty(form.getGoodsInfoName())) {
            //boolQuery.must(queryStringQuery(String.format("*%s*", form.getGoodsInfoName())).field("goodsInfoName"));
            boolQuery.must(queryStringQuery(String.format("\"*%s*\" OR *%s*", form.getGoodsInfoName(), form.getGoodsInfoName())).field("goodsInfoName"));
        }

        if (!StringHelper.isNullOrEmpty(form.getPromotionFlag())&&"1".equals(form.getPromotionFlag())){
            boolQuery.must(QueryBuilders.nestedQuery("marketingActivityList",QueryBuilders.existsQuery("marketingActivityList"), ScoreMode.None));
            searchSourceBuilder.sort("activityGoodsSort",SortOrder.ASC);
        }

        if (!StringHelper.isNullOrEmpty(form.getPromotionGrade())){
            boolQuery.must(QueryBuilders.nestedQuery("marketingActivityList",QueryBuilders.queryStringQuery(String.format("*%s*", form.getPromotionGrade())).field("marketingActivityList.marketJoinGrade"), ScoreMode.None));
        }

        if (!StringHelper.isNullOrEmpty(form.getShowChannel())){
            boolQuery.must(QueryBuilders.nestedQuery("marketingActivityList",QueryBuilders.queryStringQuery(String.format("*%s*", form.getShowChannel())).field("marketingActivityList.showChannel"), ScoreMode.None));
        }

        if (!StringHelper.isNullOrEmpty(form.getShowStock())&&"0".equals(form.getShowStock())){

            RangeQueryBuilder goodsInfoStock = rangeQuery("goodsInfoStock")
                    .gt(0);
            boolQuery.must(goodsInfoStock);
        }

        /******查询是否上架商品********/
        if (StringUtil.isNotEmptyOrWhiteSpace(form.getGoodsInfoAdded())) {
            /****-1的时候表示全部****/
            if (!form.getGoodsInfoAdded().equals("-1")) {
                //默认查询是要在列表展示的
                if (form.getKeywords() != null) {
                    // 搜索是否上架的商品
                    boolQuery.must(QueryBuilders.termQuery("goodsInfoAdded", form.getGoodsInfoAdded()));
                } else {
                    if (form.isShowList()) {
                        // 搜索是否上架的商品
                        boolQuery.must(QueryBuilders.termQuery("goodsInfoAdded", form.getGoodsInfoAdded()))
                                // 是否列表显示
                                .must(QueryBuilders.termQuery("showList", "1"));
                    } else {
                        // 搜索是否上架的商品
                        boolQuery.must(QueryBuilders.termQuery("goodsInfoAdded", form.getGoodsInfoAdded()));
                    }
                }
            }
        } else {
            //默认查询是要在列表展示的
            if (form.getKeywords() != null) {
                boolQuery.must(QueryBuilders.termQuery("goodsInfoAdded", "1"));
            } else {
                if (form.isShowList()) {
                    // 搜索上架商品
                    boolQuery.must(QueryBuilders.termQuery("goodsInfoAdded", "1"))
                            // 是否列表显示
                            .must(QueryBuilders.termQuery("showList", "1"));
                } else {
                    // 搜索上架商品
                    boolQuery.must(QueryBuilders.termQuery("goodsInfoAdded", "1"));
                }
            }
        }

        //灰度上架:0=否;1=是
        if (!StringUtil.isNotEmptyOrWhiteSpace(form.getAlpha())) {
            //未设置,默认不看灰度上架货品
            boolQuery.must(QueryBuilders.termQuery("alpha", "0"));
        } else {
            if (form.getAlpha().equals("1")) {
                //能看灰度发布商品,不加条件筛选
            } else {
                boolQuery.must(QueryBuilders.termQuery("alpha", "0"));
            }
        }

        //Spu展示标记
        if (StringUtil.isNotEmptyOrWhiteSpace(form.getDisplaySpuFlag())) {
            boolQuery.must(QueryBuilders.termQuery("displaySpuFlag", form.getDisplaySpuFlag()));
        }

        //查询品牌id
        if (null != form.getBrandId() && form.getBrandId() > 0L) {
            boolQuery.must(termQuery("brand.brandId", form.getBrandId()));
        }

        //分类id
        if (null != form.getCatId() && form.getCatId() > 0L) {
            boolQuery.must(termQuery("catId", form.getCatId()));
        }

        // 指定商品ID查询
        if (null != form.getGoodsId() && form.getGoodsId() > 0L) {
            boolQuery.must(termQuery("goodsId", form.getGoodsId()));
        }
        // 多个商品ID查询
        if (!ListHelper.isNullOrEmpty(form.getGoodsIds())) {
            boolQuery.must(QueryBuilders.termsQuery("goodsId", form.getGoodsIds()));
        }
        // 指定货品ID查询
        if (null != form.getGoodsInfoId() && form.getGoodsInfoId() > 0L) {
            boolQuery.must(termQuery("goodsInfoId", form.getGoodsInfoId()));
        }
        // 多个货品ID查询
        if (!ListHelper.isNullOrEmpty(form.getGoodsInfoIds())) {
            boolQuery.must(QueryBuilders.termsQuery("goodsInfoId", form.getGoodsInfoIds()));
        }
        // 指定货品编码查询
        if (StringUtils.isNotEmpty(form.getGoodsInfoItemNo())) {
            boolQuery.must(matchQuery("goodsInfoItemNo", form.getGoodsInfoItemNo()));
        }

        // 多个货号询
        if (!ListHelper.isNullOrEmpty(form.getGoodsInfoItemNos())) {
            boolQuery.must(QueryBuilders.termsQuery("goodsInfoItemNo", form.getGoodsInfoItemNos()));
        }

        // 指定商品编码查询
        if (StringUtils.isNotEmpty(form.getGoodsNo())) {
            boolQuery.must(termQuery("goodsNo", form.getGoodsNo()));
        }

        // 多个商品编码
        if (!ListHelper.isNullOrEmpty(form.getGoodsNos())) {
            boolQuery.must(QueryBuilders.termsQuery("goodsNo", form.getGoodsNos()));
        }

        // 分类查询
        if (ArrayUtils.isNotEmpty(form.getCids()) && form.isVisitGcpt()) {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            boolQueryBuilder.must(QueryBuilders.termsQuery("cateList.id", form.getCids()));
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("cateList", boolQueryBuilder, ScoreMode.None);
            boolQuery.must(nestedQueryBuilder);
        }

        //剔除生产配套分类
        if (ArrayUtils.isNotEmpty(form.getCids()) && !form.isVisitGcpt()) {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            boolQueryBuilder.mustNot(QueryBuilders.termsQuery("cateList.id", form.getCids()));
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("cateList", boolQueryBuilder, ScoreMode.None);
            boolQuery.must(nestedQueryBuilder);
        }

        // 品牌查询
        if (ArrayUtils.isNotEmpty(form.getBrands())) {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            boolQueryBuilder.must(QueryBuilders.termsQuery("brand.brandName", form.getBrands()));
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("brand", boolQueryBuilder, ScoreMode.None);
            boolQuery.must(nestedQueryBuilder);
        }

        // 扩展参数
        if (ArrayUtils.isNotEmpty(form.getParams())) {
            for (String param : form.getParams()) {
                String[] paramArr = param.split(":");
                if ("价格".equals(paramArr[0]) && paramArr.length > 1 && StringUtils.isNotEmpty(paramArr[1])) {
                    long[] prices = Arrays.stream(paramArr[1].split("-")).mapToLong(a -> {
                        try {
                            return Long.parseLong(a);
                        } catch (Exception e) {
                            log.error("商品根据价格查询出现异常", e);
                            return 0L;
                        }
                    }).toArray();
                    RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("goodsInfoPreferPrice");
                    rangeQuery.gte(prices[0]);
                    rangeQuery.lte(prices.length > 1 ? prices[1] : 0);
                    boolQuery.filter(rangeQuery);
                } else {
                    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                    boolQueryBuilder.must(QueryBuilders.termQuery("paramList.attributeName", paramArr[0]));
                    boolQueryBuilder.must(QueryBuilders.termQuery("paramList.attributeValue", paramArr[1]));
                    NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("paramList", boolQueryBuilder, ScoreMode.None);
                    boolQuery.must(nestedQueryBuilder);
                }
            }
        }

        // 只显示有货
        if (Objects.nonNull(form.getShowStock()) && "0".equals(form.getShowStock())) {
//            searchRequest.addFilter(FilterBuilders.scriptFilter(filter_script)
//                    .addParam(CHECKWARE, Objects.isNull(form.getWareIds()) ? null : form.getWareIds()[0]));
        }




        if (form.getGroupGoodsId()){
            CollapseBuilder collapseBuilder = new CollapseBuilder("goodsId");

            searchSourceBuilder.collapse(collapseBuilder);
        }






        // 排序
        if (StringUtils.isNotBlank(form.getSort())) {
            switch (form.getSort()) {
                // 价格升序
                case "11D":
//                    Script script = new Script(sort_script);
//                    ScriptSortBuilder scriptSortBuilder = SortBuilders.scriptSort(script, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.ASC);
//                    searchSourceBuilder.sort(scriptSortBuilder);
                    searchSourceBuilder.sort("goodsInfoMarketPrice",SortOrder.ASC);
                    break;
                // 价格降序
                case "1D":
                    searchSourceBuilder.sort("goodsInfoMarketPrice",SortOrder.DESC);
                    break;
                // 销量降序
                case "2D":

                    //默认过滤售罄的货品,三大专区,热销
                    BoolQueryBuilder includeBoolQuery = QueryBuilders.boolQuery();

                    //库存大于0
                    RangeQueryBuilder goodsInfoStock = rangeQuery("goodsInfoStock")
                            .gt(0);
                    //允许超卖
                    includeBoolQuery.should(QueryBuilders.termQuery("overSold", "1"));
                    includeBoolQuery.should(goodsInfoStock);

                    boolQuery.must(includeBoolQuery);
                    searchSourceBuilder.sort("mallSales", SortOrder.DESC);
                    searchSourceBuilder.sort("goodsInfoAddedTime",SortOrder.DESC);
                    break;
                // 销量升序
                case "22D":
                    searchSourceBuilder.sort("mallSales", SortOrder.ASC);
                    searchSourceBuilder.sort("goodsInfoAddedTime",SortOrder.ASC);
                    break;
                // 创建时间升序
                case "33D":
                    searchSourceBuilder.sort("createDate", SortOrder.ASC);


                    break;
                // 创建时间降序
                case "3D":
                    searchSourceBuilder.sort("createDate", SortOrder.DESC);
//                    searchRequest.addSort(new ScriptSortBuilder(date_sort_script, "number")
//                            .order(SortOrder.DESC));
//                    searchRequest.addSort(new ScriptSortBuilder(stock_sort_script, "number")
//                            .order(SortOrder.DESC));
                    break;
                // 收藏升序
                case "44D":
//                    searchRequest.addSort("collectionCount", SortOrder.ASC);
                    break;
                // 收藏降序
                case "4D":
//                    searchRequest.addSort("collectionCount", SortOrder.DESC);
                    break;
                // 上架时间升序
                case "55D":
                    searchSourceBuilder.sort("goodsInfoAddedTime",SortOrder.ASC);
                    break;
                // 上架时间降序
                case "5D":
                    searchSourceBuilder.sort("goodsInfoAddedTime",SortOrder.DESC);
                    break;
                // 评论数升序
                case "66D":
//                    searchRequest.addSort("comment.commentCount", SortOrder.ASC);
                    break;
                // 评论数降序
                case "6D":
//                    searchRequest.addSort("comment.commentCount", SortOrder.DESC);
                    break;
                case "7D":
                    String marketJoinGrade = getMarketJoinGrade(form.getPriceGradeId());
                    //按照促销活动排序
                    if (!StringHelper.isNullOrEmpty(marketJoinGrade)){
                        Map<String, Object> params = new HashMap<>();
                        params.put("zero",0);
                        params.put("one",1);
                        params.put("nullCode",null);
                        params.put("emptyStr","");
                        params.put("marketJoinGrade",marketJoinGrade);
                        Script marketJoinGradeScript = new Script(ScriptType.INLINE,"painless",marketing_sort,params);
                        ScriptSortBuilder gradeScriptSortBuilder = SortBuilders.scriptSort(marketJoinGradeScript, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.DESC);
                        searchSourceBuilder.sort(gradeScriptSortBuilder);
                    }else {
                        Map<String, Object> params = new HashMap<>();
                        params.put("zero",0);
                        params.put("one",1);
                        params.put("marketJoinGrade",marketJoinGrade);
                        params.put("nullCode",null);
                        params.put("emptyStr","");
                        Script marketJoinGradeScript = new Script(ScriptType.INLINE,"painless",no_login_marketing_sort,params);
                        ScriptSortBuilder gradeScriptSortBuilder = SortBuilders.scriptSort(marketJoinGradeScript, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.DESC);
                        searchSourceBuilder.sort(gradeScriptSortBuilder);
                    }
                    //按照商品名称带关键字货品
                    searchSourceBuilder.sort("weight",SortOrder.DESC);
                    //按照库存排序
                    Map<String, Object> stockParams = new HashMap<>();
                    stockParams.put("zero",0);
                    stockParams.put("minusOne",-1);
                    stockParams.put("zeroStr","0");
                    stockParams.put("oneStr","1");
                    Script stockScript = new Script(ScriptType.INLINE, "painless", stock_sort, stockParams);
                    ScriptSortBuilder stockOrder = SortBuilders.scriptSort(stockScript, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.DESC);
                    searchSourceBuilder.sort(stockOrder);

//                    searchSourceBuilder.sort("cat1Sort",SortOrder.ASC);
//                    searchSourceBuilder.sort("cat2Sort",SortOrder.ASC);
//                    searchSourceBuilder.sort("cat3Sort",SortOrder.ASC);
                    searchSourceBuilder.sort("goodsInfoAddedTime",SortOrder.DESC);
                    break;
                //根据价格升序
                case "88D":{
                /*    if (StringHelper.isNullOrEmpty(form.getPriceGradeId())){
                        break;
                    }
                    String sortField = getPriceLevel(form.getPriceGradeId());
                    Script script2 = new Script("def price=_source."+sortField+"; if(price<0){return 0;}else{return price;}");
                    ScriptSortBuilder scriptSortBuilder2 = SortBuilders.scriptSort(script2, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.ASC);
                    searchSourceBuilder.sort(scriptSortBuilder2);*/

                    searchSourceBuilder.sort("goodsInfoMarketPrice",SortOrder.ASC);
                }
                break;
                //根据价格降序
                case "8D": {
         /*           if (StringHelper.isNullOrEmpty(form.getPriceGradeId())){
                        break;
                    }
                    String sortField = getPriceLevel(form.getPriceGradeId());
                    Script script3 = new Script("def price=_source."+sortField+"; if(price<0){return 0;}else{return price;}");
                    ScriptSortBuilder scriptSortBuilder3 = SortBuilders.scriptSort(script3, ScriptSortBuilder.ScriptSortType.NUMBER).order(SortOrder.DESC);
                    searchSourceBuilder.sort(scriptSortBuilder3);*/

                    searchSourceBuilder.sort("goodsInfoMarketPrice",SortOrder.DESC);
                }
                break;
                default:
                    break;
            }
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if(form.getGoodsInfoAddedTimeSort() != null && form.getGoodsInfoAddedTimeSort() == 0){
            boolQuery.must(termQuery("goodsInfoAdded", "1"));
            searchSourceBuilder.sort(sdf.format(form.getGoodsInfoAddedTime()),SortOrder.DESC);
        }else if(form.getGoodsInfoAddedTimeSort() != null && form.getGoodsInfoAddedTimeSort() == 1){
            boolQuery.must(termQuery("goodsInfoAdded", "1"));
            searchSourceBuilder.sort(sdf.format(form.getGoodsInfoAddedTime()),SortOrder.ASC);
        }
        searchSourceBuilder.query(boolQuery);

        AggregationBuilder aggregation =
                AggregationBuilders.nested("paramList","paramList")
                        .subAggregation(AggregationBuilders.terms("attributeName")
                                .field("paramList.attributeName").size(Integer.MAX_VALUE)
                                .subAggregation(AggregationBuilders.terms("attributeValue").field("paramList.attributeValue"))
                        );

        AggregationBuilder aggregation2 =
                AggregationBuilders.nested("brand","brand")
                        .subAggregation(AggregationBuilders.terms("brandName").field("brand.brandName").size(Integer.MAX_VALUE)
                                .subAggregation(AggregationBuilders.terms("brandLogo").field("brand.brandLogo"))
                        );
        searchSourceBuilder.aggregation(aggregation);
        searchSourceBuilder.aggregation(aggregation2);
        searchSourceBuilder.from((form.getPageNo() - 1) * form.getPageSize()).size(form.getPageSize());

        return searchSourceBuilder;
    }
/**
     * 处理聚合数据,并且过滤掉已选的参数
     *
     * @param resultMap
     * @param aggResult
     * @param brandArr  已选的品牌
     * @param paramArr  已选的扩展参数
     */
    private void processAggResult(Map<String, Object> resultMap, Object aggResult, String[] brandArr, String[] paramArr) {

        if (null == aggResult) {
            return;
        }
        // 将聚合结果转化为可操作的json对象
        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(aggResult));
        // 处理扩展参数
        if (jsonObject.containsKey(PARAMLIST)) {
            List<com.swj.api.zagoods.service.searchplatform.vo.ExpandParamVo> paramVoList = new ArrayList<>();
            Object params = jsonObject.get(PARAMLIST);
            JSONArray paramArray;
            if (params instanceof JSONArray) {
                // 转化为jsonArray
                paramArray = (JSONArray) params;
            } else {
                //只有一个param,构造array
                paramArray = new JSONArray(Arrays.asList((Object) params));
            }
            for (Iterator<?> ite = paramArray.iterator(); ite.hasNext(); ) {
                Object paramObj = ite.next();
                if (paramObj instanceof JSONObject) {
                    JSONObject paramJson = (JSONObject) paramObj;
                    for (Map.Entry<String, Object> entry : paramJson.entrySet()) {
                        // 判断当前的扩展参数是否与已选的相同
                        boolean selected = false;
                        if (paramArr != null && paramArr.length > 0) {
                            for (String paramSelected : paramArr) {
                                if (paramSelected.split(":")[0].equals(entry.getKey())) {
                                    selected = true;
                                    break;
                                }
                            }
                        }
                        // 未选中,则放入聚合结果
                        if (!selected) {
                            com.swj.api.zagoods.service.searchplatform.vo.ExpandParamVo paramVo = new com.swj.api.zagoods.service.searchplatform.vo.ExpandParamVo(entry.getKey());
                            if (entry.getValue() instanceof List) {
                                List<String> paramValues = (List<String>) entry.getValue();
                                for (String paramValue : paramValues) {
                                    paramVo.addParamValue(new com.swj.api.zagoods.service.searchplatform.vo.ParamValueVo(paramValue));
                                }
                            } else {
                                paramVo.addParamValue(new com.swj.api.zagoods.service.searchplatform.vo.ParamValueVo(entry.getValue().toString()));
                            }
                            //根据double值对扩展参数值数组进行排序
                            sortParamValWithDoubleVal(paramVo);
                            paramVoList.add(paramVo);
                        }
                    }
                }
            }
            resultMap.put("params", paramVoList);
        }
        // 处理品牌
        if (jsonObject.containsKey(BRAND)) {
            List<com.swj.api.zagoods.service.searchplatform.vo.BrandVo> brandVoList = new ArrayList<>();
            // 获取品牌信息
            Object brandObj = jsonObject.get(BRAND);
            // 已选择的品牌转换为列表
            List<String> brandList;
            if (brandArr == null || brandArr.length == 0) {
                brandList = new ArrayList<>();
            } else {
                brandList = Arrays.asList(brandArr);
            }

            if (brandObj instanceof JSONArray) {
                JSONArray brands = (JSONArray) brandObj;
                for (Object branObj : brands.toArray()) {
                    JSONObject jsonObj = (JSONObject) branObj;
                    for (Map.Entry<String, Object> entry : jsonObj.entrySet()) {
                        if (entry.getKey() != null && !brandList.contains(String.valueOf(entry.getKey()))) {
                            brandVoList.add(new com.swj.api.zagoods.service.searchplatform.vo.BrandVo(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())));
                        }
                    }
                }
            } else if (brandObj instanceof JSONObject) {
                JSONObject brand = (JSONObject) brandObj;
                for (Map.Entry<String, Object> entry : brand.entrySet()) {
                    if (entry.getKey() != null && !brandList.contains(String.valueOf(entry.getKey()))) {
                        brandVoList.add(new com.swj.api.zagoods.service.searchplatform.vo.BrandVo(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())));
                    }
                }
            } else {
                if (!brandList.contains(brandObj.toString())) {
                    brandVoList.add(new com.swj.api.zagoods.service.searchplatform.vo.BrandVo(brandObj.toString()));
                }
            }

            resultMap.put("brands", brandVoList);
        }
    }