说明

maven依赖

官方客户端 https://www.elastic.co/guide/en/elasticsearch/client/java-rest/6.4/index.html

<dependency>
                <groupId>org.elasticsearch</groupId>
                <artifactId>elasticsearch</artifactId>
                <version>6.5.0</version>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.elasticsearch.client</groupId>
                <artifactId>elasticsearch-rest-high-level-client</artifactId>
                <version>6.5.0</version>
                <exclusions>
                    <exclusion>
                        <artifactId>commons-codec</artifactId>
                        <groupId>commons-codec</groupId>
                    </exclusion>
                    <!--此处要排除掉自带的,这个自带的版本低,会报错-->
                    <exclusion>
                        <artifactId>elasticsearch</artifactId>
                        <groupId>org.elasticsearch</groupId>
                    </exclusion>
                </exclusions>
            </dependency>



Util类

配置类



package com.crb.ocms.product.domain.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * @Project crb-product-domain
 * @PackageName com.crb.ocms.product.admin.config
 * @ClassName ESConfiguration
 * @Author liqiang
 * @Date 2019/3/6 5:52 PM
 * @Description 用于加载es的相关配置
 */
@ConfigurationProperties(prefix = "esconfig")
@Data
public class ESConfiguration {
    /**
     * index别名
     */
    private String aliasName;
    
    /**
     * 索引名字
     */
    private String indexName;
    
    
    /**
     * 全量索引的最大处理线程大小
     */
    private int threadSize;
    
    /**
     * 全量索引并行执行每个线程每次执行数据大小
     */
    private int treadDataSize;
    
    /**
     * typename
     */
    private String typeName;
    
    /**
     * ESURL
     */
    private String esUrl;

    /**
     * es ip
      */
    private String host;

    /**
     * es端口
     */
    private Integer port;

    /***
     *   用于标识是否正在处理的rediskey
     */
    private String redissKey;

    /**
     *mappingJson
     */
    private String mappingJson;
}



 

util工具类



package com.crb.ocms.product.service.impl;

import com.crb.ocms.product.domain.config.ESConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.NStringEntity;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexRequest;
import org.elasticsearch.action.admin.indices.get.GetIndexResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.client.*;
import org.elasticsearch.common.CheckedFunction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.SuggestionBuilder;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;


/**
 * @Project crb-product-service
 * @PackageName com.crb.ocms.demo.service.test.impl
 * @ClassName ESHLRestUtil
 * @Author liqiang
 * @Date 2019/2/25 13:45
 * @Description es工具类
 */
@Slf4j
@Component
@EnableConfigurationProperties(ESConfiguration.class)
public class ESHLRestUtil {
    private RestHighLevelClient client = null;

    public RestHighLevelClient getClient() {
        return client;
    }

    public void setClient(RestHighLevelClient client) {
        this.client = client;
    }
    ESConfiguration esConfiguration;

    @Autowired
    public ESHLRestUtil(ESConfiguration configuration) {
        client = new SimpleRestHighLevelClient(RestClient.builder(new HttpHost(configuration.getHost(), configuration.getPort(), "http")));
        this.esConfiguration=configuration;
    }
    public SimpleRestHighLevelClient getSimpleClient(){
        return (SimpleRestHighLevelClient)client;
    }


    /**
     *根据indexname获得index名字 支持通配符匹配
     * @param indexName
     * @return
     */
    public   String [] getIndexNames(String indexName)  {

        GetIndexResponse getIndexResponse= null;
        try {
            getIndexResponse = getIndexInfo(indexName);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return getIndexResponse!=null?getIndexResponse.getIndices():null;
    }

    /**
     * 根据index名字获得index信息
     * @param indexName
     * @return
     */
    public GetIndexResponse  getIndexInfo(String indexName) throws IOException {
      //GetIndexRequest request = new GetIndexRequest().indices(indexName);
//        try {
//            GetIndexResponse getIndexResponse =client.indices().get(request, RequestOptions.DEFAULT);
//            return  getIndexResponse;
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
        //master_timeout
        /**
         * 因为现在是api使用6.5 线上是6.24 master_timeout   使用以下方式 替换掉
         */
        GetIndexRequest request = new GetIndexRequest().indices(indexName);
        String[] indices = request.indices() == null ? Strings.EMPTY_ARRAY : request.indices();
        String endpoint =indexName;
        Request httpequest = new Request("GET", endpoint);
        httpequest.addParameter("ignore_unavailable", "false");
        httpequest.addParameter("expand_wildcards", "open");
        httpequest.addParameter("allow_no_indices", "true");
        org.apache.http.HttpEntity entity = new NStringEntity("", ContentType.APPLICATION_JSON);
        httpequest.setEntity(entity);
        Response httpResponse= getClient().getLowLevelClient().performRequest(httpequest);
        GetIndexResponse getIndexResponse=getSimpleClient().parseGetIndexRespons(httpResponse);
        return  getIndexResponse;
    }

    /**
     * 获得最大index名字
     * @param names indexName_number 格式
     * @return
     */
    public  Integer getMaxIndexNumber(String []names) {
        if(names==null||names.length<=0){
            return null;
        }
        List<Integer> indexNumber=new ArrayList<Integer>();
        for (String name:
                names) {
            String[] arrs=name.split("_");
            if(arrs.length<=1){
                continue;
            }
            indexNumber.add(Integer.valueOf(arrs[1]));
        }
        indexNumber.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                 return o2 - o1;
            }
        });
        return CollectionUtils.isEmpty(indexNumber)?null:indexNumber.get(0);
    }
    /**
     * 验证索引是否存在
     *
     * @param index 索引名称
     * @return
     * @throws Exception
     */
    public boolean indexExists(String index) throws Exception {
        GetIndexRequest request = new GetIndexRequest();
        request.indices(index);
        boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
        return exists;
    }

    /**
     * 创建index
     *
     * @param index
     * @param indexType
     * @param properties 结构: {name:{type:text}} {age:{type:integer}}
     * @return
     * @throws Exception
     */
    public boolean indexCreate(String index, String indexType,
                               Map properties) throws Exception {

        if (indexExists(index)) {
            return true;
        }
        CreateIndexRequest request = new CreateIndexRequest(index);
        request.settings(Settings.builder().put("index.number_of_shards", 3)
                .put("index.number_of_replicas", 2));
        Map jsonMap = new HashMap<>();
        Map mapping = new HashMap<>();
        mapping.put("properties", properties);
        jsonMap.put(indexType, mapping);
        request.mapping(indexType, jsonMap);

        CreateIndexResponse createIndexResponse = client.indices().create(
                request,RequestOptions.DEFAULT);
        boolean acknowledged = createIndexResponse.isAcknowledged();
        return acknowledged;
    }

    /**
     * 删除指定索引
     * @param indexName
     * @return
     * @throws IOException
     */
    public boolean deleteIndex(String indexName) throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest(indexName);
        AcknowledgedResponse deleteIndexResponse = client.indices().delete(request, RequestOptions.DEFAULT);
        return deleteIndexResponse.isAcknowledged();
    }
    /**
     * 创建索引
     * @param index       索引名字
     * @param settiongs settiongs
     * @return
     * @throws Exception
     * @author lqiang
     */
    public boolean indexCreate(String index, String settiongs
    ) throws Exception {

        if (indexExists(index)) {
            return true;
        }
        CreateIndexRequest request = new CreateIndexRequest(index);
//        request.settings(Settings.builder().put("index.number_of_shards", 3)
//                .put("index.number_of_replicas", 2));
        request.source(settiongs, XContentType.JSON);

        CreateIndexResponse createIndexResponse = client.indices().create(
                request,RequestOptions.DEFAULT);
        boolean acknowledged = createIndexResponse.isAcknowledged();
        return acknowledged;
    }

    /**
     * 创建更新文档
     *
     * @param index
     * @param indexType
     * @param documentId
     * @param josonStr
     * @return
     * @throws Exception
     */
    public boolean documentCreate(String index, String indexType,
                                  String documentId, String josonStr) throws Exception {
        IndexRequest request = new IndexRequest(index, indexType, documentId);

        request.source(josonStr, XContentType.JSON);
        IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);

        if (indexResponse.getResult() == DocWriteResponse.Result.CREATED
                || indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            return true;
        }
        ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            return true;
        }
        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo
                    .getFailures()) {
                throw new Exception(failure.reason());
            }
        }
        return false;
    }

    /**
     * 创建更新文档
     *
     * @param index
     * @param indexType
     * @param documentId
     * @param map
     * @return
     * @throws Exception
     */
    public boolean documentCreate(String index, String indexType,
                                  String documentId, Map map) throws Exception {
        IndexRequest request = new IndexRequest(index, indexType, documentId);

        request.source(map);
        IndexResponse indexResponse = client.index(request,RequestOptions.DEFAULT);

        if (indexResponse.getResult() == DocWriteResponse.Result.CREATED
                || indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            return true;
        }
        ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            return true;
        }
        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo
                    .getFailures()) {
                throw new Exception(failure.reason());
            }
        }
        return false;
    }


    /**
     * 批量创建更新文档
     *
     * @param index
     * @param indexType
     * @param list      建索引时传入_id作为docuemntId
     * @return
     * @throws Exception
     */
    public boolean documentCreateBulk(String index, String indexType,
                                      List<Map<String, Object>> list) throws Exception {

        if (list.size() > 0) {

            BulkRequest bulkRequest = new BulkRequest();

            for (Map map : list) {
                IndexRequest indexRequest;

                Object idObj = map.get("_id");
                if (idObj != null && StringUtils.isNotBlank(idObj.toString())) {
                    map.remove("_id");
                    String documentId = idObj.toString();
                    indexRequest = new IndexRequest(index, indexType, documentId);
                } else {
                    indexRequest = new IndexRequest(index, indexType);
                }
                indexRequest.source(map);
                bulkRequest.add(indexRequest);

            }

            BulkResponse bulkResponse = client.bulk(bulkRequest,RequestOptions.DEFAULT);

            if (bulkResponse.hasFailures()) {
                System.out.println("索引异常信息:" + bulkResponse.buildFailureMessage());
                return false;
            }
        }

        return true;

    }

    /**
     * 创建更新文档
     *
     * @param index
     * @param indexType
     * @param documentId
     * @param routing
     * @param map
     * @return
     * @throws Exception
     */
    public boolean documentCreate(String index, String indexType,
                                  String documentId, String routing, Map map) throws Exception {
        IndexRequest request = new IndexRequest(index, indexType, documentId);

        request.routing(routing);
        request.source(map);
        IndexResponse indexResponse = client.index(request,RequestOptions.DEFAULT);

        if (indexResponse.getResult() == DocWriteResponse.Result.CREATED
                || indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            return true;
        }
        ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            return true;
        }
        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo
                    .getFailures()) {
                throw new Exception(failure.reason());
            }
        }
        return false;
    }

    /**
     * 创建索引
     *
     * @param index
     * @param indexType
     * @param josonStr
     * @return
     * @throws Exception
     */
    public String documentCreate(String index, String indexType, String josonStr)
            throws Exception {
        IndexRequest request = new IndexRequest(index, indexType);

        request.source(josonStr, XContentType.JSON);
        IndexResponse indexResponse = client.index(request,RequestOptions.DEFAULT);

        String id = indexResponse.getId();
        if (indexResponse.getResult() == DocWriteResponse.Result.CREATED
                || indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            return id;
        }
        ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            return id;
        }
        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo
                    .getFailures()) {
                throw new Exception(failure.reason());
            }
        }
        return null;
    }

    /**
     * 创建索引
     *
     * @param index
     * @param indexType
     * @param map
     * @return
     * @throws Exception
     */
    public String documentCreate(String index, String indexType,
                                 Map map) throws Exception {
        IndexRequest request = new IndexRequest(index, indexType);

        request.source(map);
        IndexResponse indexResponse = client.index(request,RequestOptions.DEFAULT);

        String id = indexResponse.getId();
        if (indexResponse.getResult() == DocWriteResponse.Result.CREATED
                || indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
            return id;
        }
        ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            return id;
        }
        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo
                    .getFailures()) {
                throw new Exception(failure.reason());
            }
        }
        return null;
    }

    public boolean documentDelete(String index, String indexType,
                                  String documentId) throws Exception {
        DeleteRequest request = new DeleteRequest(index, indexType, documentId);
        DeleteResponse deleteResponse = client.delete(request,RequestOptions.DEFAULT);
        if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
            return true;
        }
        ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
        if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
            return true;
        }
        if (shardInfo.getFailed() > 0) {
            for (ReplicationResponse.ShardInfo.Failure failure : shardInfo
                    .getFailures()) {
                throw new Exception(failure.reason());
            }
        }
        return false;
    }

    /**
     * 为指定index指定别名
     * @param indexName index名字
     * @param aliasesName 别名名字
     * @return
     */
    public boolean aliases(String indexName,String aliasesName){
            IndicesAliasesRequest request=new IndicesAliasesRequest();
            IndicesAliasesRequest.AliasActions aliasAction =
                    new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
                            .index(indexName)
                            .alias(aliasesName);
            request.addAliasAction(aliasAction);
            try {
                AcknowledgedResponse indicesAliasesResponse =
                        getClient().indices().updateAliases(request, RequestOptions.DEFAULT);
                return indicesAliasesResponse.isAcknowledged();
            } catch (IOException e) {
                log.info("指定别名失败");
                return  false;
            }
    }

    /**
     * 指定别名 并删除oldIndex的别名
     * @param oldindex
     * @param newIndex
     * @param aliasesName
     * @return
     */
    public boolean aliases(String oldindex, String newIndex,String aliasesName) {
        IndicesAliasesRequest request=new IndicesAliasesRequest();
        //旧的删除
        IndicesAliasesRequest.AliasActions aliasAction =
                new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.REMOVE)
                        .index(oldindex)
                        .alias(aliasesName);
        request.addAliasAction(aliasAction);
        //新的绑定
        aliasAction =
                new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
                        .index(newIndex)
                        .alias(aliasesName);
        request.addAliasAction(aliasAction);
        try {
            AcknowledgedResponse indicesAliasesResponse =
                    getClient().indices().updateAliases(request, RequestOptions.DEFAULT);
            return indicesAliasesResponse.isAcknowledged();
        } catch (IOException e) {
            return  false;
        }
    }
    /**
     * 根据id获得文档信息
     * @param index indexName
     * @param type typeName
     * @param id id
     * @return
     * @throws IOException
     */
    public GetResponse getDoucmentById(String index, String type, String id) throws IOException {
        GetRequest getRequest = new GetRequest(
                index,//索引
                type,//类型
                id);//文档ID
        GetResponse getResponse = client.get(getRequest,RequestOptions.DEFAULT);
        return getResponse;
    }
    /**
     * 简单的单表根据条件进行查询
     *
     * @param index      index
     * @param type       type
     * @param parameters 参数
     * @return
     */
    public List<String> queryByMatch(String index, String type, Map<String, String> parameters) {
        List<String> jsons=new ArrayList<String>();
        try {
            SearchRequest searchRequest=new SearchRequest();
            BoolQueryBuilder booleanQueryBuilder= QueryBuilders.boolQuery();
            SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
            for (String key :
                        parameters.keySet()) {
                    booleanQueryBuilder.must().add(QueryBuilders.termQuery(key,parameters.get(key)));
            }
            searchSourceBuilder.query(booleanQueryBuilder);
            searchRequest.source(searchSourceBuilder);

            SearchResponse response = client.search(searchRequest,RequestOptions.DEFAULT);
            if(response.getHits().getHits()!=null){
                for (SearchHit searchHit:
                response.getHits().getHits() ) {
                    jsons.add(searchHit.getSourceAsString());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jsons;
    }
    /**
     * 只包含字母
     *
     * @return 验证成功返回true,验证失败返回false
     */
    public static boolean checkLetter(String cardNum) {
        String regex = "^[A-Za-z]+$";
        return Pattern.matches(regex, cardNum);
    }

    /**
     * 验证中文
     *
     * @param chinese 中文字符
     * @return 验证成功返回true,验证失败返回false
     */
    public static boolean checkChinese(String chinese) {
        String regex = "^[\u4E00-\u9FA5]+$";
        return Pattern.matches(regex, chinese);
    }


    /**
     * Description:提示词,支持中文、拼音、首字母等
     * <p>
     * 1、检测搜索词是中文还是拼音
     * 2、若是中文,直接按照name字段提示
     * 3、若是拼音(拼音+汉字),先按照name.keyword_pinyin获取,若是无结果按照首字母name.keyword_first_py获取
     * @param index
     * @param type
     * @param field 提示字段名字
     * @param text 文本
     * @return
     * @author liqiang
     */
    public Set<String> getSuggestWord(String index, String type, String field, String text) throws IOException {
        String postField=field;
        if (checkLetter(text)) {
            postField = field + ".keyword_pinyin";
        } else if (checkChinese(text)) {
            postField = field;
        } else {
            postField = field + ".keyword_pinyin";
        }
        Set<String> suggestTexts= postSuggestWord(index,type,postField,text);
        if(org.springframework.util.CollectionUtils.isEmpty(suggestTexts)){
            return postSuggestWord(index,type,field+".keyword_first_py",text);
        }
        return suggestTexts;
    }
    /**
     * Description:提示词,支持中文、拼音、首字母等
     * @param index
     * @param type
     * @param field 提示字段名字
     * @param text 文本
     * @return
     * @author liqiang
     */
    private Set<String> postSuggestWord(String index, String type, String field, String text) throws IOException {

        // 1、创建search请求
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.types(type);
        // 2、用SearchSourceBuilder来构造查询请求体 ,请仔细查看它的方法,构造各种查询的方法都在这。
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.fetchSource(false);//不返回_source数据
        sourceBuilder.size(0);//忽略hits
        //做查询建议
        //词项建议
        SuggestionBuilder termSuggestionBuilder =
                SuggestBuilders.completionSuggestion(field).text(text);
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        suggestBuilder.addSuggestion("suggest_productName", termSuggestionBuilder);
        sourceBuilder.suggest(suggestBuilder);
        searchRequest.source(sourceBuilder);
        Set<String> suggestTextList = new HashSet<String>();
        //3、发送请求
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        //4、处理响应
        //搜索结果状态信息
        if (RestStatus.OK.equals(searchResponse.status())) {
            // 获取建议结果
            Suggest suggest = searchResponse.getSuggest();
            CompletionSuggestion termSuggestion = suggest.getSuggestion("suggest_productName");
            for (CompletionSuggestion.Entry entry : termSuggestion.getEntries()) {
                for (CompletionSuggestion.Entry.Option option : entry) {
                    suggestTextList.add(option.getText().string());
                }
            }
        }
        return suggestTextList;
    }
    /**
     * @Project demo
     * @PackageName com.crb.ocms.product.service.impl
     * @ClassName SimpleRestHighLevelClient
     * @Author qiang.li
     * @Date 2019/4/2 10:45 AM
     * @Description es版本降级导致的高级api会生成部分属性 低版本不能识别  将解析结果的方法暴露在外面
     */
    public class SimpleRestHighLevelClient extends RestHighLevelClient {
        public SimpleRestHighLevelClient(RestClientBuilder restClientBuilder) {
            super(restClientBuilder);
        }

        /**
         * 父类解析的解析响应内容是受保护的 所以定义一个继承
         * 解决版本不一致的 自定义请求 解析
         * @param response
         * @param entityParser
         * @param <Req>
         * @param <Resp>
         * @return
         * @throws IOException
         */
        public <Req extends Validatable, Resp> Resp parseEntity(Response response,CheckedFunction<XContentParser, Resp, IOException> entityParser)throws IOException {

            return this.parseEntity(response.getEntity(),entityParser);
        }

        /**
         * 解析获得idnex的响应
         * @param response
         * @return
         * @throws IOException
         */
        public GetIndexResponse parseGetIndexRespons(Response response) throws IOException {
            return  parseEntity(response,GetIndexResponse::fromXContent);
        }

        /**
         * 解析查询响应
         * @param response
         * @return
         * @throws IOException
         */
        public SearchResponse parseSearchResponse(Response response) throws IOException {
            return parseEntity(response,SearchResponse::fromXContent);
        }

        /**
         * 解析索引迁移响应
         * @param response
         * @return
         * @throws IOException
         */
        public BulkByScrollResponse parseBulkByScrollResponse(Response response) throws IOException {
            return  parseEntity(response,BulkByScrollResponse::fromXContent);
        }



    }

}



 

封装的处理器

工作原理

将指定条件的数据迁移到新索引,然后再新索引上面进行导入(多线程并行导入),导入完毕删除老索引和别名 然后为新索引绑定别名 实现不停机更新

抽象接口



package com.crb.ocms.product.serviceTool;

import org.elasticsearch.index.reindex.ReindexRequest;

import java.io.IOException;
import java.util.concurrent.Future;

/**
 * @Project crb-product-service
 * @PackageName com.crb.ocms.product.serviceTool
 * @ClassName EsImportService
 * @Author liqiang
 * @Date 2019/3/29 1:28 PM
 * @Description es导入抽象接口
 */
public interface ESImportService {

    /**
     * 获得索引迁移条件
     * @param reindexRequest
     * @return
     * @throws IOException
     */
    public boolean reindex(ReindexRequest reindexRequest) throws IOException;

    /**
     * 异步导入
     * @return
     */
    public Future<Boolean> importAllAsyn();
}



 

抽象的处理器

使用模板模式将通用代码抽出来



package com.crb.ocms.product.serviceTool;

import com.crb.ocms.product.domain.config.ESConfiguration;
import com.crb.ocms.product.domain.entity.MdProduct;
import com.crb.ocms.product.domain.redisskey.MdEsProductRedisKeyEnum;
import com.crb.ocms.product.domain.util.exceptions.OCmsExceptions;
import com.crb.ocms.product.service.impl.ESHLRestUtil;
import com.hazelcast.util.StringUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.http.entity.ContentType;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.ReindexRequest;
import org.redisson.api.RedissonClient;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Project crb-product-service
 * @PackageName com.crb.ocms.product.serviceTool
 * @ClassName ESAbstrctImpor
 * @Author liqiang
 * @Date 2019/3/29 1:35 PM
 * @Description 抽象的es导入处理器
 */
@Log4j2
public abstract class ESAbstractImport implements  ESImportService {
   private RestTemplate restTemplate;
    private ESConfiguration esConfiguration;
    private ESHLRestUtil eshlRestUtil;
    private RedissonClient redissonClient;
    /**
     * 当前index名字
     */
    private String templateIndexName;
    /**
     * 之前的index名字
     */
    private String beforeIndexName;
    public ESAbstractImport(RestTemplate restTemplate, ESConfiguration esConfiguration,RedissonClient redissonClient){
        esConfiguration.setIndexName("cmsproudct");
        esConfiguration.setAliasName("cmsproductAliasName");
        this.restTemplate=restTemplate;
        this.esConfiguration=esConfiguration;
        this.eshlRestUtil=new ESHLRestUtil(esConfiguration);
        this.redissonClient=redissonClient;
    }


    /**
     * 获得创建索引的mapping抽象方法
     * @return
     */
    public abstract String  getMapping();

    /**
     * 获得所有迁移requestBody抽象方法
     * @return
     */
    public abstract  ReindexRequest getReindex();

    /**
     * es索引秦阿姨

     * @return
     */
    @Override
    public boolean reindex(ReindexRequest reindexRequest) throws IOException {

      //  BulkByScrollResponse bulkByScrollResponse= eshlRestUtil.getClient().reindex(reindexRequest,RequestOptions.DEFAULT);
        /**
         * 因为现在是api使用6.5 线上是6.24 查询语句多生成zero_terms_query   使用以下方式 替换掉
         */
        String endpoint ="/_reindex";
        Request httpequest = new Request("POST", endpoint);
        BytesRef source = XContentHelper.toXContent(reindexRequest, XContentType.JSON, false).toBytesRef();
        org.apache.http.HttpEntity entity = new NStringEntity(new String(source.bytes).replaceAll("\"zero_terms_query\":\"NONE\","," "), ContentType.APPLICATION_JSON);
        httpequest.setEntity(entity);
        Response httpResponse=eshlRestUtil.getClient().getLowLevelClient().performRequest(httpequest);
        BulkByScrollResponse getIndexResponse=eshlRestUtil.getSimpleClient().parseBulkByScrollResponse(httpResponse);
        return true;
    }

    /**
     * 导入数据 根据设置的线程大小根据数据大小算出线程数量 通过多线程并行处理
     * @return
     */
    public boolean importAll() throws IOException {
        log.info("正在产品全量导入2:"+esConfiguration.getEsUrl());
        if (redissonClient.getMap(esConfiguration.getRedissKey()).isExists()) {
            throw new OCmsExceptions("全量索引正在导入中....");
        }
        String []indexNames=eshlRestUtil.getIndexNames(esConfiguration.getIndexName()+"*");

        if (indexNames!=null&&indexNames.length>0) {
            Integer number= eshlRestUtil.getMaxIndexNumber(indexNames);
            beforeIndexName=number==null?esConfiguration.getIndexName():esConfiguration.getIndexName()+"_"+number;
            templateIndexName=esConfiguration.getIndexName()+"_"+(number==null?1:(number+1));
        } else {
            templateIndexName = esConfiguration.getIndexName();
        }
        boolean isSuccess = true;
        long count = getCount();
        //根据线程处理大小获得页码
        int index = (new Double(Math.ceil(count*1.0 / esConfiguration.getTreadDataSize()))).intValue();
        if (index <= 0 && count > 0) {
            index = 1;
        }
        //获得最大处理线程大小
        int threadSize = index > esConfiguration.getThreadSize() ? esConfiguration.getThreadSize() : index;
        //导入处理器
        ESAbstractImport.ProcessImportIndex processImportIndex = new ESAbstractImport.ProcessImportIndex(esConfiguration.getTreadDataSize(), index, templateIndexName);
        ThreadPoolExecutor executorService=null;
        try {
            //第一次导入直接创建索引
            if (beforeIndexName == null) {
                log.info("开始创建索引");
                eshlRestUtil.indexCreate(templateIndexName,getMapping());
                log.info("创建索引成功");
            } else {
                log.info("开始创建索引1");
                eshlRestUtil.indexCreate(templateIndexName,getMapping());
                log.info("创建索引成功1");
                log.info("开始创建索引2");
                //非第一次导入将子集数据迁移进来
                reindex(getReindex());
                log.info("开始创建索引2");
            }

            /** 线程池的自定义配置,IO密集型任务. */
            executorService = new ThreadPoolExecutor(
                    // 核心线程数
                    threadSize,
                    // 最大线程数
                    100,
                    // 存活时间,30s
                    30,
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<Runnable>(150),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.CallerRunsPolicy());

            redissonClient.getMap(esConfiguration.getRedissKey()).put("templateIndexName", templateIndexName);

            //统一保存异步处理结果
            List<Future<Boolean>> processResult = new ArrayList<Future<Boolean>>();
            for (int i = 0; i < threadSize; i++) {
                processResult.add(executorService.submit(processImportIndex));
            }

            for (Future<Boolean> future :
                    processResult) {
                if (!future.get()) {
                    isSuccess = future.get();
                    break;
                }
            }
            //表示重建成功
            if (isSuccess) {
                //表示第一次初始化 指定别名
                if (StringUtil.isNullOrEmpty(beforeIndexName)) {
                    boolean isFail = false;
                    int i = 0;
                    do {
                        isFail = !eshlRestUtil.aliases(templateIndexName,esConfiguration.getAliasName());
                        if(i>0) {
                            //休眠
                            Thread.sleep(i * 1000);
                        }
                        i++;
                    } while (isFail && i < 10);
                    if (isFail) {
                        log.info("开始删除索引2");
                        eshlRestUtil.deleteIndex(templateIndexName);
                        log.info("开始删除成功");
                    }
                } else {
                    boolean isFail = false;
                    int i = 0;
                    do {
                        //重新绑定别名 失败重试十次
                        isFail = !eshlRestUtil.aliases(beforeIndexName,templateIndexName,esConfiguration.getAliasName());
                        if(i>0) {
                            //休眠
                            Thread.sleep(i * 1000);
                        }
                        i++;
                    } while (isFail && i < 10);
                    if (isFail) {
                        //删除备份索引
                        eshlRestUtil.deleteIndex(templateIndexName);
                        throw new OCmsExceptions("索引重做失败,请重试");
                    } else {
                        //删除备份索引
                        eshlRestUtil.deleteIndex(beforeIndexName);
                    }
                }
            } else {
                for (Future<Boolean> future :
                        processResult) {
                    if (!future.get()) {
                        //没有重做成功删除创建的新索引
                        eshlRestUtil.deleteIndex(templateIndexName);
                        future.isCancelled();//取消其他正在执行的线程
                    }
                }
            }
        } catch (Exception e) {
            String uuid = UUID.randomUUID().toString();
            log.error(uuid + "\n" + e.getMessage());
            eshlRestUtil.deleteIndex(templateIndexName);
            throw new OCmsExceptions("<ES文档导入失败>异常编码" + uuid + "\n" + e.getMessage());
        } finally {
            if (executorService != null) {
                executorService.shutdown();
            }
            redissonClient.getBucket(esConfiguration.getRedissKey()).delete();

        }
        return isSuccess;
    }

    /**
     * 异步的导入方法
     * @return
     */
    @Override
    public Future<Boolean> importAllAsyn() {
        ExecutorService executorService=new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
        return executorService.submit(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                return importAll();
            }
        });
    }

    /**
     * 抽象的处理导入方法
     * @param pageRequest
     * @return
     * @throws IOException
     */
    public abstract   boolean processImport(PageRequest pageRequest) throws IOException;


    public abstract Long getCount();

    /**
     * 用于处理批量索引导入
     */
    class ProcessImportIndex implements Callable<Boolean> {

        //数据总页数
        private int pageCount;
        //总条数
        private int pageSize;

        private String indexName;
        private AtomicLong currentIndex = new AtomicLong();
        //当前处理页数
        AtomicInteger currentPage = new AtomicInteger();

        public ProcessImportIndex(int pageSize, int pageCount, String indexName) {
            this.pageSize = pageSize;
            this.pageCount = pageCount;
            this.indexName = indexName;
        }

        /**
         * Computes a result, or throws an exception if unable to do so.
         *
         * @return computed result
         * @throws Exception if unable to compute a result
         */
        @Override
        public Boolean call() throws Exception {
            try {
                return process();
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        public boolean process() throws IOException {
            int page = currentPage.getAndIncrement();
            redissonClient.getMap(MdEsProductRedisKeyEnum.IMPORT_INDEX).put("pageCount", pageCount);
            while (page < pageCount) {
                redissonClient.getMap(MdEsProductRedisKeyEnum.IMPORT_INDEX).put("currentPage", page+1);
                processImport(new PageRequest(page, pageSize));
                page = currentPage.getAndIncrement();
            }
            return true;
        }

        public AtomicInteger getCurrentPage() {
            return currentPage;
        }

        public void setCurrentPage(AtomicInteger currentPage) {
            this.currentPage = currentPage;
        }


    }

    public String getTemplateIndexName() {
        return templateIndexName;
    }

    public void setTemplateIndexName(String templateIndexName) {
        this.templateIndexName = templateIndexName;
    }

    public interface BulkCallback {
        public void process(MdProduct mdProduct);
    }

    public RestTemplate getRestTemplate() {
        return restTemplate;
    }

    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public ESConfiguration getEsConfiguration() {
        return esConfiguration;
    }

    public void setEsConfiguration(ESConfiguration esConfiguration) {
        this.esConfiguration = esConfiguration;
    }

    public ESHLRestUtil getEshlRestUtil() {
        return eshlRestUtil;
    }

    public void setEshlRestUtil(ESHLRestUtil eshlRestUtil) {
        this.eshlRestUtil = eshlRestUtil;
    }

    public RedissonClient getRedissonClient() {
        return redissonClient;
    }

    public void setRedissonClient(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public String getBeforeIndexName() {
        return beforeIndexName;
    }

    public void setBeforeIndexName(String beforeIndexName) {
        this.beforeIndexName = beforeIndexName;
    }
}



使用例子



package com.crb.ocms.product.serviceTool;

import com.crb.ocms.product.domain.config.ESConfiguration;
import com.crb.ocms.product.domain.entity.MdProduct;
import com.crb.ocms.product.domain.repository.MdProductRepository;
import com.crb.ocms.product.domain.vo.req.FindProductCharacterStocksVo;
import com.crb.ocms.product.domain.vo.resp.ProductCharacterStocksVo;
import com.crb.ocms.product.service.MdProductService;
import com.crb.ocms.product.service.feign.IcProductStoreAccountService;
import com.crb.ocms.product.service.impl.MdESProductServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.ReindexRequest;
import org.redisson.api.RedissonClient;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @Project crb-product-service
 * @PackageName com.crb.ocms.product.serviceTool
 * @ClassName ProductStockImport
 * @Author liqiang
 * @Date 2019/3/29 2:15 PM
 * @Description 处理库存全量索引导入
 */
@Log4j2
@Deprecated
public class ProductStockImport extends  ProductIndexImport {
    MdProductRepository mdProductRepository;
    IcProductStoreAccountService icProductStoreAccountService;
    String regionCode;
    public ProductStockImport(RestTemplate restTemplate, ESConfiguration esConfiguration, RedissonClient redissonClient, MdProductRepository mdProductRepository, IcProductStoreAccountService icProductStoreAccountService, String regionCode) {
        super(restTemplate, esConfiguration, redissonClient);
        this.mdProductRepository=mdProductRepository;
        this.regionCode=regionCode;
        this.icProductStoreAccountService=icProductStoreAccountService;
    }

    /**
     * 索引迁移条件。将指定条件数据迁移到新的索引
     * @return
     */
    @Override
    public ReindexRequest getReindex() {
        ReindexRequest reindexRequest=new ReindexRequest();
        reindexRequest.setSourceIndices(getBeforeIndexName());
        reindexRequest.setSourceQuery(QueryBuilders.boolQuery().mustNot(QueryBuilders.matchPhraseQuery("info","ic_product_store_account")));
        reindexRequest.setDestIndex(getTemplateIndexName());
        return reindexRequest;

    }

    /**
     * 已经迁移到经销权 先禁用
     * @param pageRequest
     * @return
     * @throws IOException
     */
    @Deprecated
    @Override
    public boolean processImport(PageRequest pageRequest) throws IOException {
        MdProduct mdProduct = new MdProduct();
        mdProduct.setRegionCode(regionCode);
        ExampleMatcher exampleMatcher = ExampleMatcher.matching().withMatcher("regionCode", ExampleMatcher.GenericPropertyMatchers.exact()).withIgnorePaths("optCounter","createdDate","updatedDate","id");;
        Example example=Example.of(mdProduct,exampleMatcher);
        Page<MdProduct> result = mdProductRepository.findAll(example,pageRequest);
        for (MdProduct md:result){
            if (md.getFullPalletSaleFlag()==null){
                md.setFullPalletSaleFlag(0);
            }
        }
        List<Long>  productIds=result.getContent().stream().map(MdProduct::getMdProductId).collect(Collectors.toList());
        FindProductCharacterStocksVo findProductCharacterStocksVo=new FindProductCharacterStocksVo();
        findProductCharacterStocksVo.setProductIds(productIds);
        findProductCharacterStocksVo.setRegionCode(regionCode);
        List<ProductCharacterStocksVo> productCharacterStocksVoList= icProductStoreAccountService.findProductCharacterStocks(findProductCharacterStocksVo);
        BulkRequest bulkRequest=new BulkRequest();
        for (ProductCharacterStocksVo productCharacterStocksVo:
                productCharacterStocksVoList) {
            if(productCharacterStocksVo.getCharacterId().longValue()==-1L){
                continue;
            }
            XContentBuilder builder = XContentFactory.jsonBuilder();
            builder.startObject();
            builder.field("productId",productCharacterStocksVo.getProductId());
            builder.field("factoryId",productCharacterStocksVo.getFactoryId());
            builder.field("characterId",productCharacterStocksVo.getCharacterId());
            builder.field("sumCount",productCharacterStocksVo.getSumCount());
            builder.field("regionCode",regionCode);
            builder.field("info").startObject()
                    .field("name","ic_product_store_account")
                    .field("parent",productCharacterStocksVo.getProductId()+"_"+regionCode)
                    .endObject();
            builder.endObject();
            String indexId="ic_product_store_account_"+productCharacterStocksVo.getProductId()+"_"+productCharacterStocksVo.getCharacterId()+"_"+productCharacterStocksVo.getFactoryId();
            IndexRequest indexRequest=new IndexRequest(getTemplateIndexName(),getEsConfiguration().getTypeName(), indexId);
            indexRequest.source(builder);
            indexRequest.routing(productCharacterStocksVo.getProductId()+"_"+regionCode);
            bulkRequest.add(indexRequest);
        }
        log.info(bulkRequest.getDescription());
        getEshlRestUtil().getClient().bulk(bulkRequest, RequestOptions.DEFAULT);
        return true;

    }

    /**
     * 数据总条数
     * @return
     */
    @Override
    public Long getCount() {
        MdProduct mdProduct = new MdProduct();
        mdProduct.setRegionCode(regionCode);
        ExampleMatcher exampleMatcher = ExampleMatcher.matching().
                withMatcher("regionCode", ExampleMatcher.GenericPropertyMatchers.exact()).withIgnorePaths("optCounter","createdDate","updatedDate","id");
        Example example=Example.of(mdProduct,exampleMatcher);
       return mdProductRepository.count(example);
    }
}



 



processImport就只用关系查询出指定分页的数据 然后导入到es