/**
   * Search, collecting hits with a {@link Collector}, and computing drill down and sideways counts.
   */
  @SuppressWarnings({"rawtypes", "unchecked"})
  public DrillSidewaysResult search(DrillDownQuery query, Collector hitCollector)
      throws IOException {

    Map<String, Integer> drillDownDims = query.getDims();

    FacetsCollector drillDownCollector = new FacetsCollector();

    if (drillDownDims.isEmpty()) {
      // There are no drill-down dims, so there is no
      // drill-sideways to compute:
      searcher.search(query, MultiCollector.wrap(hitCollector, drillDownCollector));
      return new DrillSidewaysResult(buildFacetsResult(drillDownCollector, null, null), null);
    }

    BooleanQuery ddq = query.getBooleanQuery();
    BooleanClause[] clauses = ddq.getClauses();

    Query baseQuery;
    int startClause;
    if (clauses.length == drillDownDims.size()) {
      // TODO: we could optimize this pure-browse case by
      // making a custom scorer instead:
      baseQuery = new MatchAllDocsQuery();
      startClause = 0;
    } else {
      assert clauses.length == 1 + drillDownDims.size();
      baseQuery = clauses[0].getQuery();
      startClause = 1;
    }

    FacetsCollector[] drillSidewaysCollectors = new FacetsCollector[drillDownDims.size()];
    for (int i = 0; i < drillSidewaysCollectors.length; i++) {
      drillSidewaysCollectors[i] = new FacetsCollector();
    }

    Query[] drillDownQueries = new Query[clauses.length - startClause];
    for (int i = startClause; i < clauses.length; i++) {
      drillDownQueries[i - startClause] = clauses[i].getQuery();
    }
    DrillSidewaysQuery dsq =
        new DrillSidewaysQuery(
            baseQuery,
            drillDownCollector,
            drillSidewaysCollectors,
            drillDownQueries,
            scoreSubDocsAtOnce());
    searcher.search(dsq, hitCollector);

    return new DrillSidewaysResult(
        buildFacetsResult(
            drillDownCollector,
            drillSidewaysCollectors,
            drillDownDims.keySet().toArray(new String[drillDownDims.size()])),
        null);
  }