private AggregationBuilder createNestedAggregation(Field field) {
   AggregationBuilder nestedBuilder;
   String nestedPath = field.getNestedPath();
   if (field.isReverseNested()) {
     if (nestedPath == null || !nestedPath.startsWith("~"))
       return AggregationBuilders.reverseNested(getNestedAggName(field)).path(nestedPath);
     nestedPath = nestedPath.substring(1);
   }
   nestedBuilder = AggregationBuilders.nested(getNestedAggName(field)).path(nestedPath);
   return nestedBuilder;
 }
  @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;
  }