private void executeFetchPhase() {
      sortedShardList = searchPhaseController.sortDocs(queryResults.values());
      Map<SearchShardTarget, ExtTIntArrayList> docIdsToLoad =
          searchPhaseController.docIdsToLoad(sortedShardList);

      if (docIdsToLoad.isEmpty()) {
        finishHim();
      }

      final AtomicInteger counter = new AtomicInteger(docIdsToLoad.size());

      for (final Map.Entry<SearchShardTarget, ExtTIntArrayList> entry : docIdsToLoad.entrySet()) {
        SearchShardTarget shardTarget = entry.getKey();
        ExtTIntArrayList docIds = entry.getValue();
        FetchSearchRequest fetchSearchRequest =
            new FetchSearchRequest(request, queryResults.get(shardTarget).id(), docIds);
        DiscoveryNode node = nodes.get(shardTarget.nodeId());
        searchService.sendExecuteFetch(
            node,
            fetchSearchRequest,
            new SearchServiceListener<FetchSearchResult>() {
              @Override
              public void onResult(FetchSearchResult result) {
                result.shardTarget(entry.getKey());
                fetchResults.put(result.shardTarget(), result);
                if (counter.decrementAndGet() == 0) {
                  finishHim();
                }
              }

              @Override
              public void onFailure(Throwable t) {
                if (logger.isDebugEnabled()) {
                  logger.debug("Failed to execute fetch phase", t);
                }
                successfulOps.decrementAndGet();
                if (counter.decrementAndGet() == 0) {
                  finishHim();
                }
              }
            });
      }
    }
  public void writeTo(StreamOutput out, InternalSearchHits.StreamContext context)
      throws IOException {
    out.writeFloat(score);
    out.writeText(id);
    out.writeText(type);
    out.writeOptionalStreamable(nestedIdentity);
    out.writeLong(version);
    out.writeBytesReference(source);
    if (explanation == null) {
      out.writeBoolean(false);
    } else {
      out.writeBoolean(true);
      writeExplanation(out, explanation);
    }
    if (fields == null) {
      out.writeVInt(0);
    } else {
      out.writeVInt(fields.size());
      for (SearchHitField hitField : fields().values()) {
        hitField.writeTo(out);
      }
    }
    if (highlightFields == null) {
      out.writeVInt(0);
    } else {
      out.writeVInt(highlightFields.size());
      for (HighlightField highlightField : highlightFields.values()) {
        highlightField.writeTo(out);
      }
    }

    if (sortValues.length == 0) {
      out.writeVInt(0);
    } else {
      out.writeVInt(sortValues.length);
      for (Object sortValue : sortValues) {
        if (sortValue == null) {
          out.writeByte((byte) 0);
        } else {
          Class type = sortValue.getClass();
          if (type == String.class) {
            out.writeByte((byte) 1);
            out.writeString((String) sortValue);
          } else if (type == Integer.class) {
            out.writeByte((byte) 2);
            out.writeInt((Integer) sortValue);
          } else if (type == Long.class) {
            out.writeByte((byte) 3);
            out.writeLong((Long) sortValue);
          } else if (type == Float.class) {
            out.writeByte((byte) 4);
            out.writeFloat((Float) sortValue);
          } else if (type == Double.class) {
            out.writeByte((byte) 5);
            out.writeDouble((Double) sortValue);
          } else if (type == Byte.class) {
            out.writeByte((byte) 6);
            out.writeByte((Byte) sortValue);
          } else if (type == Short.class) {
            out.writeByte((byte) 7);
            out.writeShort((Short) sortValue);
          } else if (type == Boolean.class) {
            out.writeByte((byte) 8);
            out.writeBoolean((Boolean) sortValue);
          } else if (sortValue instanceof Text) {
            out.writeByte((byte) 9);
            out.writeText((Text) sortValue);
          } else {
            throw new IOException("Can't handle sort field value of type [" + type + "]");
          }
        }
      }
    }

    if (matchedQueries.length == 0) {
      out.writeVInt(0);
    } else {
      out.writeVInt(matchedQueries.length);
      for (String matchedFilter : matchedQueries) {
        out.writeString(matchedFilter);
      }
    }

    if (context.streamShardTarget() == InternalSearchHits.StreamContext.ShardTargetType.STREAM) {
      if (shard == null) {
        out.writeBoolean(false);
      } else {
        out.writeBoolean(true);
        shard.writeTo(out);
      }
    } else if (context.streamShardTarget()
        == InternalSearchHits.StreamContext.ShardTargetType.LOOKUP) {
      if (shard == null) {
        out.writeVInt(0);
      } else {
        out.writeVInt(context.shardHandleLookup().get(shard));
      }
    }

    if (innerHits == null) {
      out.writeVInt(0);
    } else {
      out.writeVInt(innerHits.size());
      for (Map.Entry<String, InternalSearchHits> entry : innerHits.entrySet()) {
        out.writeString(entry.getKey());
        entry
            .getValue()
            .writeTo(
                out,
                InternalSearchHits.streamContext()
                    .streamShardTarget(InternalSearchHits.StreamContext.ShardTargetType.NO_STREAM));
      }
    }
  }
  @Override
  public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
    List<SearchHitField> metaFields = Lists.newArrayList();
    List<SearchHitField> otherFields = Lists.newArrayList();
    if (fields != null && !fields.isEmpty()) {
      for (SearchHitField field : fields.values()) {
        if (field.values().isEmpty()) {
          continue;
        }
        if (field.isMetadataField()) {
          metaFields.add(field);
        } else {
          otherFields.add(field);
        }
      }
    }

    builder.startObject();
    // For inner_hit hits shard is null and that is ok, because the parent search hit has all this
    // information.
    // Even if this was included in the inner_hit hits this would be the same, so better leave it
    // out.
    if (explanation() != null && shard != null) {
      builder.field("_shard", shard.shardId());
      builder.field("_node", shard.nodeIdText());
    }
    if (shard != null) {
      builder.field(Fields._INDEX, shard.indexText());
    }
    builder.field(Fields._TYPE, type);
    builder.field(Fields._ID, id);
    if (nestedIdentity != null) {
      nestedIdentity.toXContent(builder, params);
    }
    if (version != -1) {
      builder.field(Fields._VERSION, version);
    }
    if (Float.isNaN(score)) {
      builder.nullField(Fields._SCORE);
    } else {
      builder.field(Fields._SCORE, score);
    }
    for (SearchHitField field : metaFields) {
      builder.field(field.name(), field.value());
    }
    if (source != null) {
      XContentHelper.writeRawField("_source", source, builder, params);
    }
    if (!otherFields.isEmpty()) {
      builder.startObject(Fields.FIELDS);
      for (SearchHitField field : otherFields) {
        builder.startArray(field.name());
        for (Object value : field.getValues()) {
          builder.value(value);
        }
        builder.endArray();
      }
      builder.endObject();
    }
    if (highlightFields != null && !highlightFields.isEmpty()) {
      builder.startObject(Fields.HIGHLIGHT);
      for (HighlightField field : highlightFields.values()) {
        builder.field(field.name());
        if (field.fragments() == null) {
          builder.nullValue();
        } else {
          builder.startArray();
          for (Text fragment : field.fragments()) {
            builder.value(fragment);
          }
          builder.endArray();
        }
      }
      builder.endObject();
    }
    if (sortValues != null && sortValues.length > 0) {
      builder.startArray(Fields.SORT);
      for (Object sortValue : sortValues) {
        if (sortValue != null && sortValue.equals(MAX_TERM_AS_TEXT)) {
          // We don't display MAX_TERM in JSON responses in case some clients have UTF-8 parsers
          // that wouldn't accept a
          // non-character in the response, even though this is valid UTF-8
          builder.nullValue();
        } else {
          builder.value(sortValue);
        }
      }
      builder.endArray();
    }
    if (matchedQueries.length > 0) {
      builder.startArray(Fields.MATCHED_QUERIES);
      for (String matchedFilter : matchedQueries) {
        builder.value(matchedFilter);
      }
      builder.endArray();
    }
    if (explanation() != null) {
      builder.field(Fields._EXPLANATION);
      buildExplanation(builder, explanation());
    }
    if (innerHits != null) {
      builder.startObject(Fields.INNER_HITS);
      for (Map.Entry<String, InternalSearchHits> entry : innerHits.entrySet()) {
        builder.startObject(entry.getKey());
        entry.getValue().toXContent(builder, params);
        builder.endObject();
      }
      builder.endObject();
    }
    builder.endObject();
    return builder;
  }
 @Override
 public String index() {
   return shard.index();
 }
 @Override
 public void writeTo(StreamOutput out) throws IOException {
   super.writeTo(out);
   shardTarget.writeTo(out);
   result.writeTo(out);
 }