private AggregationBuilder<?> termsAgg(MethodField field) throws SqlParseException {
   String aggName = gettAggNameFromParamsOrAlias(field);
   TermsBuilder terms = AggregationBuilders.terms(aggName);
   String value = null;
   for (KVValue kv : field.getParams()) {
     value = kv.value.toString();
     switch (kv.key.toLowerCase()) {
       case "field":
         terms.field(value);
         break;
       case "size":
         terms.size(Integer.parseInt(value));
         break;
       case "shard_size":
         terms.shardSize(Integer.parseInt(value));
         break;
       case "min_doc_count":
         terms.minDocCount(Integer.parseInt(value));
         break;
       case "alias":
       case "nested":
       case "reverse_nested":
         break;
       default:
         throw new SqlParseException("terms aggregation err or not define field " + kv.toString());
     }
   }
   return terms;
 }
 @Override
 public Map<String, Integer> countTermsByField(SearchCriteria searchCriteria, String field) {
   ElasticSearchCriteria criteria = (ElasticSearchCriteria) searchCriteria;
   if (criteria == null) return null;
   SearchRequestBuilder srb = criteria2builder(criteria);
   srb.setFrom(0);
   srb.setSize(0);
   TermsBuilder tb = AggregationBuilders.terms(field);
   tb.field(field);
   srb.addAggregation(tb);
   try {
     SearchResponse response = srb.execute().get();
     StringTerms aggr = response.getAggregations().get(field);
     Map<String, Integer> result = new LinkedHashMap<>();
     for (Terms.Bucket bucket : aggr.getBuckets()) {
       result.put(bucket.getKey(), (int) bucket.getDocCount());
     }
     return result;
   } catch (Exception e) {
     logger.error(e.getMessage(), e);
   }
   return Collections.emptyMap();
 }
  @Override
  public SqlElasticSearchRequestBuilder explain() throws SqlParseException {
    this.request = client.prepareSearch();
    request.setListenerThreaded(false);
    setIndicesAndTypes();

    setWhere(select.getWhere());
    AggregationBuilder<?> lastAgg = null;

    for (List<Field> groupBy : select.getGroupBys()) {
      if (!groupBy.isEmpty()) {
        Field field = groupBy.get(0);
        lastAgg = aggMaker.makeGroupAgg(field);

        if (lastAgg != null && lastAgg instanceof TermsBuilder && !(field instanceof MethodField)) {
          ((TermsBuilder) lastAgg).size(select.getRowCount());
        }

        if (field.isNested()) {

          AggregationBuilder nestedBuilder = createNestedAggregation(field);
          if (insertFilterIfExistsAfter(lastAgg, groupBy, nestedBuilder, 1)) {
            groupBy.remove(1);
          } else {
            nestedBuilder.subAggregation(lastAgg);
          }
          request.addAggregation(wrapNestedIfNeeded(nestedBuilder, field.isReverseNested()));
        } else {
          request.addAggregation(lastAgg);
        }

        for (int i = 1; i < groupBy.size(); i++) {
          field = groupBy.get(i);
          AggregationBuilder<?> subAgg = aggMaker.makeGroupAgg(field);
          if (subAgg instanceof TermsBuilder && !(field instanceof MethodField)) {
            ((TermsBuilder) subAgg).size(0);
          }

          if (field.isNested()) {
            AggregationBuilder nestedBuilder = createNestedAggregation(field);
            if (insertFilterIfExistsAfter(subAgg, groupBy, nestedBuilder, i + 1)) {
              groupBy.remove(i + 1);
              i++;
            } else {
              nestedBuilder.subAggregation(subAgg);
            }
            lastAgg.subAggregation(wrapNestedIfNeeded(nestedBuilder, field.isReverseNested()));

          } else {
            lastAgg.subAggregation(subAgg);
          }

          lastAgg = subAgg;
        }
      }
    }

    Map<String, KVValue> groupMap = aggMaker.getGroupMap();
    // add field
    if (select.getFields().size() > 0) {
      setFields(select.getFields());
      explanFields(request, select.getFields(), lastAgg);
    }

    // add order
    if (lastAgg != null && select.getOrderBys().size() > 0) {
      for (Order order : select.getOrderBys()) {
        KVValue temp = groupMap.get(order.getName());
        if (temp != null) {
          TermsBuilder termsBuilder = (TermsBuilder) temp.value;
          switch (temp.key) {
            case "COUNT":
              termsBuilder.order(Terms.Order.count(isASC(order)));
              break;
            case "KEY":
              termsBuilder.order(Terms.Order.term(isASC(order)));
              // add the sort to the request also so the results get sorted as well
              request.addSort(order.getName(), SortOrder.valueOf(order.getType()));
              break;
            case "FIELD":
              termsBuilder.order(Terms.Order.aggregation(order.getName(), isASC(order)));
              break;
            default:
              throw new SqlParseException(order.getName() + " can not to order");
          }
        } else {
          request.addSort(order.getName(), SortOrder.valueOf(order.getType()));
        }
      }
    }

    setLimitFromHint(this.select.getHints());

    request.setSearchType(SearchType.DEFAULT);
    updateRequestWithIndexAndRoutingOptions(select, request);
    SqlElasticSearchRequestBuilder sqlElasticRequestBuilder =
        new SqlElasticSearchRequestBuilder(request);
    return sqlElasticRequestBuilder;
  }