// inherit javadoc
  public TopDocs search(Query query, Filter filter, final int nDocs) throws IOException {
    Scorer scorer = query.weight(this).scorer(reader);
    if (scorer == null) return new TopDocs(0, new ScoreDoc[0]);

    final BitSet bits = filter != null ? filter.bits(reader) : null;
    final HitQueue hq = new HitQueue(nDocs);
    final int[] totalHits = new int[1];
    scorer.score(
        new HitCollector() {
          private float minScore = 0.0f;

          public final void collect(int doc, float score) {
            if (score > 0.0f
                && // ignore zeroed buckets
                (bits == null || bits.get(doc))) { // skip docs not in bits
              totalHits[0]++;
              if (hq.size() < nDocs || score >= minScore) {
                hq.insert(new ScoreDoc(doc, score));
                minScore = ((ScoreDoc) hq.top()).score; // maintain minScore
              }
            }
          }
        });

    ScoreDoc[] scoreDocs = new ScoreDoc[hq.size()];
    for (int i = hq.size() - 1; i >= 0; i--) // put docs in array
    scoreDocs[i] = (ScoreDoc) hq.pop();

    return new TopDocs(totalHits[0], scoreDocs);
  }
  // inherit javadoc
  public TopFieldDocs search(Query query, Filter filter, final int nDocs, Sort sort)
      throws IOException {
    Scorer scorer = query.weight(this).scorer(reader);
    if (scorer == null) return new TopFieldDocs(0, new ScoreDoc[0], sort.fields);

    final BitSet bits = filter != null ? filter.bits(reader) : null;
    final FieldSortedHitQueue hq = new FieldSortedHitQueue(reader, sort.fields, nDocs);
    final int[] totalHits = new int[1];
    scorer.score(
        new HitCollector() {
          public final void collect(int doc, float score) {
            if (score > 0.0f
                && // ignore zeroed buckets
                (bits == null || bits.get(doc))) { // skip docs not in bits
              totalHits[0]++;
              hq.insert(new FieldDoc(doc, score));
            }
          }
        });

    ScoreDoc[] scoreDocs = new ScoreDoc[hq.size()];
    for (int i = hq.size() - 1; i >= 0; i--) // put docs in array
    scoreDocs[i] = hq.fillFields((FieldDoc) hq.pop());

    return new TopFieldDocs(totalHits[0], scoreDocs, hq.getFields());
  }
  // inherit javadoc
  public void search(Query query, Filter filter, final HitCollector results) throws IOException {
    HitCollector collector = results;
    if (filter != null) {
      final BitSet bits = filter.bits(reader);
      collector =
          new HitCollector() {
            public final void collect(int doc, float score) {
              if (bits.get(doc)) { // skip docs not in bits
                results.collect(doc, score);
              }
            }
          };
    }

    Scorer scorer = query.weight(this).scorer(reader);
    if (scorer == null) return;
    scorer.score(collector);
  }
  public BitSet bits(IndexReader reader) throws IOException {
    if (cache == null) {
      cache = new WeakHashMap();
    }

    synchronized (cache) { // check cache
      BitSet cached = (BitSet) cache.get(reader);
      if (cached != null) {
        return cached;
      }
    }

    final BitSet bits = filter.bits(reader);

    synchronized (cache) { // update cache
      cache.put(reader, bits);
    }

    return bits;
  }