1.抽象接口定义

 public abstract class SearchQueryEngine<T> {

     @Autowired
protected ElasticsearchTemplate elasticsearchTemplate; public abstract int saveOrUpdate(List<T> list); public abstract <R> List<R> aggregation(T query, Class<R> clazz); public abstract <R> Page<R> scroll(T query, Class<R> clazz, Pageable pageable, ScrollId scrollId); public abstract <R> List<R> find(T query, Class<R> clazz, int size); public abstract <R> Page<R> find(T query, Class<R> clazz, Pageable pageable); public abstract <R> R sum(T query, Class<R> clazz); protected Document getDocument(T t) {
Document annotation = t.getClass().getAnnotation(Document.class);
if (annotation == null) {
throw new SearchQueryBuildException("Can't find annotation @Document on " + t.getClass().getName());
}
return annotation;
} /**
* 获取字段名,若设置column则返回该值
*
* @param field
* @param column
* @return
*/
protected String getFieldName(Field field, String column) {
return StringUtils.isNotBlank(column) ? column : field.getName();
} /**
* 设置属性值
*
* @param field
* @param obj
* @param value
*/
protected void setFieldValue(Field field, Object obj, Object value) {
boolean isAccessible = field.isAccessible();
field.setAccessible(true);
try {
switch (field.getType().getSimpleName()) {
case "BigDecimal":
field.set(obj, new BigDecimal(value.toString()).setScale(5, BigDecimal.ROUND_HALF_UP));
break;
case "Long":
field.set(obj, new Long(value.toString()));
break;
case "Integer":
field.set(obj, new Integer(value.toString()));
break;
case "Date":
field.set(obj, new Date(Long.valueOf(value.toString())));
break;
default:
field.set(obj, value);
}
} catch (IllegalAccessException e) {
throw new SearchQueryBuildException(e);
} finally {
field.setAccessible(isAccessible);
}
} /**
* 获取字段值
*
* @param field
* @param obj
* @return
*/
protected Object getFieldValue(Field field, Object obj) {
boolean isAccessible = field.isAccessible();
field.setAccessible(true);
try {
return field.get(obj);
} catch (IllegalAccessException e) {
throw new SearchQueryBuildException(e);
} finally {
field.setAccessible(isAccessible);
}
} /**
* 转换为es识别的value值
*
* @param value
* @return
*/
protected Object formatValue(Object value) {
if (value instanceof Date) {
return ((Date) value).getTime();
} else {
return value;
}
} /**
* 获取索引分区数
*
* @param t
* @return
*/
protected int getNumberOfShards(T t) {
return Integer.parseInt(elasticsearchTemplate.getSetting(getDocument(t).index()).get(IndexMetaData.SETTING_NUMBER_OF_SHARDS).toString());
}
}

2.接口实现

 @Component
@ComponentScan
public class SimpleSearchQueryEngine<T> extends SearchQueryEngine<T> { private int numberOfRowsPerScan = 10; @Override
public int saveOrUpdate(List<T> list) {
if (CollectionUtils.isEmpty(list)) {
return 0;
} T base = list.get(0);
Field id = null;
for (Field field : base.getClass().getDeclaredFields()) {
BusinessID businessID = field.getAnnotation(BusinessID.class);
if (businessID != null) {
id = field;
break;
}
}
if (id == null) {
throw new SearchQueryBuildException("Can't find @BusinessID on " + base.getClass().getName());
} Document document = getDocument(base);
List<UpdateQuery> bulkIndex = new ArrayList<>();
for (T t : list) {
UpdateQuery updateQuery = new UpdateQuery();
updateQuery.setIndexName(document.index());
updateQuery.setType(document.type());
updateQuery.setId(getFieldValue(id, t).toString());
updateQuery.setUpdateRequest(new UpdateRequest(updateQuery.getIndexName(), updateQuery.getType(), updateQuery.getId()).doc(JSONObject.toJSONString(t, SerializerFeature.WriteMapNullValue)));
updateQuery.setDoUpsert(true);
updateQuery.setClazz(t.getClass());
bulkIndex.add(updateQuery);
}
elasticsearchTemplate.bulkUpdate(bulkIndex);
return list.size();
} @Override
public <R> List<R> aggregation(T query, Class<R> clazz) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = buildNativeSearchQueryBuilder(query);
nativeSearchQueryBuilder.addAggregation(buildGroupBy(query));
Aggregations aggregations = elasticsearchTemplate.query(nativeSearchQueryBuilder.build(), new AggregationResultsExtractor());
try {
return transformList(null, aggregations, clazz.newInstance(), new ArrayList());
} catch (Exception e) {
throw new SearchResultBuildException(e);
}
} /**
* 将Aggregations转为List
*
* @param terms
* @param aggregations
* @param baseObj
* @param resultList
* @param <R>
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
* @throws InstantiationException
*/
private <R> List<R> transformList(Aggregation terms, Aggregations aggregations, R baseObj, List<R> resultList) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
for (String column : aggregations.asMap().keySet()) {
Aggregation childAggregation = aggregations.get(column);
if (childAggregation instanceof InternalSum) {
// 使用@Sum
if (!(terms instanceof InternalSum)) {
R targetObj = (R) baseObj.getClass().newInstance();
BeanUtils.copyProperties(baseObj, targetObj);
resultList.add(targetObj);
}
setFieldValue(baseObj.getClass().getDeclaredField(column), resultList.get(resultList.size() - 1), ((InternalSum) childAggregation).getValue());
terms = childAggregation;
} else {
Terms childTerms = (Terms) childAggregation;
for (Terms.Bucket bucket : childTerms.getBuckets()) {
if (CollectionUtils.isEmpty(bucket.getAggregations().asList())) {
// 未使用@Sum
R targetObj = (R) baseObj.getClass().newInstance();
BeanUtils.copyProperties(baseObj, targetObj);
setFieldValue(targetObj.getClass().getDeclaredField(column), targetObj, bucket.getKey());
resultList.add(targetObj);
} else {
setFieldValue(baseObj.getClass().getDeclaredField(column), baseObj, bucket.getKey());
transformList(childTerms, bucket.getAggregations(), baseObj, resultList);
}
}
}
}
return resultList;
} @Override
public <R> Page<R> scroll(T query, Class<R> clazz, Pageable pageable, ScrollId scrollId) {
if (pageable.getPageSize() % numberOfRowsPerScan > 0) {
throw new SearchQueryBuildException("Page size must be an integral multiple of " + numberOfRowsPerScan);
}
SearchQuery searchQuery = buildNativeSearchQueryBuilder(query).withPageable(new PageRequest(pageable.getPageNumber(), numberOfRowsPerScan / getNumberOfShards(query), pageable.getSort())).build();
if (StringUtils.isEmpty(scrollId.getValue())) {
scrollId.setValue(elasticsearchTemplate.scan(searchQuery, 10000l, false));
}
Page<R> page = elasticsearchTemplate.scroll(scrollId.getValue(), 10000l, clazz);
if (page == null || page.getContent().size() == 0) {
elasticsearchTemplate.clearScroll(scrollId.getValue());
}
return page;
} @Override
public <R> List<R> find(T query, Class<R> clazz, int size) {
// Caused by: QueryPhaseExecutionException[Result window is too large, from + size must be less than or equal to: [10000] but was [2147483647].
// See the scroll api for a more efficient way to request large data sets. This limit can be set by changing the [index.max_result_window] index level parameter.]
if (size % numberOfRowsPerScan > 0) {
throw new SearchQueryBuildException("Parameter 'size' must be an integral multiple of " + numberOfRowsPerScan);
}
int pageNum = 0;
List<R> result = new ArrayList<>();
ScrollId scrollId = new ScrollId();
while (true) {
Page<R> page = scroll(query, clazz, new PageRequest(pageNum, numberOfRowsPerScan), scrollId);
if (page != null && page.getContent().size() > 0) {
result.addAll(page.getContent());
} else {
break;
}
if (result.size() >= size) {
break;
} else {
pageNum++;
}
}
elasticsearchTemplate.clearScroll(scrollId.getValue());
return result;
} @Override
public <R> Page<R> find(T query, Class<R> clazz, Pageable pageable) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = buildNativeSearchQueryBuilder(query).withPageable(pageable);
return elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), clazz);
} @Override
public <R> R sum(T query, Class<R> clazz) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = buildNativeSearchQueryBuilder(query);
for (SumBuilder sumBuilder : getSumBuilderList(query)) {
nativeSearchQueryBuilder.addAggregation(sumBuilder);
}
Aggregations aggregations = elasticsearchTemplate.query(nativeSearchQueryBuilder.build(), new AggregationResultsExtractor());
try {
return transformSumResult(aggregations, clazz);
} catch (Exception e) {
throw new SearchResultBuildException(e);
}
} private <R> R transformSumResult(Aggregations aggregations, Class<R> clazz) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
R targetObj = clazz.newInstance();
for (Aggregation sum : aggregations.asList()) {
if (sum instanceof InternalSum) {
setFieldValue(targetObj.getClass().getDeclaredField(sum.getName()), targetObj, ((InternalSum) sum).getValue());
}
}
return targetObj;
} private NativeSearchQueryBuilder buildNativeSearchQueryBuilder(T query) {
Document document = getDocument(query);
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
.withIndices(document.index())
.withTypes(document.type()); QueryBuilder whereBuilder = buildBoolQuery(query);
if (whereBuilder != null) {
nativeSearchQueryBuilder.withQuery(whereBuilder);
} return nativeSearchQueryBuilder;
} /**
* 布尔查询构建
*
* @param query
* @return
*/
private BoolQueryBuilder buildBoolQuery(T query) {
BoolQueryBuilder boolQueryBuilder = boolQuery();
buildMatchQuery(boolQueryBuilder, query);
buildRangeQuery(boolQueryBuilder, query);
BoolQueryBuilder queryBuilder = boolQuery().must(boolQueryBuilder);
return queryBuilder;
} /**
* and or 查询构建
*
* @param boolQueryBuilder
* @param query
*/
private void buildMatchQuery(BoolQueryBuilder boolQueryBuilder, T query) {
Class clazz = query.getClass();
for (Field field : clazz.getDeclaredFields()) {
MatchQuery annotation = field.getAnnotation(MatchQuery.class);
Object value = getFieldValue(field, query);
if (annotation == null || value == null) {
continue;
}
if (Container.must.equals(annotation.container())) {
boolQueryBuilder.must(matchQuery(getFieldName(field, annotation.column()), formatValue(value)));
} else if (should.equals(annotation.container())) {
if (value instanceof Collection) {
BoolQueryBuilder shouldQueryBuilder = boolQuery();
Collection tmp = (Collection) value;
for (Object obj : tmp) {
shouldQueryBuilder.should(matchQuery(getFieldName(field, annotation.column()), formatValue(obj)));
}
boolQueryBuilder.must(shouldQueryBuilder);
} else {
boolQueryBuilder.must(boolQuery().should(matchQuery(getFieldName(field, annotation.column()), formatValue(value))));
}
}
}
} /**
* 范围查询构建
*
* @param boolQueryBuilder
* @param query
*/
private void buildRangeQuery(BoolQueryBuilder boolQueryBuilder, T query) {
Class clazz = query.getClass();
for (Field field : clazz.getDeclaredFields()) {
RangeQuery annotation = field.getAnnotation(RangeQuery.class);
Object value = getFieldValue(field, query);
if (annotation == null || value == null) {
continue;
}
if (Operator.gt.equals(annotation.operator())) {
boolQueryBuilder.must(rangeQuery(getFieldName(field, annotation.column())).gt(formatValue(value)));
} else if (Operator.gte.equals(annotation.operator())) {
boolQueryBuilder.must(rangeQuery(getFieldName(field, annotation.column())).gte(formatValue(value)));
} else if (Operator.lt.equals(annotation.operator())) {
boolQueryBuilder.must(rangeQuery(getFieldName(field, annotation.column())).lt(formatValue(value)));
} else if (Operator.lte.equals(annotation.operator())) {
boolQueryBuilder.must(rangeQuery(getFieldName(field, annotation.column())).lte(formatValue(value)));
}
}
} /**
* Sum构建
*
* @param query
* @return
*/
private List<SumBuilder> getSumBuilderList(T query) {
List<SumBuilder> list = new ArrayList<>();
Class clazz = query.getClass();
for (Field field : clazz.getDeclaredFields()) {
Sum annotation = field.getAnnotation(Sum.class);
if (annotation == null) {
continue;
}
list.add(AggregationBuilders.sum(field.getName()).field(field.getName()));
}
if (CollectionUtils.isEmpty(list)) {
throw new SearchQueryBuildException("Can't find @Sum on " + clazz.getName());
}
return list;
} /**
* GroupBy构建
*
* @param query
* @return
*/
private TermsBuilder buildGroupBy(T query) {
List<Field> sumList = new ArrayList<>();
Object groupByCollection = null;
Class clazz = query.getClass();
for (Field field : clazz.getDeclaredFields()) {
Sum sumAnnotation = field.getAnnotation(Sum.class);
if (sumAnnotation != null) {
sumList.add(field);
}
GroupBy groupByannotation = field.getAnnotation(GroupBy.class);
Object value = getFieldValue(field, query);
if (groupByannotation == null || value == null) {
continue;
} else if (!(value instanceof Collection)) {
throw new SearchQueryBuildException("GroupBy filed must be collection");
} else if (CollectionUtils.isEmpty((Collection<String>) value)) {
continue;
} else if (groupByCollection != null) {
throw new SearchQueryBuildException("Only one @GroupBy is allowed");
} else {
groupByCollection = value;
}
}
Iterator<String> iterator = ((Collection<String>) groupByCollection).iterator();
TermsBuilder termsBuilder = recursiveAddAggregation(iterator, sumList);
return termsBuilder;
} /**
* 添加Aggregation
*
* @param iterator
* @return
*/
private TermsBuilder recursiveAddAggregation(Iterator<String> iterator, List<Field> sumList) {
String groupBy = iterator.next();
TermsBuilder termsBuilder = AggregationBuilders.terms(groupBy).field(groupBy).size(0);
if (iterator.hasNext()) {
termsBuilder.subAggregation(recursiveAddAggregation(iterator, sumList));
} else {
for (Field field : sumList) {
termsBuilder.subAggregation(AggregationBuilders.sum(field.getName()).field(field.getName()));
}
sumList.clear();
}
return termsBuilder.order(Terms.Order.term(true));
}

3.存储scrollId值对象

import lombok.Data;

@Data
public class ScrollId { private String value; }

4.用于判断查询操作的枚举类

public enum Operator {
gt, gte, lt, lte
}
public enum Container {
must, should
}

ElasticSearch 工具类封装(基于ElasticsearchTemplate)的更多相关文章

  1. flink---实时项目--day02-----1. 解析参数工具类 2. Flink工具类封装 3. 日志采集架构图 4. 测流输出 5. 将kafka中数据写入HDFS 6 KafkaProducer的使用 7 练习

    1. 解析参数工具类(ParameterTool) 该类提供了从不同数据源读取和解析程序参数的简单实用方法,其解析args时,只能支持单只参数. 用来解析main方法传入参数的工具类 public c ...

  2. Redis操作Set工具类封装,Java Redis Set命令封装

    Redis操作Set工具类封装,Java Redis Set命令封装 >>>>>>>>>>>>>>>>& ...

  3. Redis操作List工具类封装,Java Redis List命令封装

    Redis操作List工具类封装,Java Redis List命令封装 >>>>>>>>>>>>>>>> ...

  4. Redis操作Hash工具类封装,Redis工具类封装

    Redis操作Hash工具类封装,Redis工具类封装 >>>>>>>>>>>>>>>>>> ...

  5. Redis操作字符串工具类封装,Redis工具类封装

    Redis操作字符串工具类封装,Redis工具类封装 >>>>>>>>>>>>>>>>>>& ...

  6. (转载) 百度地图工具类封装(包括定位,附近、城市、范围poi检索,反地理编码)

    目录视图 摘要视图 订阅 赠书 | 异步2周年,技术图书免费选      程序员8月书讯      项目管理+代码托管+文档协作,开发更流畅 百度地图工具类封装(包括定位,附近.城市.范围poi检索, ...

  7. 小D课堂 - 零基础入门SpringBoot2.X到实战_第9节 SpringBoot2.x整合Redis实战_40、Redis工具类封装讲解和实战

    笔记 4.Redis工具类封装讲解和实战     简介:高效开发方式 Redis工具类封装讲解和实战         1.常用客户端 https://redisdesktop.com/download ...

  8. 关于TornadoFx和Android的全局配置工具类封装实现及思路解析

    原文地址: 关于TornadoFx和Android的全局配置工具类封装实现及思路解析 - Stars-One的杂货小窝 目前个人开发软件存在设置页面,可以让用户自定义些设置,但我发现,存储数据的代码逻 ...

  9. Json转换工具类(基于google的Gson和阿里的fastjson)

    在项目之中我们经常会涉及到字符串和各种对象的转换,为此特地整理了一下常用的转换方法 一.基于com.google.code.gson封装的json转换工具类 1. 在pom.xml文件里面引入gson ...

随机推荐

  1. JS event loop

    一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Java ...

  2. Java语法 [HelloWorld]

    程序代码: public class lqx {// AAAAANBBBBCKJKSLJIOQL/*请手打哦!*/ public static void main (String[] args) { ...

  3. C语言冷知识

    C语言属强类型语言(1)编程语言分2种:强类型语言和弱类型语言.强类型语言中所有的变量都有自己固定的类型,这个类型有固定的内存占用,有固定的解析方法:弱类型语言中没有类型的概念,所有变量全都是一个类型 ...

  4. 手工搭建web项目

    https://www.cnblogs.com/skyblue-li/p/5966311.html

  5. Linux 上pcntl安装步骤

    一. 下载对应的PHP源码包 wget http://cn2.php.net/get/php-5.5.20.tar.gz/from/this/mirror 二. 解压下载的源码文件 tar -zxvf ...

  6. [leetcode]716. Max Stack 最大栈

    Design a max stack that supports push, pop, top, peekMax and popMax. push(x) -- Push element x onto ...

  7. Android Studio 减小项目文件夹的大小和.gitignore文件配置

    Build --> Clean Project 可以清理出很大一部分的空间 手动删除以下文件或者目录 Dir : ProjectFolder/buildDir : ProjectFolder/a ...

  8. JS的事件流的概念(重点)

      09-JS的事件流的概念(重点)   在学习jQuery的事件之前,大家必须要对JS的事件有所了解.看下文 事件的概念 HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件 ...

  9. centos7 微信安装

    安装过程如下: ,下载最新版本tar.gz压缩包 wget https://github.com/geeeeeeeeek/electronic-wechat/releases/download/V2. ...

  10. 如何禁用Chrome / Firefox /IE浏览器的Cookie

    Firefox: 点击菜单按钮并点击选项 按钮. 选择 隐私 面板. 将“Firefox 将会:”这一项设置为 使用自定义历史记录设置. 取消 接受来自站点的 Cookie 选项 即可禁用 Cooki ...