private FlatSearchResponse searchFlatPaged(
      FlatSearchRequest request,
      Collection<IndexingContext> indexingContexts,
      boolean ignoreContext)
      throws IOException {
    TreeSet<ArtifactInfo> result = new TreeSet<ArtifactInfo>(request.getArtifactInfoComparator());

    int totalHits = 0;

    for (IndexingContext ctx : indexingContexts) {
      if (ignoreContext || ctx.isSearchable()) {
        int hitCount =
            searchFlat(
                request, result, ctx, request.getQuery(), request.getStart(), request.getCount());

        if (hitCount == AbstractSearchResponse.LIMIT_EXCEEDED) {
          totalHits = hitCount;
        } else {
          totalHits += hitCount;
        }
      }

      if (request.isHitLimited() && (totalHits > request.getResultHitLimit())
          || totalHits == AbstractSearchResponse.LIMIT_EXCEEDED) {
        totalHits = AbstractSearchResponse.LIMIT_EXCEEDED;
        result = new TreeSet<ArtifactInfo>(request.getArtifactInfoComparator());
        break;
      }
    }

    return new FlatSearchResponse(request.getQuery(), totalHits, result);
  }
  private GroupedSearchResponse searchGrouped(
      GroupedSearchRequest request,
      Collection<IndexingContext> indexingContexts,
      boolean ignoreContext)
      throws IOException {
    TreeMap<String, ArtifactInfoGroup> result =
        new TreeMap<String, ArtifactInfoGroup>(request.getGroupKeyComparator());

    int totalHits = 0;

    for (IndexingContext ctx : indexingContexts) {
      if (ignoreContext || ctx.isSearchable()) {
        int hitCount =
            searchGrouped(request, result, request.getGrouping(), ctx, request.getQuery());

        if (hitCount == AbstractSearchResponse.LIMIT_EXCEEDED) {
          totalHits = hitCount;
        } else {
          totalHits += hitCount;
        }
      }

      if (request.isHitLimited() && (totalHits > request.getResultHitLimit())
          || totalHits == AbstractSearchResponse.LIMIT_EXCEEDED) {
        totalHits = AbstractSearchResponse.LIMIT_EXCEEDED;
        result = new TreeMap<String, ArtifactInfoGroup>(request.getGroupKeyComparator());
        break;
      }
    }

    return new GroupedSearchResponse(request.getQuery(), totalHits, result);
  }
  private IteratorSearchResponse searchIteratorPaged(
      IteratorSearchRequest request,
      Collection<IndexingContext> indexingContexts,
      boolean ignoreContext)
      throws IOException {
    // manage defaults!
    if (request.getStart() < 0) {
      request.setStart(IteratorSearchRequest.UNDEFINED);
    }
    if (request.getCount() < 0) {
      request.setCount(IteratorSearchRequest.UNDEFINED);
    }

    // to not change the API all away, but we need stable ordering here
    // filter for those 1st, that take part in here
    ArrayList<IndexingContext> contexts = new ArrayList<IndexingContext>(indexingContexts.size());

    for (IndexingContext ctx : indexingContexts) {
      if (ignoreContext || ctx.isSearchable()) {
        contexts.add(ctx);
      }
    }

    ArrayList<IndexReader> contextsToSearch = new ArrayList<IndexReader>(contexts.size());

    for (IndexingContext ctx : contexts) {
      contextsToSearch.add(ctx.getIndexReader());
    }

    MultiReader multiReader =
        new MultiReader(contextsToSearch.toArray(new IndexReader[contextsToSearch.size()]));

    IndexSearcher indexSearcher = new IndexSearcher(multiReader);

    // NEXUS-3482 made us to NOT use reverse ordering (it is a fix I wanted to implement, but user
    // contributed patch
    // did come in faster! -- Thanks)
    Hits hits =
        indexSearcher.search(
            request.getQuery(),
            new Sort(
                new SortField[] {
                  SortField.FIELD_SCORE, new SortField(null, SortField.DOC, false)
                }));

    return new IteratorSearchResponse(
        request.getQuery(),
        hits.length(),
        new DefaultIteratorResultSet(request, indexSearcher, contexts, hits));
  }