From 68e4acdfb02a0d41536df963c8e8c1bfb96a17ac Mon Sep 17 00:00:00 2001 From: zhouhao Date: Thu, 21 Apr 2022 09:22:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96es=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/utils/ConverterUtils.java | 178 ++++++++++++++++++ .../jetlinks/community/utils/TimeUtils.java | 57 +++++- .../search/parser/DefaultTermTypeParser.java | 13 +- .../search/utils/QueryParamTranslator.java | 17 +- 4 files changed, 257 insertions(+), 8 deletions(-) create mode 100644 jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/ConverterUtils.java mode change 100644 => 100755 jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/TimeUtils.java diff --git a/jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/ConverterUtils.java b/jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/ConverterUtils.java new file mode 100644 index 00000000..ba4ac3b3 --- /dev/null +++ b/jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/ConverterUtils.java @@ -0,0 +1,178 @@ +package org.jetlinks.community.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.ComparatorUtils; +import org.apache.commons.collections4.MapUtils; +import org.hswebframework.ezorm.core.param.Sort; +import org.hswebframework.ezorm.core.param.Term; +import org.hswebframework.web.api.crud.entity.TermExpressionParser; +import org.hswebframework.web.bean.FastBeanCopier; +import org.jetlinks.reactor.ql.utils.CompareUtils; +import reactor.core.publisher.Flux; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class ConverterUtils { + + /** + * 尝试转换值为集合,如果不是集合格式则直接返回该值 + * + * @param value 值 + * @param converter 转换器,用户转换单个结果 + * @return 转换结果 + */ + public static Object tryConvertToList(Object value, Function converter) { + List list = convertToList(value, converter); + if (list.size() == 1) { + return list.get(0); + } + return list; + } + + /** + * 转换参数为指定类型的List + * + * @param value 参数 + * @param converter 类型转换器 + * @param List中元素类型 + * @return 转换后的List + */ + public static List convertToList(Object value, Function converter) { + if (value == null) { + return Collections.emptyList(); + } + if (value instanceof String) { + value = ((String) value).split(","); + } + + if (value instanceof Object[]) { + value = Arrays.asList(((Object[]) value)); + } + if (value instanceof Collection) { + return ((Collection) value) + .stream() + .map(converter) + .collect(Collectors.toList()); + } + return Collections.singletonList(converter.apply(value)); + } + + /** + * 转换参数为List + * + * @param value 参数 + * @return 排序后的流 + */ + public static List convertToList(Object value) { + return convertToList(value, Function.identity()); + } + + /** + * 根据排序参数对指定对Flux流进行排序 + * + * @param flux Flux + * @param sorts 排序参数 + * @param 流中元素类型 + * @return 排序后的流 + */ + public static Flux convertSortedStream(Flux flux, Sort... sorts) { + return convertSortedStream(flux, Arrays.asList(sorts)); + } + + /** + * 根据排序参数对指定对Flux流进行排序 + * + * @param flux Flux + * @param sorts 排序参数 + * @param 流中元素类型 + * @return 排序后的流 + */ + public static Flux convertSortedStream(Flux flux, Collection sorts) { + if (CollectionUtils.isEmpty(sorts)) { + return flux; + } + List> comparators = new ArrayList<>(sorts.size()); + for (Sort sort : sorts) { + String column = sort.getName(); + Comparator comparator = (left, right) -> { + Object leftVal = FastBeanCopier.copy(left, new HashMap<>()).get(column); + Object rightVal = FastBeanCopier.copy(right, new HashMap<>()).get(column); + return CompareUtils.compare(leftVal, rightVal); + }; + if (sort.getOrder().equalsIgnoreCase("desc")) { + comparator = comparator.reversed(); + } + comparators.add(comparator); + + } + return flux.sort(ComparatorUtils.chainedComparator(comparators)); + } + + /** + * 将Map转为tag,如果map中到值不是数字,则转为json. + *
+     *      {"key1":"value1","key2":["value2"]} => key,value1,key2,["value2"]
+     *  
+ * + * @param map map + * @return tags + */ + public static String[] convertMapToTags(Map map) { + if (MapUtils.isEmpty(map)) { + return new String[0]; + } + String[] tags = new String[map.size() * 2]; + int index = 0; + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + if (value == null) { + continue; + } + String strValue = value instanceof String + ? String.valueOf(value) + : JSON.toJSONString(value); + + tags[index++] = key; + tags[index++] = strValue; + } + if (tags.length > index) { + return Arrays.copyOf(tags, index); + } + return tags; + } + + /** + * 将对象转为查询条件,支持json和表达式格式,如: + *
+     *   //name = xxx and age > 10
+     *   convertTerms("name is xxx and age gt 10")
+     *
+     * 
+ * + * @param value + * @return 条件集合 + */ + @SuppressWarnings("all") + public static List convertTerms(Object value) { + if (value instanceof String) { + String strVal = String.valueOf(value); + //json字符串 + if (strVal.startsWith("[")) { + value = JSON.parseArray(strVal); + } else { + //表达式 + return TermExpressionParser.parse(strVal); + } + } + if (value instanceof List) { + return new JSONArray(((List) value)).toJavaList(Term.class); + } else { + throw new UnsupportedOperationException("unsupported term value:" + value); + } + } +} diff --git a/jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/TimeUtils.java b/jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/TimeUtils.java old mode 100644 new mode 100755 index 1dc44e28..1dd657c2 --- a/jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/TimeUtils.java +++ b/jetlinks-components/common-component/src/main/java/org/jetlinks/community/utils/TimeUtils.java @@ -1,15 +1,13 @@ package org.jetlinks.community.utils; +import org.jetlinks.reactor.ql.utils.CastUtils; +import reactor.core.publisher.Flux; + import java.math.BigDecimal; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.Date; -/** - * 时间工具类 - * - * @author zhouhao - */ public class TimeUtils { @@ -54,17 +52,25 @@ public class TimeUtils { duration = duration.plus(plus); } } + if (numIndex != 0) { + duration = duration.plus(Duration.ofMillis(new BigDecimal(tmp, 0, numIndex).longValue())); + } return duration; } + public static ChronoUnit parseUnit(String expr) { expr = expr.toUpperCase(); + if (expr.equals("MILLENNIA")) { + return ChronoUnit.MILLENNIA; + } else if (expr.equals("FOREVER")) { + return ChronoUnit.FOREVER; + } if (!expr.endsWith("S")) { expr = expr + "S"; } - return ChronoUnit.valueOf(expr); } @@ -85,4 +91,43 @@ public class TimeUtils { return new Date(DateMathParser.parse(expr, System::currentTimeMillis)); } + public static Date convertToDate(Object obj) { + if(obj instanceof String){ + return new Date(DateMathParser.parse(String.valueOf(obj), System::currentTimeMillis)); + } + return CastUtils.castDate(obj); + } + + + public static long round(long ts, long interval) { + return (ts / interval) * interval; + } + + /** + * 解析指定时间区间为每一个间隔时间 + * + * @param from 时间从 + * @param to 时间到 + * @param interval 间隔 + * @return 时间 + */ + public static Flux parseIntervalRange(long from, long to, long interval) { + return Flux + .create(sink -> { + long _from = from, _to = to; + if (_from > _to) { + _from = to; + _to = from; + } + _from = round(_from, interval); + _to = round(_to, interval); + sink.next(_from); + while (_from < _to && !sink.isCancelled()) { + _from = _from + interval; + sink.next(_from); + } + sink.complete(); + }); + + } } diff --git a/jetlinks-components/elasticsearch-component/src/main/java/org/jetlinks/community/elastic/search/parser/DefaultTermTypeParser.java b/jetlinks-components/elasticsearch-component/src/main/java/org/jetlinks/community/elastic/search/parser/DefaultTermTypeParser.java index 4d4a456c..84800ef5 100644 --- a/jetlinks-components/elasticsearch-component/src/main/java/org/jetlinks/community/elastic/search/parser/DefaultTermTypeParser.java +++ b/jetlinks-components/elasticsearch-component/src/main/java/org/jetlinks/community/elastic/search/parser/DefaultTermTypeParser.java @@ -1,6 +1,8 @@ package org.jetlinks.community.elastic.search.parser; +import org.apache.lucene.search.join.ScoreMode; import org.elasticsearch.index.query.BoolQueryBuilder; +import org.elasticsearch.index.query.NestedQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.hswebframework.ezorm.core.param.Term; @@ -23,6 +25,15 @@ public class DefaultTermTypeParser implements TermTypeParser { private QueryBuilder queryBuilder(Term term) { - return TermTypeEnum.of(term.getTermType().trim()).map(e -> e.process(term)).orElse(QueryBuilders.boolQuery()); + return TermTypeEnum.of(term.getTermType().trim()) + .map(e -> createQueryBuilder(e,term)) + .orElse(QueryBuilders.boolQuery()); + } + + static QueryBuilder createQueryBuilder(TermTypeEnum linkTypeEnum, Term term) { + if (term.getColumn().contains(".")) { + return new NestedQueryBuilder(term.getColumn().split("[.]")[0], linkTypeEnum.process(term), ScoreMode.Max); + } + return linkTypeEnum.process(term); } } diff --git a/jetlinks-components/elasticsearch-component/src/main/java/org/jetlinks/community/elastic/search/utils/QueryParamTranslator.java b/jetlinks-components/elasticsearch-component/src/main/java/org/jetlinks/community/elastic/search/utils/QueryParamTranslator.java index aeaf257e..c1317459 100644 --- a/jetlinks-components/elasticsearch-component/src/main/java/org/jetlinks/community/elastic/search/utils/QueryParamTranslator.java +++ b/jetlinks-components/elasticsearch-component/src/main/java/org/jetlinks/community/elastic/search/utils/QueryParamTranslator.java @@ -11,8 +11,13 @@ import org.hswebframework.ezorm.core.param.Sort; import org.hswebframework.ezorm.core.param.Term; import org.jetlinks.community.elastic.search.index.ElasticSearchIndexMetadata; import org.jetlinks.community.elastic.search.parser.DefaultLinkTypeParser; +import org.jetlinks.community.utils.ConverterUtils; +import org.jetlinks.community.utils.TimeUtils; +import org.jetlinks.core.metadata.Converter; import org.jetlinks.core.metadata.DataType; import org.jetlinks.core.metadata.PropertyMetadata; +import org.jetlinks.core.metadata.types.DateTimeType; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import java.util.Map; @@ -43,12 +48,21 @@ public class QueryParamTranslator { Consumer paramConverter = doNotingParamConverter; if (metadata != null) { paramConverter = t -> { - if (StringUtils.isEmpty(t.getColumn())) { + if (ObjectUtils.isEmpty(t.getColumn())) { return; } PropertyMetadata property = metadata.getProperty(t.getColumn()); if (null != property) { DataType type = property.getValueType(); + t.setValue( + ConverterUtils.tryConvertToList(t.getValue(), val -> { + if (type instanceof DateTimeType) { + return TimeUtils.convertToDate(val).getTime(); + } else if (type instanceof Converter) { + return ((Converter) type).convert(val); + } + return val; + })); converter.getOrDefault(type.getId(), defaultDataTypeConverter).accept(type, t); } }; @@ -58,6 +72,7 @@ public class QueryParamTranslator { } return queryBuilders; } + public static SearchSourceBuilder convertSearchSourceBuilder(QueryParam queryParam, ElasticSearchIndexMetadata metadata) { SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); if (queryParam.isPaging()) {