private InternalSearchHit createSearchHit(
      SearchContext context,
      FieldsVisitor fieldsVisitor,
      int docId,
      int subDocId,
      List<String> extractFieldNames,
      LeafReaderContext subReaderContext) {
    loadStoredFields(context, subReaderContext, fieldsVisitor, subDocId);
    fieldsVisitor.postProcess(context.mapperService());

    Map<String, SearchHitField> searchFields = null;
    if (!fieldsVisitor.fields().isEmpty()) {
      searchFields = new HashMap<>(fieldsVisitor.fields().size());
      for (Map.Entry<String, List<Object>> entry : fieldsVisitor.fields().entrySet()) {
        searchFields.put(
            entry.getKey(), new InternalSearchHitField(entry.getKey(), entry.getValue()));
      }
    }

    DocumentMapper documentMapper =
        context.mapperService().documentMapper(fieldsVisitor.uid().type());
    Text typeText;
    if (documentMapper == null) {
      typeText = new StringAndBytesText(fieldsVisitor.uid().type());
    } else {
      typeText = documentMapper.typeText();
    }
    InternalSearchHit searchHit =
        new InternalSearchHit(docId, fieldsVisitor.uid().id(), typeText, searchFields);

    // go over and extract fields that are not mapped / stored
    SourceLookup sourceLookup = context.lookup().source();
    sourceLookup.setSegmentAndDocument(subReaderContext, subDocId);
    if (fieldsVisitor.source() != null) {
      sourceLookup.setSource(fieldsVisitor.source());
    }
    if (extractFieldNames != null) {
      for (String extractFieldName : extractFieldNames) {
        List<Object> values = context.lookup().source().extractRawValues(extractFieldName);
        if (!values.isEmpty()) {
          if (searchHit.fieldsOrNull() == null) {
            searchHit.fields(new HashMap<String, SearchHitField>(2));
          }

          SearchHitField hitField = searchHit.fields().get(extractFieldName);
          if (hitField == null) {
            hitField = new InternalSearchHitField(extractFieldName, new ArrayList<>(2));
            searchHit.fields().put(extractFieldName, hitField);
          }
          for (Object value : values) {
            hitField.values().add(value);
          }
        }
      }
    }

    return searchHit;
  }
  private InternalSearchHit createNestedSearchHit(
      SearchContext context,
      int nestedTopDocId,
      int nestedSubDocId,
      int rootSubDocId,
      List<String> extractFieldNames,
      boolean loadAllStored,
      Set<String> fieldNames,
      LeafReaderContext subReaderContext)
      throws IOException {
    // Also if highlighting is requested on nested documents we need to fetch the _source from the
    // root document,
    // otherwise highlighting will attempt to fetch the _source from the nested doc, which will
    // fail,
    // because the entire _source is only stored with the root document.
    final FieldsVisitor rootFieldsVisitor =
        new FieldsVisitor(
            context.sourceRequested() || extractFieldNames != null || context.highlight() != null);
    loadStoredFields(context, subReaderContext, rootFieldsVisitor, rootSubDocId);
    rootFieldsVisitor.postProcess(context.mapperService());

    Map<String, SearchHitField> searchFields =
        getSearchFields(context, nestedSubDocId, loadAllStored, fieldNames, subReaderContext);
    DocumentMapper documentMapper =
        context.mapperService().documentMapper(rootFieldsVisitor.uid().type());
    SourceLookup sourceLookup = context.lookup().source();
    sourceLookup.setSegmentAndDocument(subReaderContext, nestedSubDocId);

    ObjectMapper nestedObjectMapper =
        documentMapper.findNestedObjectMapper(nestedSubDocId, context, subReaderContext);
    assert nestedObjectMapper != null;
    InternalSearchHit.InternalNestedIdentity nestedIdentity =
        getInternalNestedIdentity(
            context, nestedSubDocId, subReaderContext, documentMapper, nestedObjectMapper);

    BytesReference source = rootFieldsVisitor.source();
    if (source != null) {
      Tuple<XContentType, Map<String, Object>> tuple = XContentHelper.convertToMap(source, true);
      Map<String, Object> sourceAsMap = tuple.v2();

      List<Map<String, Object>> nestedParsedSource;
      SearchHit.NestedIdentity nested = nestedIdentity;
      do {
        Object extractedValue =
            XContentMapValues.extractValue(nested.getField().string(), sourceAsMap);
        if (extractedValue == null) {
          // The nested objects may not exist in the _source, because it was filtered because of
          // _source filtering
          break;
        } else if (extractedValue instanceof List) {
          // nested field has an array value in the _source
          nestedParsedSource = (List<Map<String, Object>>) extractedValue;
        } else if (extractedValue instanceof Map) {
          // nested field has an object value in the _source. This just means the nested field has
          // just one inner object, which is valid, but uncommon.
          nestedParsedSource = ImmutableList.of((Map<String, Object>) extractedValue);
        } else {
          throw new IllegalStateException("extracted source isn't an object or an array");
        }
        sourceAsMap = nestedParsedSource.get(nested.getOffset());
        nested = nested.getChild();
      } while (nested != null);

      context.lookup().source().setSource(sourceAsMap);
      XContentType contentType = tuple.v1();
      BytesReference nestedSource = contentBuilder(contentType).map(sourceAsMap).bytes();
      context.lookup().source().setSource(nestedSource);
      context.lookup().source().setSourceContentType(contentType);
    }

    InternalSearchHit searchHit =
        new InternalSearchHit(
            nestedTopDocId,
            rootFieldsVisitor.uid().id(),
            documentMapper.typeText(),
            nestedIdentity,
            searchFields);
    if (extractFieldNames != null) {
      for (String extractFieldName : extractFieldNames) {
        List<Object> values = context.lookup().source().extractRawValues(extractFieldName);
        if (!values.isEmpty()) {
          if (searchHit.fieldsOrNull() == null) {
            searchHit.fields(new HashMap<String, SearchHitField>(2));
          }

          SearchHitField hitField = searchHit.fields().get(extractFieldName);
          if (hitField == null) {
            hitField = new InternalSearchHitField(extractFieldName, new ArrayList<>(2));
            searchHit.fields().put(extractFieldName, hitField);
          }
          for (Object value : values) {
            hitField.values().add(value);
          }
        }
      }
    }

    return searchHit;
  }
 public static InternalSearchHit readSearchHit(
     StreamInput in, InternalSearchHits.StreamContext context) throws IOException {
   InternalSearchHit hit = new InternalSearchHit();
   hit.readFrom(in, context);
   return hit;
 }
  public InternalSearchResponse merge(
      ScoreDoc[] sortedDocs,
      AtomicArray<? extends QuerySearchResultProvider> queryResultsArr,
      AtomicArray<? extends FetchSearchResultProvider> fetchResultsArr) {

    List<? extends AtomicArray.Entry<? extends QuerySearchResultProvider>> queryResults =
        queryResultsArr.asList();
    List<? extends AtomicArray.Entry<? extends FetchSearchResultProvider>> fetchResults =
        fetchResultsArr.asList();

    if (queryResults.isEmpty()) {
      return InternalSearchResponse.empty();
    }

    QuerySearchResult firstResult = queryResults.get(0).value.queryResult();

    boolean sorted = false;
    int sortScoreIndex = -1;
    if (firstResult.topDocs() instanceof TopFieldDocs) {
      sorted = true;
      TopFieldDocs fieldDocs = (TopFieldDocs) firstResult.queryResult().topDocs();
      for (int i = 0; i < fieldDocs.fields.length; i++) {
        if (fieldDocs.fields[i].getType() == SortField.Type.SCORE) {
          sortScoreIndex = i;
        }
      }
    }

    // merge facets
    InternalFacets facets = null;
    if (!queryResults.isEmpty()) {
      // we rely on the fact that the order of facets is the same on all query results
      if (firstResult.facets() != null
          && firstResult.facets().facets() != null
          && !firstResult.facets().facets().isEmpty()) {
        List<Facet> aggregatedFacets = Lists.newArrayList();
        List<Facet> namedFacets = Lists.newArrayList();
        for (Facet facet : firstResult.facets()) {
          // aggregate each facet name into a single list, and aggregate it
          namedFacets.clear();
          for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults) {
            for (Facet facet1 : entry.value.queryResult().facets()) {
              if (facet.getName().equals(facet1.getName())) {
                namedFacets.add(facet1);
              }
            }
          }
          if (!namedFacets.isEmpty()) {
            Facet aggregatedFacet =
                ((InternalFacet) namedFacets.get(0))
                    .reduce(new InternalFacet.ReduceContext(cacheRecycler, namedFacets));
            aggregatedFacets.add(aggregatedFacet);
          }
        }
        facets = new InternalFacets(aggregatedFacets);
      }
    }

    // count the total (we use the query result provider here, since we might not get any hits (we
    // scrolled past them))
    long totalHits = 0;
    float maxScore = Float.NEGATIVE_INFINITY;
    boolean timedOut = false;
    Boolean terminatedEarly = null;
    for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults) {
      QuerySearchResult result = entry.value.queryResult();
      if (result.searchTimedOut()) {
        timedOut = true;
      }
      if (result.terminatedEarly() != null) {
        if (terminatedEarly == null) {
          terminatedEarly = result.terminatedEarly();
        } else if (result.terminatedEarly()) {
          terminatedEarly = true;
        }
      }
      totalHits += result.topDocs().totalHits;
      if (!Float.isNaN(result.topDocs().getMaxScore())) {
        maxScore = Math.max(maxScore, result.topDocs().getMaxScore());
      }
    }
    if (Float.isInfinite(maxScore)) {
      maxScore = Float.NaN;
    }

    // clean the fetch counter
    for (AtomicArray.Entry<? extends FetchSearchResultProvider> entry : fetchResults) {
      entry.value.fetchResult().initCounter();
    }

    // merge hits
    List<InternalSearchHit> hits = new ArrayList<>();
    if (!fetchResults.isEmpty()) {
      for (ScoreDoc shardDoc : sortedDocs) {
        FetchSearchResultProvider fetchResultProvider = fetchResultsArr.get(shardDoc.shardIndex);
        if (fetchResultProvider == null) {
          continue;
        }
        FetchSearchResult fetchResult = fetchResultProvider.fetchResult();
        int index = fetchResult.counterGetAndIncrement();
        if (index < fetchResult.hits().internalHits().length) {
          InternalSearchHit searchHit = fetchResult.hits().internalHits()[index];
          searchHit.score(shardDoc.score);
          searchHit.shard(fetchResult.shardTarget());

          if (sorted) {
            FieldDoc fieldDoc = (FieldDoc) shardDoc;
            searchHit.sortValues(fieldDoc.fields);
            if (sortScoreIndex != -1) {
              searchHit.score(((Number) fieldDoc.fields[sortScoreIndex]).floatValue());
            }
          }

          hits.add(searchHit);
        }
      }
    }

    // merge suggest results
    Suggest suggest = null;
    if (!queryResults.isEmpty()) {
      final Map<String, List<Suggest.Suggestion>> groupedSuggestions = new HashMap<>();
      boolean hasSuggestions = false;
      for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults) {
        Suggest shardResult = entry.value.queryResult().queryResult().suggest();

        if (shardResult == null) {
          continue;
        }
        hasSuggestions = true;
        Suggest.group(groupedSuggestions, shardResult);
      }

      suggest =
          hasSuggestions
              ? new Suggest(Suggest.Fields.SUGGEST, Suggest.reduce(groupedSuggestions))
              : null;
    }

    // merge addAggregation
    InternalAggregations aggregations = null;
    if (!queryResults.isEmpty()) {
      if (firstResult.aggregations() != null && firstResult.aggregations().asList() != null) {
        List<InternalAggregations> aggregationsList = new ArrayList<>(queryResults.size());
        for (AtomicArray.Entry<? extends QuerySearchResultProvider> entry : queryResults) {
          aggregationsList.add((InternalAggregations) entry.value.queryResult().aggregations());
        }
        aggregations =
            InternalAggregations.reduce(
                aggregationsList, new ReduceContext(null, bigArrays, scriptService));
      }
    }

    InternalSearchHits searchHits =
        new InternalSearchHits(
            hits.toArray(new InternalSearchHit[hits.size()]), totalHits, maxScore);

    return new InternalSearchResponse(
        searchHits, facets, aggregations, suggest, timedOut, terminatedEarly);
  }
 private boolean isChildHit(InternalSearchHit hit) {
   DocumentMapper hitDocumentMapper = mapperService.documentMapper(hit.type());
   return documentMapper.type().equals(hitDocumentMapper.parentFieldMapper().type());
 }
 private boolean isParentHit(InternalSearchHit hit) {
   return hit.type().equals(documentMapper.parentFieldMapper().type());
 }