@Override
 public void writeTo(StreamOutput out) throws IOException {
   super.writeTo(out);
   out.writeByte(percolatorTypeId);
   out.writeVLong(requestedSize);
   out.writeVLong(count);
   out.writeVInt(matches.length);
   for (BytesRef match : matches) {
     out.writeBytesRef(match);
   }
   out.writeVLong(scores.length);
   for (float score : scores) {
     out.writeFloat(score);
   }
   out.writeVInt(hls.size());
   for (Map<String, HighlightField> hl : hls) {
     out.writeVInt(hl.size());
     for (Map.Entry<String, HighlightField> entry : hl.entrySet()) {
       out.writeString(entry.getKey());
       entry.getValue().writeTo(out);
     }
   }
   out.writeOptionalStreamable(aggregations);
   if (pipelineAggregators == null) {
     out.writeBoolean(false);
   } else {
     out.writeBoolean(true);
     out.writeVInt(pipelineAggregators.size());
     for (PipelineAggregator pipelineAggregator : pipelineAggregators) {
       out.writeBytesReference(pipelineAggregator.type().stream());
       pipelineAggregator.writeTo(out);
     }
   }
 }
  @Override
  public void execute(SearchContext context) {
    if (context.aggregations() == null) {
      context.queryResult().aggregations(null);
      return;
    }

    if (context.queryResult().aggregations() != null) {
      // no need to compute the aggs twice, they should be computed on a per context basis
      return;
    }

    Aggregator[] aggregators = context.aggregations().aggregators();
    List<Aggregator> globals = new ArrayList<>();
    for (int i = 0; i < aggregators.length; i++) {
      if (aggregators[i] instanceof GlobalAggregator) {
        globals.add(aggregators[i]);
      }
    }

    // optimize the global collector based execution
    if (!globals.isEmpty()) {
      BucketCollector globalsCollector = BucketCollector.wrap(globals);
      Query query = Queries.newMatchAllQuery();
      Query searchFilter = context.searchFilter(context.types());

      if (searchFilter != null) {
        BooleanQuery filtered =
            new BooleanQuery.Builder()
                .add(query, Occur.MUST)
                .add(searchFilter, Occur.FILTER)
                .build();
        query = filtered;
      }
      try {
        final Collector collector;
        if (context.getProfilers() == null) {
          collector = globalsCollector;
        } else {
          InternalProfileCollector profileCollector =
              new InternalProfileCollector(
                  globalsCollector,
                  CollectorResult.REASON_AGGREGATION_GLOBAL,
                  // TODO: report on sub collectors
                  Collections.emptyList());
          collector = profileCollector;
          // start a new profile with this collector
          context.getProfilers().addProfiler().setCollector(profileCollector);
        }
        globalsCollector.preCollection();
        context.searcher().search(query, collector);
      } catch (Exception e) {
        throw new QueryPhaseExecutionException(context, "Failed to execute global aggregators", e);
      } finally {
        context.clearReleasables(SearchContext.Lifetime.COLLECTION);
      }
    }

    List<InternalAggregation> aggregations = new ArrayList<>(aggregators.length);
    for (Aggregator aggregator : context.aggregations().aggregators()) {
      try {
        aggregator.postCollection();
        aggregations.add(aggregator.buildAggregation(0));
      } catch (IOException e) {
        throw new AggregationExecutionException(
            "Failed to build aggregation [" + aggregator.name() + "]", e);
      }
    }
    context.queryResult().aggregations(new InternalAggregations(aggregations));
    try {
      List<PipelineAggregator> pipelineAggregators =
          context.aggregations().factories().createPipelineAggregators();
      List<SiblingPipelineAggregator> siblingPipelineAggregators =
          new ArrayList<>(pipelineAggregators.size());
      for (PipelineAggregator pipelineAggregator : pipelineAggregators) {
        if (pipelineAggregator instanceof SiblingPipelineAggregator) {
          siblingPipelineAggregators.add((SiblingPipelineAggregator) pipelineAggregator);
        } else {
          throw new AggregationExecutionException(
              "Invalid pipeline aggregation named ["
                  + pipelineAggregator.name()
                  + "] of type ["
                  + pipelineAggregator.type().name()
                  + "]. Only sibling pipeline aggregations are allowed at the top level");
        }
      }
      context.queryResult().pipelineAggregators(siblingPipelineAggregators);
    } catch (IOException e) {
      throw new AggregationExecutionException("Failed to build top level pipeline aggregators", e);
    }

    // disable aggregations so that they don't run on next pages in case of scrolling
    context.aggregations(null);
    context.queryCollectors().remove(AggregationPhase.class);
  }