@Override
  protected void doAssertLuceneQuery(
      HasParentQueryBuilder queryBuilder, Query query, SearchContext searchContext)
      throws IOException {
    assertThat(query, instanceOf(HasChildQueryBuilder.LateParsingQuery.class));
    HasChildQueryBuilder.LateParsingQuery lpq = (HasChildQueryBuilder.LateParsingQuery) query;
    assertEquals(queryBuilder.score() ? ScoreMode.Max : ScoreMode.None, lpq.getScoreMode());

    if (queryBuilder.innerHit() != null) {
      // have to rewrite again because the provided queryBuilder hasn't been rewritten (directly
      // returned from
      // doCreateTestQueryBuilder)
      queryBuilder =
          (HasParentQueryBuilder) queryBuilder.rewrite(searchContext.getQueryShardContext());

      assertNotNull(searchContext);
      Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
      InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders);
      for (InnerHitBuilder builder : innerHitBuilders.values()) {
        builder.build(searchContext, searchContext.innerHits());
      }
      assertNotNull(searchContext.innerHits());
      assertEquals(1, searchContext.innerHits().getInnerHits().size());
      assertTrue(
          searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
      InnerHitsContext.BaseInnerHits innerHits =
          searchContext.innerHits().getInnerHits().get(queryBuilder.innerHit().getName());
      assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
      assertEquals(innerHits.sort().sort.getSort().length, 1);
      assertEquals(innerHits.sort().sort.getSort()[0].getField(), STRING_FIELD_NAME_2);
    }
  }
  @Override
  protected void doAssertLuceneQuery(
      NestedQueryBuilder queryBuilder, Query query, SearchContext searchContext)
      throws IOException {
    QueryBuilder innerQueryBuilder = queryBuilder.query();
    assertThat(query, instanceOf(ToParentBlockJoinQuery.class));
    ToParentBlockJoinQuery parentBlockJoinQuery = (ToParentBlockJoinQuery) query;
    // TODO how to assert this?
    if (queryBuilder.innerHit() != null) {
      // have to rewrite again because the provided queryBuilder hasn't been rewritten (directly
      // returned from
      // doCreateTestQueryBuilder)
      queryBuilder =
          (NestedQueryBuilder) queryBuilder.rewrite(searchContext.getQueryShardContext());

      assertNotNull(searchContext);
      Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
      InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders);
      for (InnerHitBuilder builder : innerHitBuilders.values()) {
        builder.build(searchContext, searchContext.innerHits());
      }
      assertNotNull(searchContext.innerHits());
      assertEquals(1, searchContext.innerHits().getInnerHits().size());
      assertTrue(
          searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
      InnerHitsContext.BaseInnerHits innerHits =
          searchContext.innerHits().getInnerHits().get(queryBuilder.innerHit().getName());
      assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
      assertEquals(innerHits.sort().sort.getSort().length, 1);
      assertEquals(innerHits.sort().sort.getSort()[0].getField(), INT_FIELD_NAME);
    }
  }
 @Override
 protected QueryBuilder doRewrite(QueryRewriteContext queryShardContext) throws IOException {
   QueryBuilder rewrittenQuery = query.rewrite(queryShardContext);
   if (rewrittenQuery != query) {
     InnerHitBuilder rewrittenInnerHit = InnerHitBuilder.rewrite(innerHit, rewrittenQuery);
     return new HasParentQueryBuilder(type, rewrittenQuery, score, rewrittenInnerHit);
   }
   return this;
 }
 @Override
 protected void extractInnerHitBuilders(Map<String, InnerHitBuilder> innerHits) {
   if (innerHit != null) {
     innerHit.inlineInnerHits(innerHits);
   }
 }
  public static Optional<HasParentQueryBuilder> fromXContent(QueryParseContext parseContext)
      throws IOException {
    XContentParser parser = parseContext.parser();
    float boost = AbstractQueryBuilder.DEFAULT_BOOST;
    String parentType = null;
    boolean score = false;
    String queryName = null;
    InnerHitBuilder innerHits = null;
    boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;

    String currentFieldName = null;
    XContentParser.Token token;
    Optional<QueryBuilder> iqb = Optional.empty();
    while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
      if (token == XContentParser.Token.FIELD_NAME) {
        currentFieldName = parser.currentName();
      } else if (token == XContentParser.Token.START_OBJECT) {
        if (parseContext.getParseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
          iqb = parseContext.parseInnerQueryBuilder();
        } else if (parseContext.getParseFieldMatcher().match(currentFieldName, INNER_HITS_FIELD)) {
          innerHits = InnerHitBuilder.fromXContent(parseContext);
        } else {
          throw new ParsingException(
              parser.getTokenLocation(),
              "[has_parent] query does not support [" + currentFieldName + "]");
        }
      } else if (token.isValue()) {
        if (parseContext.getParseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
          parentType = parser.text();
        } else if (parseContext.getParseFieldMatcher().match(currentFieldName, SCORE_MODE_FIELD)) {
          String scoreModeValue = parser.text();
          if ("score".equals(scoreModeValue)) {
            score = true;
          } else if ("none".equals(scoreModeValue)) {
            score = false;
          } else {
            throw new ParsingException(
                parser.getTokenLocation(),
                "[has_parent] query does not support ["
                    + scoreModeValue
                    + "] as an option for score_mode");
          }
        } else if (parseContext.getParseFieldMatcher().match(currentFieldName, SCORE_FIELD)) {
          score = parser.booleanValue();
        } else if (parseContext
            .getParseFieldMatcher()
            .match(currentFieldName, IGNORE_UNMAPPED_FIELD)) {
          ignoreUnmapped = parser.booleanValue();
        } else if (parseContext
            .getParseFieldMatcher()
            .match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
          boost = parser.floatValue();
        } else if (parseContext
            .getParseFieldMatcher()
            .match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
          queryName = parser.text();
        } else {
          throw new ParsingException(
              parser.getTokenLocation(),
              "[has_parent] query does not support [" + currentFieldName + "]");
        }
      }
    }
    if (iqb.isPresent() == false) {
      // if inner query is empty, bubble this up to caller so they can decide how to deal with it
      return Optional.empty();
    }
    HasParentQueryBuilder queryBuilder =
        new HasParentQueryBuilder(parentType, iqb.get(), score)
            .ignoreUnmapped(ignoreUnmapped)
            .queryName(queryName)
            .boost(boost);
    if (innerHits != null) {
      queryBuilder.innerHit(innerHits);
    }
    return Optional.of(queryBuilder);
  }