package com.ruoyi.biemo.elasticsearch.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.biemo.business.domain.DocInfo;
import com.ruoyi.biemo.core.page.Page;
import com.ruoyi.biemo.elasticsearch.annotation.EsId;
import com.ruoyi.biemo.elasticsearch.entity.FieldMapping;
import com.ruoyi.biemo.elasticsearch.function.GFunction;
import com.ruoyi.biemo.mongodb.config.Constant;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.exception.ServiceException;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.util.EntityUtils;
import org.apache.poi.ss.formula.functions.T;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeAction;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequestBuilder;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.*;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.elasticsearch.annotations.Document;

import javax.print.Doc;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author hjb
 * @version 1.0
 * @date 2022/8/30 10:47
 */
public abstract class EsService<T> {

    protected  Logger logger = LoggerFactory.getLogger(getClass());

    // ES 默认最大返回 10000 与 scroll查询有关
    int MAX_RESULT_WINDOW = 10000;
    // 批量构建时的每次从数据库查询数量
    int BATCH_SIZE = 1000;
    // 加载数据超过200 时用批量加载 减少数据库连接
    int BATCH_LOAD_SIZE = 200;
    // ES 滚动查询 有效时间 与 scroll查询有关
    TimeValue SCROLL_TIME =  TimeValue.timeValueMinutes(1);


    private String index;

    private Class<T> entity;

    private Method getId;

    protected abstract T loadData(SearchSourceBuilder context,SearchHit hit);

    /**
     * 批量加载数据
     * @param context
     * @param hitArr
     * @return
     */
    public abstract List<T> batchLoadData(SearchSourceBuilder context,SearchHit[] hitArr);


    /**
     * 索引设置
     */
    protected Settings.Builder createSettings(int shards, int replicas){
        // 设置分片数和副本数
        return Settings.builder().put("index.number_of_shards", shards).put("index.number_of_replicas", replicas);
    }

    protected EsService() {
        Type type = this.getClass().getGenericSuperclass();
        ParameterizedType parameterizedType = (ParameterizedType) type;
        Type[] t = parameterizedType.getActualTypeArguments();
        this.entity = (Class<T>) t[0];
        if (this.entity.isAnnotationPresent(Document.class)) {
            this.index = this.entity.getAnnotation(Document.class).indexName();
        }
        Field[] fields = this.entity.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(EsId.class)) {
                String fieldName = field.getName();
                String FieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                String methodName = GFunction.get + FieldName;
                Method getMethod = null;
                try {
                    getMethod = this.entity.getMethod(methodName, null);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
                this.getId = getMethod;
            }
        }
    }
    /**
     * 创建mapping
     * @param
     * @param
     * @param clazz    索引类型
     */
    public  boolean createIndexAndCreateMapping( Class clazz, boolean rebuild, int number_of_shards, int number_of_replicas) {
        RestHighLevelClient client = null;
        try{
            client = ElasticSearchPoolUtil.getClient();
            if (indexExist()) {
                if(rebuild){
                    AcknowledgedResponse deleteIndexResponse = client.indices().delete(new DeleteIndexRequest(index), RequestOptions.DEFAULT);
                    if(deleteIndexResponse.isAcknowledged()){
                        return createIndexAndCreateMapping(index,ElasticSearchUtils.getFieldInfo(clazz), number_of_shards, number_of_replicas);
                    }else{
                        logger.error("删除旧索引数据失败，创建索引mapping失败");
                        return false;
                    }
                }else{
                    logger.info("不需要删除旧的索引，没有进一步创建索引结构哦~");
                    return true;
                }
            }else{
                return createIndexAndCreateMapping(index,ElasticSearchUtils.getFieldInfo(clazz), number_of_shards, number_of_replicas);
            }
        }catch (Exception e){
            throw new RuntimeException();
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
    }

    /**
     * 根据信息自动创建索引与mapping
     * 构建mapping描述
     * @param index 索引名称
     * @param
     * @param fieldMappingList  字段信息
     * @param
     * @param number_of_shards 分片数
     * @param number_of_replicas 副本数
     * @return
     */
    public boolean createIndexAndCreateMapping(String index, List<FieldMapping> fieldMappingList, int number_of_shards, int number_of_replicas) {
        XContentBuilder mapping = null;
        RestHighLevelClient client = null;
        try {
            mapping = XContentFactory.jsonBuilder()
                    .startObject()
                    .startObject("_source").field("enabled","false").endObject()
                    .startObject("properties"); //设置自定义字段
            for(FieldMapping info : fieldMappingList){
                String field = info.getField();
                String dateType = info.getType();
                if(dateType == null || "".equals(dateType.trim())){
                    dateType = "string";
                }
                dateType = dateType.toLowerCase();
                int participle = info.getParticiple();
                if("string".equals(dateType)){
                    if(participle == 0){
                        mapping.startObject(field)
                                .field("type","keyword")
                                .field("index", false)
                                .field("ignore_above", info.getIgnoreAbove())
                                .endObject();
                    } else if(participle == 1) {
                        mapping.startObject(field)
                                .field("type","text")
                                .field("analyzer","hanlp")
                                .endObject();
                    }else if(participle == 2){
                        mapping.startObject(field)
                                .field("type","text")
                                .field("analyzer","hanlp_standard")
                                .endObject();
                    }else if(participle == 3){
                        mapping.startObject(field)
                                .field("type","text")
                                .field("analyzer","hanlp_index")
                                .endObject();
                    }else if(participle == 4){
                        mapping.startObject(field)
                                .field("type","text")
                                .field("analyzer","hanlp_nlp")
                                .endObject();
                    }else if(participle == 5){
                        mapping.startObject(field)
                                .field("type","text")
                                .field("analyzer","hanlp_n_short")
                                .endObject();
                    }else if(participle == 6){
                        mapping.startObject(field)
                                .field("type","text")
                                .field("analyzer","hanlp_crf")
                                .endObject();
                    }else if(participle == 7){
                        mapping.startObject(field)
                                .field("type","text")
                                .field("analyzer","hanlp_speed")
                                .endObject();
                    }
                }else if("datetime".equals(dateType)){
                    mapping.startObject(field)
                            .field("type","date")
                            .field("format","yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis")
                            .endObject();
                }else if ("timestamp".equals(dateType)){
                    mapping.startObject(field)
                            .field("type","date")
                            .field("format","strict_date_optional_time||epoch_millis")
                            .endObject();
                }else if("float".equals(dateType)||"double".equals(dateType)){
                    mapping.startObject(field)
                            .field("type","scaled_float")
                            .field("scaling_factor",100)
                            .endObject();
                }else {
                    mapping.startObject(field)
                            .field("type",dateType)
                            .field("index",true)
                            .endObject();
                }
            }
            mapping.endObject()
                    .endObject();
            CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
            // 设置分片数和副本数
            createIndexRequest.settings(createSettings(number_of_shards,number_of_replicas));
            // 构建对应index的mapping
            createIndexRequest.mapping(mapping);
            client = ElasticSearchPoolUtil.getClient();
            // 发送创建index的请求
            CreateIndexResponse createIndexResponse = client.indices().create(createIndexRequest,RequestOptions.DEFAULT);
            return createIndexResponse.isAcknowledged();
        } catch (Exception e) {
            logger.error("根据信息自动创建索引与mapping创建失败，失败信息为:"+ e.getMessage());
            throw new RuntimeException();
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
    }



    public boolean indexExist() throws Exception{
        RestHighLevelClient client = null;
        GetIndexRequest request = new GetIndexRequest(index);
        request.local(false);
        request.humanReadable(true);
        request.includeDefaults(false);
        client = ElasticSearchPoolUtil.getClient();
        boolean result = client.indices().exists(request, RequestOptions.DEFAULT);
        ElasticSearchPoolUtil.returnClient(client);
        return result;
    }

    public void insertOrUpdateOne(T entity) {
        IndexRequest request = new IndexRequest(index);
        RestHighLevelClient client = null;
        try {
            request.id(this.getId.invoke(entity, null).toString());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        request.source(JSON.toJSONString(entity), XContentType.JSON);
        try {
            client = ElasticSearchPoolUtil.getClient();
            client.index(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
    }

    public void insertBatch(List<T> list) {
        if(list==null||list.size()==0){
            return;
        }
        RestHighLevelClient client = null;
        try {
            client = ElasticSearchPoolUtil.getClient();
            if(!indexExist()){
                createIndexAndCreateMapping(list.get(0).getClass(),true,1,0);
            }
            BulkRequest request = new BulkRequest();
            list.forEach(item -> {
                try {
                    request.add(new IndexRequest(index).id(getId.invoke(item, null).toString())
                            .source(JSON.toJSONString(item), XContentType.JSON));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            });
            client.bulk(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
    }

    public void deleteBatch(List<String> idList) {
        BulkRequest request = new BulkRequest();
        idList.forEach(item -> request.add(new DeleteRequest(index, item)));
        RestHighLevelClient client = null;
        try {
            client = ElasticSearchPoolUtil.getClient();
            client.bulk(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
    }
    public List<T> search(SearchSourceBuilder builder) {
        SearchRequest request = new SearchRequest(index);
        request.source(builder);
        RestHighLevelClient client = null;
        try {
            client = ElasticSearchPoolUtil.getClient();
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            List<T> res = new ArrayList<>(hits.length);
            for (SearchHit hit : hits) {
                //res.add(JSON.parseObject(hit.getSourceAsString(), entity));
                res.add(loadData(builder,hit));
            }
            return res;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
    }

    //分页查询 根据需要返回自定义加载数据 比如从mongodb mysql 等
    public Page<T> searchPage(SearchSourceBuilder builder,boolean searchFrom) {
        try {
            List<SearchHit> content = new ArrayList<>();
            Long total = search(content,builder);
            List<T> res = new ArrayList<>();
            if(searchFrom){
                res = sequenceLoadData(content);
            }else{
                res = esLoadData(content);
            }
            Integer pageNo = builder.from();
            Page<T> page=new Page<>(pageNo<=0?1:pageNo/builder.size()+1,builder.size());
            page.setRows(res);
            page.setTotal(total);
            return page;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected List<T> sequenceLoadData(List<SearchHit> content){
        return content.stream().map(x -> loadData(null, x)).filter(x -> x != null).collect(Collectors.toList());
    }
    protected List<T> esLoadData(List<SearchHit> content){
        return content.stream().map(x -> JSON.parseObject(x.getSourceAsString(), entity)).filter(x -> x != null).collect(Collectors.toList());
    }
    protected long search(List<SearchHit> content, SearchSourceBuilder searchSourceBuilder){
        // 用于判断 是否 返回所有数据
        boolean flag = true;
        // 总数量
        long total = 0;
        // 分页大小
        int pageSize = searchSourceBuilder.size();
        // 分页页码
        int pageNo = searchSourceBuilder.from();
        // 已经处理过了
        int from = pageNo;
        // 待查询页面内第一条记录的下标
        int firstRowNum = from + 1 ;
        // 最后一行
        int lastRowNum = from + pageSize;
        // 创建查询请求
        SearchRequest searchRequest = new SearchRequest(index);
        // 构建SearchSourceBuilder
        searchRequest.source(searchSourceBuilder);
        // 不分页 最多返回10000条
        if(pageNo==0 && pageSize==0){
            searchSourceBuilder.from(0).size(MAX_RESULT_WINDOW);
        }else{
            // 分页
            // 小于  MAX_RESULT_WINDOW  10000
            if(lastRowNum<=MAX_RESULT_WINDOW){
                searchSourceBuilder.from(from).size(pageSize);
                flag = false;
            }
        }
        SearchResponse searchResponse;
        RestHighLevelClient client = null;
        try {
            client = ElasticSearchPoolUtil.getClient();
            // 查询数据超过第10000
            if (lastRowNum > MAX_RESULT_WINDOW) {
                // 滚动搜索
                total =  searchScroll(content,firstRowNum,lastRowNum,searchSourceBuilder,searchRequest);
            }else{
                searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
                SearchHits hits = searchResponse.getHits();
                total  = hits.getTotalHits().value;
                // 不分页情况下 总条数超过10000
                if(total>MAX_RESULT_WINDOW&&flag){
                    searchScroll(content,firstRowNum,(int)total,searchSourceBuilder,searchRequest);
                }else{
                    content = loadDataByResponse(content, searchResponse);
                }
            }
        } catch (Exception e) {
            logger.error("查询索引错误，错误如下：{}",e);
            throw new CustomException("查询索引出错",e);
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
        return total;
    }


    /**
     * 通过响应加载hit数据
     * @param searchHitList
     * @param searchResponse
     * @return
     */
    private List<SearchHit> loadDataByResponse(List<SearchHit> searchHitList, SearchResponse searchResponse) {
        SearchHits hits = searchResponse.getHits();
        // 条数过多 批量加载 减少数据库连接
        SearchHit[] hitArr = hits.getHits();
        if(Objects.nonNull(hitArr)&&hitArr.length>=BATCH_LOAD_SIZE){
            searchHitList.addAll(Arrays.asList(hitArr));
        }else{
            //逐条加载
            for (SearchHit hit : hitArr) {

                String id=hit.getId();
                if (StringUtils.isBlank(id)){
                    continue;
                }
                searchHitList.add(hit);
            }
        }
        return searchHitList;
    }


    /**
     * scroll 查询 返回总条数
     * @param searchHitList
     * @param firstRowNum
     * @param lastRowNum
     * @param searchSourceBuilder
     * @param searchRequest
     * @return
     */
    private long searchScroll(List<SearchHit> searchHitList,int firstRowNum,int lastRowNum,SearchSourceBuilder searchSourceBuilder,SearchRequest searchRequest){
        long total = 0L;
        SearchResponse searchResponse;
        // 初始位置
        int startPosition;
        // 结束位置
        int endPosition;
        // 页面大小
        int pageSize = lastRowNum-firstRowNum+1;
        // 第一条数据所在滚动指针
        int firstScrollCursor = firstRowNum/MAX_RESULT_WINDOW;
        // 最后一条数据所在指针
        int lastScrollCursor = lastRowNum/MAX_RESULT_WINDOW;
        // 滚动次数
        int scrollCount = 0;
        // 滚动id
        String scrollId;
        RestHighLevelClient client = null;
        try {
            client = ElasticSearchPoolUtil.getClient();
            searchSourceBuilder.size(MAX_RESULT_WINDOW);
            searchRequest.scroll(SCROLL_TIME);
            // scroll 查询 获取前10000 条数据和scrollId
            searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            total  = hits.getTotalHits().value;
            // 1分钟 内的scrollId 第一次
            scrollId = searchResponse.getScrollId();
            // 部分数据在10000 以内 先获取一部分
            if(firstRowNum<MAX_RESULT_WINDOW){
                startPosition = firstRowNum - scrollCount*MAX_RESULT_WINDOW>0?firstRowNum - scrollCount*MAX_RESULT_WINDOW:0;
                endPosition = lastRowNum-scrollCount*MAX_RESULT_WINDOW>MAX_RESULT_WINDOW?MAX_RESULT_WINDOW:lastRowNum-scrollCount*MAX_RESULT_WINDOW;
                loopLoadDataByPosition(searchHitList, startPosition, endPosition, hits);
            }
            if(StringUtils.isNotEmpty(scrollId)){
                // 指针还没有到最后的指针
                while (scrollCount<lastScrollCursor){
                    searchResponse = searchScroll(scrollId);
                    scrollId = searchResponse.getScrollId();
                    scrollCount++;
                    //
                    if(scrollCount>=firstScrollCursor){
                        // 从hits取对应数据 剩余容量
                        int capacity = pageSize-searchHitList.size();
                        if(capacity>0){
                            hits = searchResponse.getHits();
                            // 命中数量
                            int hitCount = hits.getHits().length;
                            startPosition = firstRowNum - scrollCount*MAX_RESULT_WINDOW>0?firstRowNum - scrollCount*MAX_RESULT_WINDOW:0;
                            endPosition = lastRowNum-scrollCount*MAX_RESULT_WINDOW>MAX_RESULT_WINDOW?MAX_RESULT_WINDOW:lastRowNum-scrollCount*MAX_RESULT_WINDOW;
                            // 超过命中次数  取命中次数
                            endPosition = endPosition>hitCount?hitCount:endPosition;
                            if(scrollCount==firstScrollCursor)
                                startPosition--;
                            if(firstScrollCursor!=lastScrollCursor&&scrollCount==lastScrollCursor)
                                endPosition=capacity>MAX_RESULT_WINDOW?MAX_RESULT_WINDOW:capacity-startPosition;
                            loopLoadDataByPosition(searchHitList, startPosition, endPosition, hits);
                        }
                    }

                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
        return total;
    }


    /**
     * 滚动搜索
     * @param scrollId 滚动Id
     * @return
     * @throws IOException
     */
    protected SearchResponse searchScroll(String scrollId) throws Exception{
        RestHighLevelClient client = null;
        client = ElasticSearchPoolUtil.getClient();
        SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
        searchScrollRequest.scroll(SCROLL_TIME);
        SearchResponse response = client.scroll(searchScrollRequest, RequestOptions.DEFAULT);
        ElasticSearchPoolUtil.returnClient(client);
        return  response;

    }


    /**
     * 循环批量加载数据
     * @param context
     * @param startPosition
     * @param endPosition
     * @param hits
     */
    private List<T> loopLoadDataByPosition( SearchSourceBuilder context, int startPosition, int endPosition, SearchHits hits) {
        int length = hits.getHits().length;
        int destLength = endPosition-startPosition;
        destLength = destLength>length?length:destLength;
        SearchHit[] hitArr = new SearchHit[destLength];
        System.arraycopy(hits.getHits(),startPosition,hitArr,0,hitArr.length);
        return batchLoadData(context,hitArr);
    }
    private List<SearchHit> loopLoadDataByPosition(List<SearchHit> searchHitList, int startPosition, int endPosition, SearchHits hits) {
        int length = hits.getHits().length;
        int destLength = endPosition-startPosition;
        destLength = destLength>length?length:destLength;
        SearchHit[] hitArr = new SearchHit[destLength];
        System.arraycopy(hits.getHits(),startPosition,hitArr,0,hitArr.length);
        searchHitList.addAll(Arrays.asList(hitArr));
        return searchHitList;
    }

    public void deleteIndex() {
        RestHighLevelClient client = null;
        try {
            client = ElasticSearchPoolUtil.getClient();
            client.indices().delete(new DeleteIndexRequest(index), RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
    }

    public void deleteByQuery(QueryBuilder builder) {
        DeleteByQueryRequest request = new DeleteByQueryRequest(index);
        request.setQuery(builder);
        request.setBatchSize(10000);
        request.setConflicts("proceed");
        RestHighLevelClient client = null;
        try {
            client = ElasticSearchPoolUtil.getClient();
            client.deleteByQuery(request, RequestOptions.DEFAULT);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            ElasticSearchPoolUtil.returnClient(client);
        }
    }

    protected EsQueryBuilder esLambdaQuery() {
        return new EsQueryBuilder();
    }


    @Data
    protected class EsQueryBuilder {

        private SearchSourceBuilder searchSourceBuilder;

        private BoolQueryBuilder boolQueryBuilder;

        private EsQueryBuilder() {
            if (this.searchSourceBuilder == null) {
                this.searchSourceBuilder = new SearchSourceBuilder();
            }
            if (this.boolQueryBuilder == null) {
                this.boolQueryBuilder = QueryBuilders.boolQuery();
            }
        }

        public EsQueryBuilder eq(GFunction<? extends T, Object> gFunction, Object value) {
            String field = gFunction.field();
            this.searchSourceBuilder.postFilter(QueryBuilders.termQuery(field, value));
            this.boolQueryBuilder.filter(QueryBuilders.termQuery(field, value));
            return this;
        }

        public EsQueryBuilder getDoc_InfoBy_Id(GFunction<? extends T, Object> gFunction, Object value) {
            String field = gFunction.field();
            field="_"+field;
            this.searchSourceBuilder.postFilter(QueryBuilders.termQuery(field, value));
            this.boolQueryBuilder.filter(QueryBuilders.termQuery(field, value));
            return this;
        }


        public EsQueryBuilder eqAll(T t) {
            Field[] fields = t.getClass().getDeclaredFields();
            if(fields!=null&&fields.length>0){
                BoolQueryBuilder funScoreBuilders = QueryBuilders.boolQuery();
                for(Field field : fields){
                    field.setAccessible(true);
                    String name = field.getName();
                    try {
                        Object value = field.get(t);
                        if(value==null||name==null||Constant.SERIALVERSIONUID.equals(name)){
                            continue;
                        }
                       funScoreBuilders = boolTermQueryBuild( funScoreBuilders,name,value);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
                this.searchSourceBuilder.query(funScoreBuilders);
                this.boolQueryBuilder.must(funScoreBuilders);
            }
            return this;
        }

        private BoolQueryBuilder boolTermQueryBuild(BoolQueryBuilder boolQueryBuilder, String key, Object value) {
            return boolQueryBuilder.must(
                    QueryBuilders.functionScoreQuery(
                            QueryBuilders.termQuery(key, value), ScoreFunctionBuilders.weightFactorFunction(1)
                    )
                            .setMinScore(0f)
                            .maxBoost(50f)
                            .scoreMode(FunctionScoreQuery.ScoreMode.MAX)
                            .boostMode(CombineFunction.REPLACE)
            );
        }
        public EsQueryBuilder notEq(GFunction<? extends T, Object> gFunction, Object value) {
            String field = gFunction.field();
            this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery(field, value)));
            this.boolQueryBuilder.mustNot(QueryBuilders.termQuery(field, value));
            return this;
        }

        public EsQueryBuilder in(GFunction<? extends T, Object> gFunction, Object... values) {
            String field = gFunction.field();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            for (Object value : values) {
                boolQueryBuilder.should(QueryBuilders.termQuery(field, value));
                this.boolQueryBuilder.should(QueryBuilders.termQuery(field, value));
            }
            this.searchSourceBuilder.postFilter(boolQueryBuilder);
            return this;
        }

        public EsQueryBuilder notIn(GFunction<? extends T, Object> gFunction, Object... values) {
            String field = gFunction.field();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            for (Object value : values) {
                boolQueryBuilder.mustNot(QueryBuilders.termQuery(field, value));
                this.boolQueryBuilder.mustNot(QueryBuilders.termQuery(field, value));
            }
            this.searchSourceBuilder.postFilter(boolQueryBuilder);
            return this;
        }

        public EsQueryBuilder between(GFunction<? extends T, Object> gFunction, Object begin, Object end) {
            String field = gFunction.field();
            this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().filter(QueryBuilders.rangeQuery(field).from(begin).to(end)));
            this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).from(begin).to(end));
            return this;
        }

        public EsQueryBuilder notBetween(GFunction<? extends T, Object> gFunction, Object begin, Object end) {
            String field = gFunction.field();
            this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.rangeQuery(field).from(begin).to(end)));
            this.boolQueryBuilder.mustNot(QueryBuilders.rangeQuery(field).from(begin).to(end));
            return this;
        }

        public EsQueryBuilder gt(GFunction<? extends T, Object> gFunction, Object value) {
            String field = gFunction.field();
            this.searchSourceBuilder.postFilter(QueryBuilders.rangeQuery(field).gt(value));
            this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).gt(value));
            return this;
        }

        public EsQueryBuilder lt(GFunction<? extends T, Object> gFunction, Object value) {
            String field = gFunction.field();
            this.searchSourceBuilder.postFilter(QueryBuilders.rangeQuery(field).lt(value));
            this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).lt(value));
            return this;
        }

        public EsQueryBuilder ge(GFunction<? extends T, Object> gFunction, Object value) {
            String field = gFunction.field();
            this.searchSourceBuilder.postFilter(QueryBuilders.rangeQuery(field).gte(value));
            this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).gte(value));
            return this;
        }

        public EsQueryBuilder le(GFunction<? extends T, Object> gFunction, Object value) {
            String field = gFunction.field();
            this.searchSourceBuilder.postFilter(QueryBuilders.rangeQuery(field).lte(value));
            this.boolQueryBuilder.filter(QueryBuilders.rangeQuery(field).lte(value));
            return this;
        }

        public EsQueryBuilder sort(GFunction<? extends T, Object> gFunction, SortOrder value) {
            this.searchSourceBuilder.sort(gFunction.field(), value);
            return this;
        }

        public EsQueryBuilder page(Integer pageNo, Integer pageSize) {
            pageNo = (pageNo - 1) * pageSize;
            this.searchSourceBuilder.from(pageNo).size(pageSize);
            return this;
        }

        public EsQueryBuilder matchAll(GFunction<? extends T, Object> gFunction, Object... values) {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            String field = gFunction.field();
            for (Object value : values) {
                boolQueryBuilder.filter(QueryBuilders.matchPhrasePrefixQuery(field, value));
                this.boolQueryBuilder.filter(QueryBuilders.matchPhrasePrefixQuery(field, value));
            }
            this.searchSourceBuilder.postFilter(boolQueryBuilder);
            return this;
        }

        public EsQueryBuilder matchOne(GFunction<? extends T, Object> gFunction, Object... values) {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            String field = gFunction.field();
            for (Object value : values) {
                boolQueryBuilder.should(QueryBuilders.matchPhrasePrefixQuery(field, value));
                this.boolQueryBuilder.should(QueryBuilders.matchPhrasePrefixQuery(field, value));
            }
            this.searchSourceBuilder.postFilter(boolQueryBuilder);
            return this;
        }

        public EsQueryBuilder notMatch(GFunction<? extends T, Object> gFunction, Object value) {
            String field = gFunction.field();
            this.boolQueryBuilder.mustNot(QueryBuilders.matchPhrasePrefixQuery(field, value));
            this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.matchPhrasePrefixQuery(field, value)));
            return this;
        }

        public EsQueryBuilder fuzzyAll(GFunction<? extends T, Object> gFunction, Fuzziness fuzziness, Object... values) {
            String field = gFunction.field();
            for (Object value : values) {
                this.searchSourceBuilder.postFilter(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
                this.boolQueryBuilder.filter(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
            }
            return this;
        }

        public EsQueryBuilder fuzzyOne(GFunction<? extends T, Object> gFunction, Fuzziness fuzziness, Object... values) {
            String field = gFunction.field();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            for (Object value : values) {
                boolQueryBuilder.should(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
                this.boolQueryBuilder.should(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
            }
            this.searchSourceBuilder.postFilter(boolQueryBuilder);
            return this;
        }

        public EsQueryBuilder notFuzzy(GFunction<? extends T, Object> gFunction, Fuzziness fuzziness, Object value) {
            String field = gFunction.field();
            this.boolQueryBuilder.mustNot(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness));
            this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.fuzzyQuery(field, value).fuzziness(fuzziness)));
            return this;
        }

        public EsQueryBuilder isNull(GFunction<? extends T, Object> gFunction) {
            String field = gFunction.field();
            this.boolQueryBuilder.filter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(field)));
            this.searchSourceBuilder.postFilter(QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(field)));
            return this;
        }

        public EsQueryBuilder isNotNull(GFunction<? extends T, Object> gFunction) {
            String field = gFunction.field();
            this.boolQueryBuilder.filter(QueryBuilders.existsQuery(field));
            this.searchSourceBuilder.postFilter(QueryBuilders.existsQuery(field));
            return this;
        }

        public List<T> query() {
            return search(this.searchSourceBuilder);
        }

        public Page<T> queryPage(boolean searchfrom){
            return searchPage(this.searchSourceBuilder,searchfrom);
        }

        public void delete() {
            deleteByQuery(this.boolQueryBuilder);
        }

    }

    public Class getGenericType(int index) {
        Type genType = getClass().getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {
            throw new RuntimeException("Index outof bounds");
        }
        if (!(params[index] instanceof Class)) {
            return Object.class;
        }
        return (Class) params[index];
    }

    //分词 返回分词结果
    public List<String> analyzeTitle(DocInfo docInfo){
        try {
            RestHighLevelClient client = ElasticSearchPoolUtil.getClient();

            Request request = new Request("GET", "_analyze");
            JSONObject entity = new JSONObject();
            entity.put("analyzer", "hanlp_index");
            entity.put("text", docInfo.getTitle());
            request.setJsonEntity(entity.toJSONString());
            Response response = client.getLowLevelClient().performRequest(request);
            JSONObject tokens = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
            JSONArray arrays = tokens.getJSONArray("tokens");
            List<String> list=new ArrayList<>();
            for (int i = 0; i < arrays.size(); i++) {
                JSONObject obj = JSON.parseObject(arrays.getString(i));
                list.add(obj.getString("token"));
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

//    //分词 返回分词结果
//    public List<String> analyzeTitle(DocInfo docInfo){
//        try {
//            RestHighLevelClient client = ElasticSearchPoolUtil.getClient();
//            RestClient lowLevelClient = client.getLowLevelClient();
//            Request request = new Request("GET", "_analyze");
//            JSONObject entity = new JSONObject();
//            entity.put("analyzer", "ik_max_word");
//            entity.put("text", text);
//            AnalyzeRequestBuilder analyzeRequestBuilder = new AnalyzeRequestBuilder((ElasticsearchClient) lowLevelClient,AnalyzeAction.INSTANCE,"doc_info",docInfo.getTitle());
//            analyzeRequestBuilder.setAnalyzer("hanlp_index");
//            List<AnalyzeAction.AnalyzeToken> analyzeTokenList = analyzeRequestBuilder.execute().actionGet().getTokens();
//            List<String> searchTermList = new ArrayList<>();
//            for (int i = 0; i < analyzeTokenList.size(); i++) {
//                searchTermList.add(String.valueOf(analyzeTokenList.get(i)));
//            }
//            System.out.println("111");
//            return searchTermList;
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return null;
//    }

}


