@Override
  protected Query doToQuery(QueryShardContext context) throws IOException {
    DocumentMapper childDocMapper = context.getMapperService().documentMapper(type);
    if (childDocMapper == null) {
      if (ignoreUnmapped) {
        return new MatchNoDocsQuery();
      } else {
        throw new QueryShardException(
            context, "[" + NAME + "] no mapping found for type [" + type + "]");
      }
    }
    ParentFieldMapper parentFieldMapper = childDocMapper.parentFieldMapper();
    if (parentFieldMapper.active() == false) {
      throw new QueryShardException(
          context, "[" + NAME + "] _parent field has no parent type configured");
    }
    String fieldName = ParentFieldMapper.joinField(parentFieldMapper.type());

    BooleanQuery.Builder query = new BooleanQuery.Builder();
    query.add(new DocValuesTermsQuery(fieldName, id), BooleanClause.Occur.MUST);
    // Need to take child type into account, otherwise a child doc of different type with the same
    // id could match
    query.add(new TermQuery(new Term(TypeFieldMapper.NAME, type)), BooleanClause.Occur.FILTER);
    return query.build();
  }
  @Override
  protected Query doToQuery(QueryShardContext context) throws IOException {
    Query innerQuery;
    String[] previousTypes = context.getTypes();
    context.setTypes(type);
    try {
      innerQuery = query.toQuery(context);
    } finally {
      context.setTypes(previousTypes);
    }

    DocumentMapper parentDocMapper = context.getMapperService().documentMapper(type);
    if (parentDocMapper == null) {
      if (ignoreUnmapped) {
        return new MatchNoDocsQuery();
      } else {
        throw new QueryShardException(
            context,
            "[" + NAME + "] query configured 'parent_type' [" + type + "] is not a valid type");
      }
    }

    Set<String> childTypes = new HashSet<>();
    ParentChildIndexFieldData parentChildIndexFieldData = null;
    for (DocumentMapper documentMapper : context.getMapperService().docMappers(false)) {
      ParentFieldMapper parentFieldMapper = documentMapper.parentFieldMapper();
      if (parentFieldMapper.active() && type.equals(parentFieldMapper.type())) {
        childTypes.add(documentMapper.type());
        parentChildIndexFieldData = context.getForField(parentFieldMapper.fieldType());
      }
    }

    if (childTypes.isEmpty()) {
      throw new QueryShardException(
          context, "[" + NAME + "] no child types found for type [" + type + "]");
    }

    Query childrenQuery;
    if (childTypes.size() == 1) {
      DocumentMapper documentMapper =
          context.getMapperService().documentMapper(childTypes.iterator().next());
      childrenQuery = documentMapper.typeFilter();
    } else {
      BooleanQuery.Builder childrenFilter = new BooleanQuery.Builder();
      for (String childrenTypeStr : childTypes) {
        DocumentMapper documentMapper = context.getMapperService().documentMapper(childrenTypeStr);
        childrenFilter.add(documentMapper.typeFilter(), BooleanClause.Occur.SHOULD);
      }
      childrenQuery = childrenFilter.build();
    }

    // wrap the query with type query
    innerQuery = Queries.filtered(innerQuery, parentDocMapper.typeFilter());
    return new HasChildQueryBuilder.LateParsingQuery(
        childrenQuery,
        innerQuery,
        HasChildQueryBuilder.DEFAULT_MIN_CHILDREN,
        HasChildQueryBuilder.DEFAULT_MAX_CHILDREN,
        type,
        score ? ScoreMode.Max : ScoreMode.None,
        parentChildIndexFieldData,
        context.getSearchSimilarity());
  }