QueryGroupByParser.java 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. package org.elasticsearch.dsl.parser.syntax;
  2. import com.alibaba.druid.sql.ast.SQLExpr;
  3. import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
  4. import com.alibaba.druid.sql.ast.statement.SQLSelectGroupByClause;
  5. import com.google.common.collect.Lists;
  6. import org.apache.commons.collections.CollectionUtils;
  7. import org.elasticsearch.dsl.bean.ElasticDslContext;
  8. import org.elasticsearch.dsl.bean.ElasticSqlQueryField;
  9. import org.elasticsearch.dsl.bean.RangeSegment;
  10. import org.elasticsearch.dsl.enums.QueryFieldType;
  11. import org.elasticsearch.dsl.exception.ElasticSql2DslException;
  12. import org.elasticsearch.dsl.parser.QueryParser;
  13. import org.elasticsearch.dsl.parser.helper.ElasticSqlArgTransferHelper;
  14. import org.elasticsearch.dsl.parser.helper.ElasticSqlMethodInvokeHelper;
  15. import org.elasticsearch.dsl.parser.listener.ParseActionListener;
  16. import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
  17. import org.elasticsearch.search.aggregations.AggregationBuilder;
  18. import org.elasticsearch.search.aggregations.AggregationBuilders;
  19. import org.elasticsearch.search.aggregations.bucket.range.AbstractRangeBuilder;
  20. import org.elasticsearch.search.aggregations.bucket.range.RangeBuilder;
  21. import org.elasticsearch.search.aggregations.bucket.range.date.DateRangeBuilder;
  22. import org.elasticsearch.search.aggregations.bucket.terms.Terms;
  23. import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
  24. import org.elasticsearch.sql.ElasticSqlSelectQueryBlock;
  25. import org.joda.time.DateTime;
  26. import org.joda.time.format.DateTimeFormat;
  27. import org.joda.time.format.DateTimeFormatter;
  28. import java.util.Date;
  29. import java.util.List;
  30. public class QueryGroupByParser implements QueryParser {
  31. private static final Integer MAX_GROUP_BY_SIZE = 500;
  32. private static final String AGG_BUCKET_KEY_PREFIX = "agg_";
  33. private ParseActionListener parseActionListener;
  34. public QueryGroupByParser(ParseActionListener parseActionListener) {
  35. this.parseActionListener = parseActionListener;
  36. }
  37. @Override
  38. public void parse(ElasticDslContext dslContext) {
  39. ElasticSqlSelectQueryBlock queryBlock = (ElasticSqlSelectQueryBlock) dslContext.getQueryExpr().getSubQuery().getQuery();
  40. SQLSelectGroupByClause sqlGroupBy = queryBlock.getGroupBy();
  41. if (sqlGroupBy != null && CollectionUtils.isNotEmpty(sqlGroupBy.getItems())) {
  42. String queryAs = dslContext.getParseResult().getQueryAs();
  43. List<AbstractAggregationBuilder> aggregationList = Lists.newArrayList();
  44. for (SQLExpr groupByItem : sqlGroupBy.getItems()) {
  45. if (!(groupByItem instanceof SQLMethodInvokeExpr)) {
  46. throw new ElasticSql2DslException("[syntax error] group by item must be an agg method call");
  47. }
  48. SQLMethodInvokeExpr aggMethodExpr = (SQLMethodInvokeExpr) groupByItem;
  49. //Terms Aggregation
  50. if (ElasticSqlMethodInvokeHelper.AGG_TERMS_METHOD.equalsIgnoreCase(aggMethodExpr.getMethodName())) {
  51. ElasticSqlMethodInvokeHelper.checkTermsAggMethod(aggMethodExpr);
  52. SQLExpr termsFieldExpr = aggMethodExpr.getParameters().get(0);
  53. AggregationBuilder termsBuilder = parseTermsAggregation(queryAs, termsFieldExpr);
  54. aggregationList.add(termsBuilder);
  55. }
  56. //Range Aggregation
  57. if (ElasticSqlMethodInvokeHelper.AGG_RANGE_METHOD.equalsIgnoreCase(aggMethodExpr.getMethodName())) {
  58. ElasticSqlMethodInvokeHelper.checkRangeAggMethod(aggMethodExpr);
  59. List<RangeSegment> rangeSegments = parseRangeSegments(aggMethodExpr, dslContext.getSqlArgs());
  60. SQLExpr rangeFieldExpr = aggMethodExpr.getParameters().get(0);
  61. AggregationBuilder rangeBuilder = parseRangeAggregation(queryAs, rangeFieldExpr, rangeSegments);
  62. aggregationList.add(rangeBuilder);
  63. }
  64. }
  65. dslContext.getParseResult().setGroupBy(aggregationList);
  66. }
  67. }
  68. private AggregationBuilder parseTermsAggregation(String queryAs, SQLExpr termsFieldExpr) {
  69. QueryFieldParser queryFieldParser = new QueryFieldParser();
  70. ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(termsFieldExpr, queryAs);
  71. if(queryField.getQueryFieldType() != QueryFieldType.RootDocField && queryField.getQueryFieldType() != QueryFieldType.InnerDocField) {
  72. throw new ElasticSql2DslException(String.format("[syntax error] can not support terms aggregation for field type[%s]", queryField.getQueryFieldType()));
  73. }
  74. return createTermsBuilder(queryField.getQueryFieldFullName());
  75. }
  76. private AggregationBuilder parseRangeAggregation(String queryAs, SQLExpr rangeFieldExpr, List<RangeSegment> rangeSegments) {
  77. QueryFieldParser queryFieldParser = new QueryFieldParser();
  78. ElasticSqlQueryField queryField = queryFieldParser.parseConditionQueryField(rangeFieldExpr, queryAs);
  79. if(queryField.getQueryFieldType() != QueryFieldType.RootDocField && queryField.getQueryFieldType() != QueryFieldType.InnerDocField) {
  80. throw new ElasticSql2DslException(String.format("[syntax error] can not support range aggregation for field type[%s]", queryField.getQueryFieldType()));
  81. }
  82. return createRangeBuilder(queryField.getQueryFieldFullName(), rangeSegments);
  83. }
  84. private List<RangeSegment> parseRangeSegments(SQLMethodInvokeExpr rangeMethodExpr, Object[] args) {
  85. List<RangeSegment> rangeSegmentList = Lists.newArrayList();
  86. for (int pIdx = 1; pIdx < rangeMethodExpr.getParameters().size(); pIdx++) {
  87. SQLMethodInvokeExpr segMethodExpr = (SQLMethodInvokeExpr) rangeMethodExpr.getParameters().get(pIdx);
  88. ElasticSqlMethodInvokeHelper.checkRangeItemAggMethod(segMethodExpr);
  89. Object from = ElasticSqlArgTransferHelper.transferSqlArg(segMethodExpr.getParameters().get(0), args, true);
  90. Object to = ElasticSqlArgTransferHelper.transferSqlArg(segMethodExpr.getParameters().get(1), args, true);
  91. rangeSegmentList.add(new RangeSegment(from, to,
  92. from instanceof Number ? RangeSegment.SegmentType.Numeric : RangeSegment.SegmentType.Date));
  93. }
  94. return rangeSegmentList;
  95. }
  96. private TermsBuilder createTermsBuilder(String termsFieldName) {
  97. return AggregationBuilders.terms(AGG_BUCKET_KEY_PREFIX + termsFieldName)
  98. .field(termsFieldName)
  99. .minDocCount(1).shardMinDocCount(1)
  100. .shardSize(MAX_GROUP_BY_SIZE << 1).size(MAX_GROUP_BY_SIZE).order(Terms.Order.count(false));
  101. }
  102. private AbstractRangeBuilder createRangeBuilder(String rangeFieldName, List<RangeSegment> rangeSegments) {
  103. AbstractRangeBuilder rangeBuilder = null;
  104. RangeSegment.SegmentType segType = rangeSegments.get(0).getSegmentType();
  105. if (segType == RangeSegment.SegmentType.Numeric) {
  106. RangeBuilder numericRangeBuilder = AggregationBuilders.range(AGG_BUCKET_KEY_PREFIX + rangeFieldName).field(rangeFieldName);
  107. for (RangeSegment segment : rangeSegments) {
  108. String key = String.format("%s-%s", segment.getFrom().toString(), segment.getTo().toString());
  109. numericRangeBuilder.addRange(key, Double.valueOf(segment.getFrom().toString()), Double.valueOf(segment.getTo().toString()));
  110. }
  111. rangeBuilder = numericRangeBuilder;
  112. }
  113. if (segType == RangeSegment.SegmentType.Date) {
  114. DateRangeBuilder dateRangeBuilder = AggregationBuilders.dateRange(AGG_BUCKET_KEY_PREFIX + rangeFieldName).field(rangeFieldName);
  115. for (RangeSegment segment : rangeSegments) {
  116. Date fromDate = getDateRangeVal(segment.getFrom().toString());
  117. Date toDate = getDateRangeVal(segment.getTo().toString());
  118. String key = String.format("[%s]-[%s]", formatDateRangeAggKey(fromDate), formatDateRangeAggKey(toDate));
  119. dateRangeBuilder.addRange(key, segment.getFrom(), segment.getTo());
  120. }
  121. rangeBuilder = dateRangeBuilder;
  122. }
  123. return rangeBuilder;
  124. }
  125. private String formatDateRangeAggKey(Date date) {
  126. final String dateRangeKeyPattern = "yyyy-MM-dd HH:mm:ss";
  127. return new DateTime(date).toString(dateRangeKeyPattern);
  128. }
  129. public static Date getDateRangeVal(String date) {
  130. final String dateRangeValPattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";
  131. DateTimeFormatter formatter = DateTimeFormat.forPattern(dateRangeValPattern);
  132. return formatter.parseDateTime(date).toDate();
  133. }
  134. }