├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── github │ │ └── warriorgl │ │ ├── ElasticApplication.java │ │ ├── annotation │ │ └── FilterType.java │ │ ├── config │ │ └── ElasticSearchConfig.java │ │ ├── demo │ │ ├── DemoController.java │ │ ├── FilterBean.java │ │ └── QueryBean.java │ │ ├── enums │ │ └── FType.java │ │ └── tools │ │ ├── ESFilterWrapper.java │ │ ├── ESFunction.java │ │ ├── ESLambdaWrapper.java │ │ ├── ElasticSearchHelper.java │ │ ├── SearchResult.java │ │ └── SerializedLambdaUtils.java └── resources │ └── application.yml └── test └── java └── com └── github └── warriorgl └── ElasticApplicationTests.java /README.md: -------------------------------------------------------------------------------- 1 | ### # Elasticsearch-Helper 2 | -- QQ群:199016700-- 3 | 4 | ### 简介 5 | 6 | Elasticsearch-Helper 是一个Java对 Elasticsearch RHLC API的封装工具,通过封装简化官方API写法,降低相关API的学习成本,减少代码编写工作,提高工作效率。 7 | 8 | Elasticsearch-Helper当前版本主要简化了对各种格式数据的过滤操作。通过封装常用数据类型的过滤,通过添加注解即可根据某个字段进行过滤,来达到减少代码量的目的。 9 | 10 | 项目使用springboot框架创建。所以JDK版本需要8以上。 11 | 12 | ### 项目依赖版本说明 13 | Java版本:1.8 14 | ES版本:7.2.1 15 | ### 使用说明 16 | 1. 以Module方式加入已有项目中。删掉配置文件中的配置application.yml,在你的主项目配置文件中配置ES节点 elasticsearch.nodes。详见配置文件配置方式 17 | 2. 创建帮助类实例 18 | 19 | ``` 20 | @Bean("searchHelper") 21 | public ElasticSearchHelper elasticSearchHelper(@Autowired RestHighLevelClient highLevelClient){ 22 | return new ElasticSearchHelper(highLevelClient); 23 | } 24 | ``` 25 | 26 | 3. 在其他类中注入 searchHelper 即可。 27 | 28 | ### 注解说明 29 | 30 | 已FilterBean.java类为例讲解 31 | 32 | @FilterType注解的参数默认是字符串类型。注意属性类型不要定义成其他类型。因为给ES传值并不需要类型转换,所以统一使用字符串。 33 | 34 | @FilterType注解说明 35 | 36 | | 名称 | 说明 | 37 | |-------|-----------------| 38 | | value | 标注此值是什么类型,默认字符串;具体支持类型请查看@FType注解 | 39 | | ignoreValue| 忽略过滤值,例如ignoreValue = "0" 则ES中为0的数据就不过滤了。适用于想过滤某些数据但是还要保留部分数据。还有默认值的情况。 | 40 | | alias | 设置别名,适用于一个字段要进行不同的过滤操作,那么就可以创建多个字段把此值设置成想过滤的字段。| 41 | | separator | 分隔符,传多个数据分隔符,默认“,” | 42 | | exist | 是否当做过滤条件,默认ture | 43 | 44 | @FType注解说明 45 | | 名称 | 说明 | 46 | |----|----| 47 | | STRING | 字符串,默认类型。建议无特殊情况都用此类型 | 48 | | DATE | 日期类型 默认#号分隔 格式要求:2016#2019 可以通过设置 FilterType注解属性separator来改变分隔符 | 49 | | ARRAY | 值类型,默认逗号分隔 格式要求:a,b,c 可以通过设置 FilterType注解属性separator来改变分隔符 | 50 | | EXISTS | 查询的字段是否存在 true:不为空。 false:为空 | 51 | | MUST_NOT| 设置此值后,相当于取反。除去此值的所有数据 | 52 | | MATCHQUERY| 模糊匹配查询 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.1.RELEASE 9 | 10 | 11 | com.github.warriorgl 12 | elastic-helper 13 | 1.0 14 | elastic 15 | elasticsearch java api helper 16 | 17 | 18 | 1.8 19 | 7.11.2 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-devtools 31 | runtime 32 | true 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-configuration-processor 37 | true 38 | 39 | 40 | org.projectlombok 41 | lombok 42 | true 43 | 44 | 45 | org.apache.commons 46 | commons-lang3 47 | 3.7 48 | 49 | 50 | org.elasticsearch 51 | elasticsearch 52 | ${elasticsearch.version} 53 | 54 | 55 | org.elasticsearch.client 56 | elasticsearch-rest-client 57 | ${elasticsearch.version} 58 | 59 | 60 | org.elasticsearch.client 61 | elasticsearch-rest-high-level-client 62 | ${elasticsearch.version} 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-starter-test 67 | test 68 | 69 | 70 | org.junit.vintage 71 | junit-vintage-engine 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-maven-plugin 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/ElasticApplication.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl; 2 | 3 | import com.github.warriorgl.tools.ElasticSearchHelper; 4 | import org.elasticsearch.client.RestHighLevelClient; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.context.annotation.Bean; 9 | 10 | @SpringBootApplication 11 | public class ElasticApplication { 12 | 13 | public static void main(String[] args) { 14 | SpringApplication.run(ElasticApplication.class, args); 15 | } 16 | 17 | 18 | @Bean("searchHelper") 19 | public ElasticSearchHelper elasticSearchHelper(@Autowired RestHighLevelClient highLevelClient){ 20 | return new ElasticSearchHelper(highLevelClient); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/annotation/FilterType.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.annotation; 2 | 3 | import com.github.warriorgl.enums.FType; 4 | 5 | import java.lang.annotation.*; 6 | 7 | @Target(ElementType.FIELD) 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Documented 10 | public @interface FilterType { 11 | 12 | /** 13 | * 过滤类型 14 | * @see FType 15 | * @return 16 | */ 17 | FType value() default FType.STRING; 18 | 19 | /** 20 | * 忽略过滤 如果设置此值,为此值则跳过 21 | * @return 22 | */ 23 | String ignoreValue() default ""; 24 | 25 | 26 | /** 27 | * 过滤别名 如果一个字段检索条件不一样可使用 28 | * @return 29 | */ 30 | String alias() default ""; 31 | 32 | /** 33 | * 多个数据分隔符,默认逗号 34 | * @return 35 | */ 36 | String separator() default ","; 37 | 38 | 39 | /** 40 | * 是否存在此条件 默认true 41 | * @return 42 | */ 43 | boolean exist() default true; 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/config/ElasticSearchConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.config; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.apache.http.HttpHost; 5 | import org.elasticsearch.client.RestClient; 6 | import org.elasticsearch.client.RestClientBuilder; 7 | import org.elasticsearch.client.RestHighLevelClient; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | 13 | import java.util.Arrays; 14 | import java.util.Objects; 15 | 16 | @Configuration 17 | public class ElasticSearchConfig { 18 | 19 | @Value("${elasticsearch.nodes}") 20 | private String[] nodes; 21 | 22 | 23 | @Bean 24 | public RestClientBuilder restClientBuilder() { 25 | HttpHost[] hosts = Arrays.stream(nodes) 26 | .map(this::makeHttpHost) 27 | .filter(Objects::nonNull) 28 | .toArray(HttpHost[]::new); 29 | return RestClient.builder(hosts); 30 | } 31 | 32 | 33 | @Bean(name = "highLevelClient") 34 | public RestHighLevelClient highLevelClient(@Autowired RestClientBuilder restClientBuilder) { 35 | restClientBuilder.setHttpClientConfigCallback(httpClientBuilder -> { 36 | httpClientBuilder.setMaxConnTotal(100); 37 | httpClientBuilder.setMaxConnPerRoute(100); 38 | return httpClientBuilder; 39 | }); 40 | restClientBuilder.setRequestConfigCallback(requestConfigBuilder -> { 41 | requestConfigBuilder.setConnectTimeout(5000); 42 | requestConfigBuilder.setSocketTimeout(30000); 43 | requestConfigBuilder.setConnectionRequestTimeout(500); 44 | return requestConfigBuilder; 45 | }); 46 | return new RestHighLevelClient(restClientBuilder); 47 | } 48 | 49 | 50 | private HttpHost makeHttpHost(String s) { 51 | assert StringUtils.isNotEmpty(s); 52 | String[] address = s.split(":"); 53 | if (address.length == 2) { 54 | String ip = address[0]; 55 | int port = Integer.parseInt(address[1]); 56 | return new HttpHost(ip, port, "http"); 57 | } else { 58 | return null; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/demo/DemoController.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.demo; 2 | 3 | import com.github.warriorgl.tools.ElasticSearchHelper; 4 | import com.github.warriorgl.tools.ESFilterWrapper; 5 | import com.github.warriorgl.tools.ESLambdaWrapper; 6 | import com.github.warriorgl.tools.SearchResult; 7 | import lombok.Data; 8 | import org.elasticsearch.action.DocWriteRequest; 9 | import org.elasticsearch.action.delete.DeleteRequest; 10 | import org.elasticsearch.action.search.SearchRequest; 11 | import org.elasticsearch.common.Strings; 12 | import org.elasticsearch.common.unit.TimeValue; 13 | import org.elasticsearch.index.query.BoolQueryBuilder; 14 | import org.elasticsearch.index.query.QueryBuilder; 15 | import org.elasticsearch.index.query.QueryBuilders; 16 | import org.elasticsearch.search.aggregations.AggregationBuilders; 17 | import org.elasticsearch.search.aggregations.BucketOrder; 18 | import org.elasticsearch.search.builder.SearchSourceBuilder; 19 | import org.elasticsearch.search.sort.FieldSortBuilder; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.web.bind.annotation.RestController; 22 | 23 | import java.io.IOException; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | /** 29 | * 示例 30 | */ 31 | @RestController 32 | public class DemoController { 33 | 34 | 35 | @Autowired 36 | private ElasticSearchHelper searchHelper; 37 | 38 | /** 39 | * 检索示例 40 | * @param queryBean 41 | * @param filterBean 42 | * @return 43 | */ 44 | public SearchResult keyword(QueryBean queryBean, FilterBean filterBean){ 45 | SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); 46 | BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); 47 | 48 | /*过滤*/ 49 | List filterList = boolQueryBuilder.filter(); 50 | filterList.addAll(new ESFilterWrapper().filter(filterBean)); 51 | 52 | List boolmustList = boolQueryBuilder.must(); 53 | /*查询*/ 54 | boolmustList.add(QueryBuilders.matchQuery("title",queryBean.getKeyword())); 55 | 56 | searchSourceBuilder.query(boolQueryBuilder); 57 | 58 | return requestResult(queryBean,searchSourceBuilder); 59 | } 60 | 61 | 62 | /** 63 | * 单条更新示例 64 | * @throws IOException 65 | */ 66 | public void lambdaUpdateDocument(){ 67 | ESBean bean= new ESBean(); 68 | bean.setCode("1"); 69 | bean.setName("2"); 70 | ESLambdaWrapper lambdaWrapper= new ESLambdaWrapper(); 71 | lambdaWrapper.docId("1").add(ESBean::getCode,"1").add(ESBean::getName,"2"); 72 | searchHelper.lambdaUpdateDocument(lambdaWrapper); 73 | } 74 | 75 | 76 | /** 77 | * 单条创建示例 78 | */ 79 | public void lambdaCreateDocument(){ 80 | ESBean bean= new ESBean(); 81 | bean.setCode("1"); 82 | bean.setName("2"); 83 | ESLambdaWrapper lambdaWrapper= new ESLambdaWrapper(); 84 | lambdaWrapper.docId("1").add(ESBean::getCode,"1").add(ESBean::getName,"2"); 85 | searchHelper.lambdaCreateDocument(lambdaWrapper); 86 | } 87 | 88 | 89 | /** 90 | * 删除 91 | * @throws IOException 92 | */ 93 | public void delete() throws IOException { 94 | searchHelper.deleteDocument(new DeleteRequest().id("1")); 95 | } 96 | 97 | /** 98 | * 批量操作 99 | */ 100 | public void bulkDocument(){ 101 | List> requests = new ArrayList<>(); 102 | } 103 | 104 | 105 | private SearchResult requestResult(QueryBean queryBean,SearchSourceBuilder searchSourceBuilder) { 106 | SearchRequest firstSearchRequest = new SearchRequest("indexName"); 107 | searchSourceBuilder.trackTotalHits(true); //设置返回总数 108 | searchSourceBuilder.trackScores(true); 109 | searchSourceBuilder.size(queryBean.getSize()).from((queryBean.getPage()-1)*queryBean.getSize()); 110 | searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); 111 | /*返回字段*/ 112 | searchSourceBuilder.fetchSource(new String[]{"title"}, Strings.EMPTY_ARRAY); 113 | /*排序*/ 114 | searchSourceBuilder.sort(new FieldSortBuilder(queryBean.getSortField()).order(queryBean.getSortOrder())); 115 | /*学科聚合*/ 116 | searchSourceBuilder.aggregation(AggregationBuilders.terms("suject_count").field("suject_code").size(100000).order(BucketOrder.key(true))); 117 | firstSearchRequest.source(searchSourceBuilder); 118 | SearchResult searchResult = searchHelper.searchDocument(firstSearchRequest); 119 | return searchResult; 120 | } 121 | 122 | 123 | @Data 124 | public class ESBean { 125 | private String name; 126 | 127 | private String code; 128 | } 129 | 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/demo/FilterBean.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.demo; 2 | 3 | import com.github.warriorgl.annotation.FilterType; 4 | import com.github.warriorgl.enums.FType; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class FilterBean { 9 | 10 | /** 11 | * 开始结束时间中间使用#分隔 12 | */ 13 | @FilterType(value = FType.DATE) 14 | private String testYear; 15 | 16 | /** 17 | * 语言 数组逗号分隔 18 | */ 19 | @FilterType(value = FType.ARRAY) 20 | private String testArray; 21 | 22 | 23 | /** 24 | * 字符串过滤类型 ignoreValue=0的不过滤 25 | */ 26 | @FilterType(value = FType.STRING, ignoreValue = "0") 27 | private String testString; 28 | 29 | 30 | /** 31 | * 匹配过滤 32 | */ 33 | @FilterType(value = FType.MATCHQUERY) 34 | private String publisher; 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/demo/QueryBean.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.demo; 2 | 3 | 4 | import lombok.Data; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.elasticsearch.search.sort.SortOrder; 7 | 8 | @Data 9 | public class QueryBean { 10 | 11 | private String keyword; 12 | 13 | private String sortField; 14 | 15 | private String sort = "asc"; 16 | 17 | private Integer size=20; 18 | 19 | private Integer page=1; 20 | 21 | 22 | public SortOrder getSortOrder(){ 23 | if (StringUtils.lowerCase(this.sort).equals("desc")){ 24 | return SortOrder.DESC; 25 | } 26 | return SortOrder.ASC; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/enums/FType.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.enums; 2 | 3 | public enum FType { 4 | /** 5 | * 字符串,默认类型。建议无特殊情况都用此类型 6 | */ 7 | STRING, 8 | /** 9 | * 日期类型 #号分隔 格式要求:2016#2019 10 | */ 11 | DATE, 12 | /** 13 | * 多值类型,逗号分隔 格式要求:a,b,c 14 | */ 15 | ARRAY, 16 | 17 | /** 18 | * 查询的字段内容是否为空 true:不为空。 false:为空 19 | */ 20 | EXISTS, 21 | 22 | /** 23 | * 内容不查询 24 | */ 25 | MUST_NOT, 26 | 27 | /** 28 | * 模糊检索 29 | */ 30 | MATCHQUERY; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/tools/ESFilterWrapper.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.tools; 2 | 3 | import com.github.warriorgl.annotation.FilterType; 4 | import com.github.warriorgl.enums.FType; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.elasticsearch.index.query.BoolQueryBuilder; 8 | import org.elasticsearch.index.query.MatchQueryBuilder; 9 | import org.elasticsearch.index.query.QueryBuilder; 10 | import org.elasticsearch.index.query.QueryBuilders; 11 | import java.io.Serializable; 12 | import java.lang.reflect.Field; 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | 18 | @Slf4j 19 | public class ESFilterWrapper implements Serializable { 20 | 21 | 22 | public List filter(E filterObj) { 23 | try { 24 | List filterList = new ArrayList<>(); 25 | Field[] fields = filterObj.getClass().getDeclaredFields(); 26 | for (Field field : fields) { 27 | field.setAccessible(true); 28 | String value = (String) field.get(filterObj); 29 | if (StringUtils.isBlank(value)) { 30 | continue; 31 | } 32 | FilterType filterType = field.getAnnotation(FilterType.class); 33 | if (!filterType.exist()){ //忽略字段 34 | continue; 35 | } 36 | /*获取过滤类型*/ 37 | FType current = filterType != null ? filterType.value() : FType.STRING; 38 | /*处理过滤别名*/ 39 | String fieldName = field.getName(); 40 | if (filterType != null && !"".equals(filterType.alias())) { 41 | fieldName = filterType.alias(); 42 | } 43 | /*处理过滤 特殊值*/ 44 | String ignoreValue = filterType == null ? "" : filterType.ignoreValue(); 45 | if (ignoreValue.equals(value)) { 46 | continue; 47 | } 48 | if (current == FType.STRING) { 49 | filterList.add(QueryBuilders.termQuery(fieldName, value)); 50 | } else if (current == FType.DATE) { 51 | buildDateQuery(filterList, value, filterType, fieldName); 52 | } else if (current == FType.ARRAY) { 53 | buildArrayQuery(filterList, value, filterType, fieldName); 54 | } else if (current == FType.EXISTS) { 55 | buildExistsQuery(filterList, value, fieldName); 56 | } else if (current == FType.MUST_NOT){ 57 | buildMustNotQuery(filterList, value, filterType, fieldName); 58 | } else if (current == FType.MATCHQUERY){ 59 | buildMatchQuery(filterList, value, fieldName); 60 | } 61 | } 62 | return filterList; 63 | } catch (IllegalAccessException i) { 64 | log.error("filterwrapper exception:{}", i.getLocalizedMessage()); 65 | } 66 | return Collections.EMPTY_LIST; 67 | } 68 | 69 | 70 | private void buildMatchQuery(List filterList, String value, String fieldName){ 71 | MatchQueryBuilder fieldQuery = QueryBuilders.matchQuery(fieldName, value); 72 | filterList.add(fieldQuery); 73 | } 74 | 75 | 76 | private void buildMustNotQuery(List filterList, String value, FilterType filterType, String fieldName) { 77 | String[] array = value.split(filterType.separator()); 78 | BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); 79 | for (String a : array) { 80 | boolQueryBuilder.mustNot(QueryBuilders.termQuery(fieldName, a)); 81 | } 82 | filterList.add(boolQueryBuilder); 83 | } 84 | 85 | 86 | private void buildExistsQuery(List filterList, String value, String fieldName) { 87 | BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); 88 | boolean bvalue = Boolean.valueOf(value); 89 | if (bvalue) { 90 | filterList.add(QueryBuilders.existsQuery(fieldName)); 91 | } else { 92 | boolQueryBuilder.mustNot(QueryBuilders.existsQuery(fieldName)); 93 | filterList.add(boolQueryBuilder); 94 | } 95 | } 96 | 97 | private void buildArrayQuery(List filterList, String value, FilterType filterType, String fieldName) { 98 | String[] array = value.split(filterType.separator()); 99 | String finalFieldName = fieldName; 100 | BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); 101 | for (String a : array) { 102 | boolQueryBuilder.should(QueryBuilders.termQuery(finalFieldName, a)); 103 | } 104 | filterList.add(boolQueryBuilder); 105 | } 106 | 107 | private void buildDateQuery(List filterList, String value, FilterType filterType, String fieldName) { 108 | String[] dateRange = value.split(filterType.separator()); 109 | if (value.startsWith(filterType.separator())) { 110 | filterList.add(QueryBuilders.rangeQuery(fieldName).to(dateRange[1])); 111 | } else if (value.endsWith(filterType.separator())) { 112 | filterList.add(QueryBuilders.rangeQuery(fieldName).from(dateRange[0])); 113 | } else { 114 | filterList.add(QueryBuilders.rangeQuery(fieldName).from(dateRange[0]).to(dateRange[1])); 115 | } 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/tools/ESFunction.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.tools; 2 | import java.io.Serializable; 3 | import java.util.function.Function; 4 | 5 | @FunctionalInterface 6 | public interface ESFunction extends Function,Serializable { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/tools/ESLambdaWrapper.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.tools; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | 5 | import java.io.Serializable; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | @Slf4j 10 | public class ESLambdaWrapper implements Serializable { 11 | 12 | private Map fieldMap =new HashMap<>(); 13 | 14 | private String docId; 15 | 16 | private String index; 17 | 18 | public ESLambdaWrapper add(ESFunction lambda, Object object){ 19 | String fieldName=SerializedLambdaUtils.convertToFieldName(lambda); 20 | fieldMap.put(fieldName,object); 21 | return this; 22 | } 23 | 24 | 25 | public ESLambdaWrapper docId(String id){ 26 | this.docId=id; 27 | return this; 28 | } 29 | 30 | public ESLambdaWrapper index(String index){ 31 | this.index=index; 32 | return this; 33 | } 34 | 35 | 36 | public String getIndex() { 37 | return index; 38 | } 39 | 40 | public String getDocId() { 41 | return docId; 42 | } 43 | 44 | public Map getFieldMap() { 45 | return fieldMap; 46 | } 47 | 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/tools/ElasticSearchHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.tools; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.elasticsearch.action.ActionListener; 6 | import org.elasticsearch.action.DocWriteRequest; 7 | import org.elasticsearch.action.DocWriteResponse; 8 | import org.elasticsearch.action.bulk.BulkItemResponse; 9 | import org.elasticsearch.action.bulk.BulkRequest; 10 | import org.elasticsearch.action.bulk.BulkResponse; 11 | import org.elasticsearch.action.delete.DeleteRequest; 12 | import org.elasticsearch.action.delete.DeleteResponse; 13 | import org.elasticsearch.action.index.IndexRequest; 14 | import org.elasticsearch.action.search.SearchRequest; 15 | import org.elasticsearch.action.search.SearchResponse; 16 | import org.elasticsearch.action.update.UpdateRequest; 17 | import org.elasticsearch.client.RequestOptions; 18 | import org.elasticsearch.client.RestHighLevelClient; 19 | import org.elasticsearch.client.core.CountRequest; 20 | import org.elasticsearch.client.core.CountResponse; 21 | import org.elasticsearch.common.text.Text; 22 | import org.elasticsearch.common.xcontent.XContentType; 23 | import org.elasticsearch.index.reindex.BulkByScrollResponse; 24 | import org.elasticsearch.index.reindex.ScrollableHitSource; 25 | import org.elasticsearch.index.reindex.UpdateByQueryRequest; 26 | import org.elasticsearch.search.SearchHit; 27 | import org.elasticsearch.search.aggregations.Aggregation; 28 | import org.elasticsearch.search.aggregations.Aggregations; 29 | import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms; 30 | import org.elasticsearch.search.aggregations.bucket.terms.Terms; 31 | import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; 32 | import java.io.IOException; 33 | import java.util.*; 34 | 35 | @Slf4j 36 | public class ElasticSearchHelper { 37 | 38 | 39 | private RestHighLevelClient highLevelClient; 40 | 41 | 42 | public ElasticSearchHelper(RestHighLevelClient highLevelClient) { 43 | this.highLevelClient = highLevelClient; 44 | } 45 | 46 | 47 | /** 48 | * 批量处理数据 49 | * DocWriteRequest? 50 | * ->IndexRequest 51 | * ->UpdateRequest 52 | * ->DeleteRequest 53 | * 54 | * @param requests 55 | * @return 56 | */ 57 | public BulkResponse bulkDocument(List> requests) throws IOException { 58 | BulkRequest bulkRequest = new BulkRequest(); 59 | for (DocWriteRequest writeRequest : requests) { 60 | bulkRequest.add(writeRequest); 61 | } 62 | return highLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT); 63 | } 64 | 65 | 66 | public void indexDocument(IndexRequest indexRequest) throws IOException { 67 | highLevelClient.index(indexRequest,RequestOptions.DEFAULT); 68 | } 69 | 70 | /** 71 | * 异步批量数据处理 72 | * DocWriteRequest? 73 | * ->IndexRequest 74 | * ->UpdateRequest 75 | * ->DeleteRequest 76 | * 77 | * @param requests 78 | */ 79 | public void bulkAsyncDocument(List> requests) { 80 | bulkAsyncListenerDocument(requests, actionListener()); 81 | } 82 | 83 | /** 84 | * 异步批量数据处理-自定义响应 85 | * 86 | * @param requests 87 | * @param actionListener 88 | */ 89 | public void bulkAsyncListenerDocument(List> requests, ActionListener actionListener) { 90 | BulkRequest bulkRequest = new BulkRequest(); 91 | for (DocWriteRequest writeRequest : requests) { 92 | bulkRequest.add(writeRequest); 93 | } 94 | highLevelClient.bulkAsync(bulkRequest, RequestOptions.DEFAULT, actionListener); 95 | } 96 | 97 | 98 | private ActionListener actionListener() { 99 | ActionListener listener = new ActionListener() { 100 | @Override 101 | public void onResponse(BulkResponse bulkResponse) { 102 | if (bulkResponse.hasFailures()) { 103 | log.error("Increased resource failure causes:{}", bulkResponse.buildFailureMessage()); 104 | } 105 | } 106 | 107 | @Override 108 | public void onFailure(Exception e) { 109 | log.error("Asynchronous batch increases data exceptions:{}", e.getLocalizedMessage()); 110 | } 111 | }; 112 | return listener; 113 | } 114 | 115 | 116 | /** 117 | * 检索 118 | * 119 | * @param searchRequest 120 | * @return 121 | */ 122 | public SearchResult searchDocument(SearchRequest searchRequest) { 123 | List> list = new ArrayList<>(); 124 | SearchResponse searchResponse; 125 | try { 126 | searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT); 127 | SearchHit[] searchHit = searchResponse.getHits().getHits(); 128 | searchResponse.getTook().getMillis(); 129 | long totalHits = searchResponse.getHits().getTotalHits().value; 130 | long took = searchResponse.getTook().getMillis(); 131 | for (SearchHit document : searchHit) { 132 | Map item = document.getSourceAsMap(); 133 | if (item == null) { 134 | continue; 135 | } 136 | Map highlightFields = document.getHighlightFields(); 137 | if (!highlightFields.isEmpty()) { 138 | for (String key : highlightFields.keySet()) { 139 | Text[] fragments = highlightFields.get(key).fragments(); 140 | if (item.containsKey(key)) { 141 | item.put(key, fragments[0].string()); 142 | } 143 | String[] fieldArray = key.split("[.]"); 144 | if (fieldArray.length > 1) { 145 | item.put(fieldArray[0], fragments[0].string()); 146 | } 147 | } 148 | } 149 | list.add(item); 150 | } 151 | Map> aggregations = getAggregation(searchResponse.getAggregations()); 152 | return new SearchResult(totalHits, list, took, aggregations); 153 | } catch (Exception e) { 154 | e.printStackTrace(); 155 | } 156 | return new SearchResult(); 157 | } 158 | 159 | 160 | private Map> getAggregation(Aggregations aggregations) { 161 | if (aggregations == null) { 162 | return Collections.EMPTY_MAP; 163 | } 164 | Map> result = new HashMap<>(); 165 | Map aggregationMap = aggregations.getAsMap(); 166 | aggregationMap.forEach((k, v) -> { 167 | Map agg = new HashMap<>(); 168 | List buckets = ((ParsedStringTerms) v).getBuckets(); 169 | for (Terms.Bucket bucket : buckets) { 170 | agg.put(bucket.getKeyAsString(), bucket.getDocCount()); 171 | } 172 | result.put(k, agg); 173 | }); 174 | return result; 175 | } 176 | 177 | 178 | /** 179 | * 删除 180 | * 181 | * @param request 182 | * @return 183 | */ 184 | public Boolean deleteDocument(DeleteRequest request) throws IOException { 185 | DeleteResponse deleteResponse = highLevelClient.delete(request, RequestOptions.DEFAULT); 186 | if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) { 187 | log.info("not found doc id:{}", deleteResponse.getId()); 188 | return false; 189 | } 190 | if (deleteResponse.getResult() == DocWriteResponse.Result.DELETED) { 191 | return true; 192 | } 193 | log.info("deleteResponse Status:{}", deleteResponse.status()); 194 | return false; 195 | } 196 | 197 | 198 | /** 199 | * 异步查询更新 200 | * 201 | * @param request 202 | */ 203 | public void updateByQueryDocument(UpdateByQueryRequest request) { 204 | try { 205 | highLevelClient.updateByQuery(request, RequestOptions.DEFAULT); 206 | } catch (IOException e) { 207 | log.error("updateByQuery Exception {}", e.getLocalizedMessage()); 208 | } 209 | } 210 | 211 | 212 | private ActionListener bulkByScrolllistener() { 213 | return new ActionListener() { 214 | @Override 215 | public void onResponse(BulkByScrollResponse bulkResponse) { 216 | List failures = bulkResponse.getBulkFailures(); 217 | if (!failures.isEmpty()) { 218 | log.error("BulkByScrollResponse failures:{}", StringUtils.join(failures, "@")); 219 | } 220 | List searchFailures = bulkResponse.getSearchFailures(); 221 | if (!failures.isEmpty()) { 222 | log.error("BulkByScrollResponse searchFailures:{}", StringUtils.join(searchFailures, "@@")); 223 | } 224 | } 225 | 226 | @Override 227 | public void onFailure(Exception e) { 228 | log.error("BulkByScrollResponse Exceptions:{}", e.getLocalizedMessage()); 229 | } 230 | }; 231 | } 232 | 233 | /** 234 | * 个数查询 235 | * 236 | * @param countRequest 237 | * @return 238 | */ 239 | public Long countDocument(CountRequest countRequest) { 240 | try { 241 | CountResponse countResponse = highLevelClient.count(countRequest, RequestOptions.DEFAULT); 242 | return countResponse.getCount(); 243 | } catch (IOException e) { 244 | log.error("CountResponse Exceptions:{}", e.getLocalizedMessage()); 245 | return 0L; 246 | } 247 | } 248 | 249 | 250 | public boolean updateDocument(UpdateRequest updateRequest) { 251 | try { 252 | highLevelClient.update(updateRequest, RequestOptions.DEFAULT); 253 | return true; 254 | } catch (Exception e) { 255 | e.printStackTrace(); 256 | log.error("UpdateRequest Exception:{}", e.getLocalizedMessage()); 257 | return false; 258 | } 259 | } 260 | 261 | 262 | public boolean lambdaUpdateDocument(ESLambdaWrapper updateWrapper){ 263 | if (StringUtils.isBlank(updateWrapper.getDocId())) { 264 | log.warn("id does not exist:{}",updateWrapper.getDocId()); 265 | return false; 266 | } 267 | if (updateWrapper.getFieldMap().isEmpty()) { 268 | log.warn("content is null"); 269 | return false; 270 | } 271 | try { 272 | UpdateRequest request = new UpdateRequest(updateWrapper.getIndex(), updateWrapper.getDocId()); 273 | request.doc(updateWrapper.getFieldMap()); 274 | highLevelClient.update(request, RequestOptions.DEFAULT); 275 | }catch (IOException io){ 276 | log.error("lambdaUpdateDocument Exception:{}",io.getMessage()); 277 | return false; 278 | } 279 | return true; 280 | } 281 | 282 | 283 | public boolean lambdaCreateDocument(ESLambdaWrapper indexWrapper){ 284 | if (indexWrapper.getFieldMap().isEmpty()) { 285 | log.warn("content is null"); 286 | return false; 287 | } 288 | try { 289 | IndexRequest request = new IndexRequest(indexWrapper.getIndex()).id(indexWrapper.getDocId()); 290 | request.source(indexWrapper.getFieldMap(), XContentType.JSON); 291 | highLevelClient.index(request, RequestOptions.DEFAULT); 292 | }catch (IOException io){ 293 | log.error("lambdaCreateDocument Exception:{}",io.getMessage()); 294 | return false; 295 | } 296 | return true; 297 | } 298 | 299 | 300 | } 301 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/tools/SearchResult.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.tools; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | @Data 10 | public class SearchResult { 11 | 12 | /** 13 | * 命中总数 14 | */ 15 | public Long totalHits; 16 | 17 | /** 18 | * 返回文档集合 19 | */ 20 | public List> documents; 21 | 22 | /** 23 | * 查询耗时 单位毫秒 24 | */ 25 | public Long took; 26 | 27 | 28 | /** 29 | * 聚合结果 30 | */ 31 | public Map> aggregations; 32 | 33 | 34 | 35 | public SearchResult(Long totalHits, 36 | List> documents, 37 | Long took, 38 | Map> aggregations) { 39 | this.totalHits = totalHits; 40 | this.documents = documents; 41 | this.took = took; 42 | this.aggregations = aggregations; 43 | } 44 | 45 | 46 | public SearchResult() { 47 | this.totalHits=0L; 48 | this.documents =new ArrayList<>(); 49 | this.took=0L; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/warriorgl/tools/SerializedLambdaUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl.tools; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import java.io.Serializable; 6 | import java.lang.invoke.SerializedLambda; 7 | import java.lang.reflect.Method; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | public class SerializedLambdaUtils implements Serializable { 12 | 13 | private static Map CLASS_MAP = new ConcurrentHashMap<>(); 14 | 15 | 16 | public static String convertToFieldName(ESFunction fn){ 17 | SerializedLambda lambda = getSerializedLambda(fn); 18 | String str=lambda.getImplMethodName(); 19 | String fieldName=str.substring(3,str.length()); 20 | return StringUtils.uncapitalize(fieldName); 21 | } 22 | 23 | public static SerializedLambda getSerializedLambda(Serializable fn) { 24 | SerializedLambda lambda = CLASS_MAP.get(fn.getClass()); 25 | if(lambda == null) { 26 | try { 27 | Method method = fn.getClass().getDeclaredMethod("writeReplace"); 28 | method.setAccessible(Boolean.TRUE); 29 | lambda = (SerializedLambda) method.invoke(fn); 30 | CLASS_MAP.put(fn.getClass(), lambda); 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | return lambda; 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | compression: 4 | enabled: true 5 | servlet: 6 | session: 7 | timeout: 3600s 8 | cookie: 9 | max-age: 36000s 10 | #集群节点 11 | elasticsearch: 12 | nodes: 192.168.1.146:9200,192.168.1.155:9200,192.168.1.152:9200 13 | -------------------------------------------------------------------------------- /src/test/java/com/github/warriorgl/ElasticApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.github.warriorgl; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class ElasticApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | --------------------------------------------------------------------------------