Index: src/java/org/apache/lucene/search/BitSetFilter.java
===================================================================
--- src/java/org/apache/lucene/search/BitSetFilter.java	(revision 0)
+++ src/java/org/apache/lucene/search/BitSetFilter.java	(revision 0)
@@ -0,0 +1,42 @@
+package org.apache.lucene.search;
+
+import java.io.IOException;
+import java.util.BitSet;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.util.BitSetMatcher;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+public abstract class BitSetFilter extends Filter {
+  /**
+   * @return A BitSet with true for documents which should be permitted in
+   * search results, and false for those that should not.
+   */
+  public abstract BitSet bits(IndexReader reader) throws IOException;
+
+  /**
+   * @return a Matcher that provides the documents which should be
+   * permitted or prohibited in search results.
+   * @see BitSetMatcher
+   */
+  public Matcher getMatcher(IndexReader reader) throws IOException {
+    return new BitSetMatcher(bits(reader));
+  }
+}
Index: src/java/org/apache/lucene/search/CachingBitSetFilter.java
===================================================================
--- src/java/org/apache/lucene/search/CachingBitSetFilter.java	(revision 0)
+++ src/java/org/apache/lucene/search/CachingBitSetFilter.java	(revision 0)
@@ -0,0 +1,79 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.index.IndexReader;
+import java.util.BitSet;
+import java.util.WeakHashMap;
+import java.util.Map;
+import java.io.IOException;
+
+/**
+ * Wraps another BitSetFilter's result and caches it.  The purpose is to allow
+ * filters to simply filter, and then wrap with this class to add caching.
+ */
+public class CachingBitSetFilter extends BitSetFilter {
+  protected BitSetFilter filter;
+
+  /**
+   * A transient Filter cache.  To cache Filters even when using {@link RemoteSearchable} use
+   * {@link RemoteCachingBitSetFilter} instead.
+   */
+  protected transient Map cache;
+
+  /**
+   * @param filter Filter to cache results of
+   */
+  public CachingBitSetFilter(BitSetFilter filter) {
+    this.filter = filter;
+  }
+
+  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;
+  }
+
+  public String toString() {
+    return "CachingBitSetFilter("+filter+")";
+  }
+
+  public boolean equals(Object o) {
+    if (!(o instanceof CachingBitSetFilter)) return false;
+    return this.filter.equals(((CachingBitSetFilter)o).filter);
+  }
+
+  public int hashCode() {
+    return filter.hashCode() ^ 0x1117BF25;  
+  }
+}
Index: src/java/org/apache/lucene/search/CachingWrapperFilter.java
===================================================================
--- src/java/org/apache/lucene/search/CachingWrapperFilter.java	(revision 600387)
+++ src/java/org/apache/lucene/search/CachingWrapperFilter.java	(working copy)
@@ -26,6 +26,8 @@
 /**
  * Wraps another filter's result and caches it.  The purpose is to allow
  * filters to simply filter, and then wrap with this class to add caching.
+ * 
+ * @deprecated Use {@link CachingBitSetFilter} instead
  */
 public class CachingWrapperFilter extends Filter {
   protected Filter filter;
Index: src/java/org/apache/lucene/search/ConstantScoreQuery.java
===================================================================
--- src/java/org/apache/lucene/search/ConstantScoreQuery.java	(revision 600387)
+++ src/java/org/apache/lucene/search/ConstantScoreQuery.java	(working copy)
@@ -85,7 +85,7 @@
     public Explanation explain(IndexReader reader, int doc) throws IOException {
 
       ConstantScorer cs = (ConstantScorer)scorer(reader);
-      boolean exists = cs.bits.get(doc);
+      boolean exists = cs.matcher.skipTo(doc) && (cs.matcher.doc() == doc);
 
       ComplexExplanation result = new ComplexExplanation();
 
@@ -107,23 +107,21 @@
   }
 
   protected class ConstantScorer extends Scorer {
-    final BitSet bits;
+  	final Matcher matcher;
     final float theScore;
-    int doc=-1;
 
     public ConstantScorer(Similarity similarity, IndexReader reader, Weight w) throws IOException {
       super(similarity);
       theScore = w.getValue();
-      bits = filter.bits(reader);
+      matcher = filter.getMatcher(reader);
     }
 
     public boolean next() throws IOException {
-      doc = bits.nextSetBit(doc+1);
-      return doc >= 0;
+      return matcher.next();
     }
 
     public int doc() {
-      return doc;
+      return matcher.doc();
     }
 
     public float score() throws IOException {
@@ -131,8 +129,7 @@
     }
 
     public boolean skipTo(int target) throws IOException {
-      doc = bits.nextSetBit(target);  // requires JDK 1.4
-      return doc >= 0;
+    	return matcher.skipTo(target);
     }
 
     public Explanation explain(int doc) throws IOException {
Index: src/java/org/apache/lucene/search/Filter.java
===================================================================
--- src/java/org/apache/lucene/search/Filter.java	(revision 600387)
+++ src/java/org/apache/lucene/search/Filter.java	(working copy)
@@ -20,11 +20,33 @@
 import java.util.BitSet;
 import java.io.IOException;
 import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.util.BitSetMatcher;
 
-/** Abstract base class providing a mechanism to restrict searches to a subset
- of an index. */
+/** Abstract base class providing a mechanism to use a subset of an index
+ *  for restriction or permission of index search results.
+ *  <p>
+ *  <b>Note:</b> In Lucene 3.0 {@link #bits(IndexReader)} will be removed
+ *  and {@link #getMatcher(IndexReader)} will be defined as abstract.
+ *  All implementing classes must therefore implement {@link #getMatcher(IndexReader)}
+ *  in order to work with Lucene 3.0.
+ */
 public abstract class Filter implements java.io.Serializable {
-  /** Returns a BitSet with true for documents which should be permitted in
-    search results, and false for those that should not. */
-  public abstract BitSet bits(IndexReader reader) throws IOException;
-}
+  /**
+   * @return A BitSet with true for documents which should be permitted in
+   * search results, and false for those that should not.
+   * @deprecated Use {@link #getMatcher(IndexReader)} instead.
+   */
+  public BitSet bits(IndexReader reader) throws IOException {
+  	return null;
+  }
+
+  /**
+   * @return a Matcher that provides the documents which should be
+   * permitted or prohibited in search results.
+   * @see BitSetMatcher
+   */
+  public Matcher getMatcher(IndexReader reader) throws IOException {
+  	return new BitSetMatcher(bits(reader));
+  }
+}   
+
Index: src/java/org/apache/lucene/search/FilteredQuery.java
===================================================================
--- src/java/org/apache/lucene/search/FilteredQuery.java	(revision 600387)
+++ src/java/org/apache/lucene/search/FilteredQuery.java	(working copy)
@@ -21,7 +21,6 @@
 import org.apache.lucene.util.ToStringUtils;
 
 import java.io.IOException;
-import java.util.BitSet;
 import java.util.Set;
 
 
@@ -47,7 +46,7 @@
 
   /**
    * Constructs a new query which applies a filter to the results of the original query.
-   * Filter.bits() will be called every time this query is used in a search.
+   * Filter.getMatcher() will be called every time this query is used in a search.
    * @param query  Query to be filtered, cannot be <code>null</code>.
    * @param filter Filter to apply to query results, cannot be <code>null</code>.
    */
@@ -86,13 +85,15 @@
           inner.addDetail(preBoost);
         }
         Filter f = FilteredQuery.this.filter;
-        BitSet matches = f.bits(ir);
-        if (matches.get(i))
+        Matcher matcher = f.getMatcher(ir);
+        if (matcher.skipTo(i) && (matcher.doc() == i)) {
           return inner;
-        Explanation result = new Explanation
-          (0.0f, "failure to match filter: " + f.toString());
-        result.addDetail(inner);
-        return result;
+        } else {
+          Explanation result = new Explanation
+            (0.0f, "failure to match filter: " + f.toString());
+          result.addDetail(inner);
+          return result;
+        }
       }
 
       // return this query
@@ -100,50 +101,49 @@
 
       // return a filtering scorer
        public Scorer scorer (IndexReader indexReader) throws IOException {
-        final Scorer scorer = weight.scorer (indexReader);
-        final BitSet bitset = filter.bits (indexReader);
-        return new Scorer (similarity) {
+        final Scorer scorer = weight.scorer(indexReader);
+        final Matcher matcher = filter.getMatcher(indexReader);
 
-          public boolean next() throws IOException {
-            do {
-              if (! scorer.next()) {
+        return new Scorer(similarity) {
+
+          private boolean advanceToCommon() throws IOException {
+            while (scorer.doc() != matcher.doc()) {
+              if (scorer.doc() < matcher.doc()) {
+                if (!scorer.skipTo(matcher.doc())) {
+                  return false;
+                }
+              } else if (!matcher.skipTo(scorer.doc())) {
                 return false;
               }
-            } while (! bitset.get(scorer.doc()));
-            /* When skipTo() is allowed on scorer it should be used here
-             * in combination with bitset.nextSetBit(...)
-             * See the while loop in skipTo() below.
-             */
+            }
             return true;
           }
+
+          public boolean next() throws IOException {
+            return matcher.next() && scorer.next() && advanceToCommon();
+          }
+
           public int doc() { return scorer.doc(); }
 
           public boolean skipTo(int i) throws IOException {
-            if (! scorer.skipTo(i)) {
-              return false;
-            }
-            while (! bitset.get(scorer.doc())) {
-              int nextFiltered = bitset.nextSetBit(scorer.doc() + 1);
-              if (nextFiltered == -1) {
-                return false;
-              } else if (! scorer.skipTo(nextFiltered)) {
-                return false;
-              }
-            }
-            return true;
-           }
+            return matcher.skipTo(i)
+                && scorer.skipTo(matcher.doc())
+                && advanceToCommon();
+          }
 
           public float score() throws IOException { return getBoost() * scorer.score(); }
 
           // add an explanation about whether the document was filtered
           public Explanation explain (int i) throws IOException {
-            Explanation exp = scorer.explain (i);
-            exp.setValue(getBoost() * exp.getValue());
+            Explanation exp = scorer.explain(i);
             
-            if (bitset.get(i))
+            if (matcher.skipTo(i) && (matcher.doc() == i)) {
               exp.setDescription ("allowed by filter: "+exp.getDescription());
-            else
+              exp.setValue(getBoost() * exp.getValue());
+            } else {
               exp.setDescription ("removed by filter: "+exp.getDescription());
+              exp.setValue(0.0f);
+            }
             return exp;
           }
         };
Index: src/java/org/apache/lucene/search/FilterManager.java
===================================================================
--- src/java/org/apache/lucene/search/FilterManager.java	(revision 600387)
+++ src/java/org/apache/lucene/search/FilterManager.java	(working copy)
@@ -100,6 +100,7 @@
    * 
    * @param filter The input filter
    * @return The cached version of the filter
+   * @deprecated Use {@link #getFilter(BitSetFilter)} instead
    */
   public Filter getFilter(Filter filter) {
     synchronized(cache) {
@@ -114,6 +115,20 @@
     }
   }
 
+  public BitSetFilter getFilter(BitSetFilter filter) {
+    synchronized(cache) {
+      FilterItem fi = null;
+      fi = (FilterItem)cache.get(new Integer(filter.hashCode()));
+      if (fi != null) {
+        fi.timestamp = new Date().getTime();
+        return (BitSetFilter) fi.filter;
+      }
+      cache.put(new Integer(filter.hashCode()), new FilterItem(filter));
+      return filter;
+    }
+  }
+
+  
   /**
    * Holds the filter and the last time the filter was used, to make LRU-based
    * cache cleaning possible.
Index: src/java/org/apache/lucene/search/HitCollector.java
===================================================================
--- src/java/org/apache/lucene/search/HitCollector.java	(revision 600387)
+++ src/java/org/apache/lucene/search/HitCollector.java	(working copy)
@@ -27,26 +27,13 @@
   /** Called once for every document matching a query, with the document
    * number and its raw score.
    *
-   * <P>If, for example, an application wished to collect all of the hits for a
-   * query in a BitSet, then it might:<pre>
-   *   Searcher searcher = new IndexSearcher(indexReader);
-   *   final BitSet bits = new BitSet(indexReader.maxDoc());
-   *   searcher.search(query, new HitCollector() {
-   *       public void collect(int doc, float score) {
-   *         bits.set(doc);
-   *       }
-   *     });
-   * </pre>
-   *
-   * <p>Note: This is called in an inner search loop.  For good search
-   * performance, implementations of this method should not call
-   * {@link Searcher#doc(int)} or
-   * {@link org.apache.lucene.index.IndexReader#document(int)} on every
-   * document number encountered.  Doing so can slow searches by an order
-   * of magnitude or more.
    * <p>Note: The <code>score</code> passed to this method is a raw score.
    * In other words, the score will not necessarily be a float whose value is
    * between 0 and 1.
+   *
+   * <p>For good performance of inner search loops involving a
+   * <code>HitCollector</code> see the advice given at
+   * {@link MatchCollector#collect(int)}.
    */
   public abstract void collect(int doc, float score);
 }
Index: src/java/org/apache/lucene/search/IndexSearcher.java
===================================================================
--- src/java/org/apache/lucene/search/IndexSearcher.java	(revision 600387)
+++ src/java/org/apache/lucene/search/IndexSearcher.java	(working copy)
@@ -128,24 +128,54 @@
   // inherit javadoc
   public void search(Weight weight, 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 = weight.scorer(reader);
     if (scorer == null)
       return;
-    scorer.score(collector);
+
+    if (filter == null) {
+      scorer.score(results);
+      return;
+    }
+
+    Matcher filterMatcher = filter.getMatcher(reader); // CHECKME: use ConjunctionScorer here?
+    boolean more = filterMatcher.next();
+    while (more) {
+      int filterDocNr = filterMatcher.doc();
+      if (! scorer.skipTo(filterDocNr)) {
+        more = false;
+      } else {
+        int scorerDocNr = scorer.doc();
+        if (scorerDocNr == filterDocNr) { // permitted by filter
+          results.collect(scorerDocNr, scorer.score());
+          more = filterMatcher.skipTo(scorerDocNr + 1);
+        } else {
+          more = filterMatcher.skipTo(scorerDocNr);
+        }
+      }
+    }
   }
 
+  /** Lower-level search API.
+   *
+   * <p>{@link MatchCollector#collect(int)} is called for every matching
+   * document.
+   * <br>When filtering of the results is needed use a {@link FilteredQuery}
+   * for the query argument.
+   * <br>MatchCollector-based access to remote indexes is discouraged.
+   *
+   * <p>Applications should only use this if they need <i>all</i> of the
+   * matching documents.
+   * @throws BooleanQuery.TooManyClauses
+   * @todo Check whether a <code>match</code> method is needed in
+   * {@link Searcher}.
+   */
+  public void match(Query query,
+                    final MatchCollector results) throws IOException {
+    Weight w = createWeight(query);
+    Matcher m = w.scorer(reader);
+    m.match(results);
+  }
+    
   public Query rewrite(Query original) throws IOException {
     Query query = original;
     for (Query rewrittenQuery = query.rewrite(reader); rewrittenQuery != query;
Index: src/java/org/apache/lucene/search/MatchCollector.java
===================================================================
--- src/java/org/apache/lucene/search/MatchCollector.java	(revision 0)
+++ src/java/org/apache/lucene/search/MatchCollector.java	(revision 0)
@@ -0,0 +1,45 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** Lower-level search API.
+ * @see Matcher#match(MatchCollector)
+ */
+public abstract class MatchCollector {
+  /** Called once for every matching document, with the document number.
+   *
+   * <p>If, for example, an application wished to collect all of the hits for a
+   * query in a BitSet, then it might:<pre>
+   *   IndexSearcher searcher = new IndexSearcher(indexReader);
+   *   final BitSet bits = new BitSet(indexReader.maxDoc());
+   *   searcher.match(query, new MatchCollector() {
+   *       public void collect(int doc) {
+   *         bits.set(doc);
+   *       }
+   *     });
+   * </pre>
+   *
+   * <p>Note: This is called in an inner search loop.  For good search
+   * performance, implementations of this method should not call
+   * {@link Searcher#doc(int)} or
+   * {@link org.apache.lucene.index.IndexReader#document(int)} on every
+   * document number encountered.  Doing so can slow searches by an order
+   * of magnitude or more.
+   */
+  public abstract void collect(int doc);
+}
Index: src/java/org/apache/lucene/search/Matcher.java
===================================================================
--- src/java/org/apache/lucene/search/Matcher.java	(revision 0)
+++ src/java/org/apache/lucene/search/Matcher.java	(revision 0)
@@ -0,0 +1,108 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+/** Expert: Common functionality for different types of queries and filters.
+ * <br>A <code>Matcher</code> either iterates over documents matching a query
+ * or a filter, or provides an explanation of the reason why a single document
+ * matches a query or a filter.
+ */
+public abstract class Matcher {
+  /** Collects all matching documents.
+   * @param mc The MatchCollector to which all matching documents are passed
+   * through {@link MatchCollector#collect(int)}.
+   * <br>When this method is used, the {@link #next()}, {@link #skipTo(int)}
+   * and {@link #explain(int)} methods should not be used.
+   */
+  public void match(MatchCollector mc) throws IOException {
+    while (next()) {
+      mc.collect(doc());
+    }
+  }
+
+  /**
+   * Advances to the document matching this Matcher with the lowest doc Id
+   * greater than the current value of {@link #doc()} (or to the matching
+   * document with the lowest doc Id if next has never been called on
+   * this Matcher).
+   *
+   * <p>
+   * When this method is used the {@link #explain(int)}
+   * and {@link #match(MatchCollector)} methods should not be used.
+   * </p>
+   *
+   * @return true iff there is another matching document.
+   */
+  public abstract boolean next() throws IOException;
+
+  /** Returns the current matching document number.
+   * Initially invalid, until {@link #next()} or {@link #skipTo(int)}
+   * is called the first time.
+   */
+  public abstract int doc();
+
+  /** 
+   * Skips to the document matching this Matcher with the lowest doc Id
+   * greater than or equal to a given target.
+   *
+   * <p>
+   * The behavior of this method is undefined if the target specified is
+   * less than or equal to the current value of {@link #doc()}.
+   * <p>
+   * Behaves as if written:
+   * <pre>
+   *   boolean skipTo(int target) {
+   *     do {
+   *       if (!next())
+   * 	     return false;
+   *     } while (target > doc());
+   *     return true;
+   *   }
+   * </pre>
+   * Most implementations are considerably more efficient than that.
+   * </p>
+   *
+   * <p>
+   * When this method is used the {@link #explain(int)}
+   * and {@link #match(MatchCollector)} methods should not be used.
+   * </p>
+   *
+   * @param target The target document number.
+   * @return true iff there is such a match.
+   */
+  public abstract boolean skipTo(int target) throws IOException;
+
+  /** Returns an explanation of the matching of a single document.
+   * <br>When this method is used, the {@link #next()}, {@link #skipTo(int)}
+   * and {@link #match(MatchCollector)} methods should not be used.<br>
+   * This implementation uses {@link #toString()} in the description
+   * of the explanation of the match.
+   * @param docNr The document number for the explanation.
+   */
+  public Explanation explain(int docNr) throws IOException {
+    ComplexExplanation ce = new ComplexExplanation();
+    boolean match = skipTo(docNr) && (doc() == docNr);
+    ce.setMatch(new Boolean(match));
+    ce.setDescription((match ? "included" : "excluded")
+                       + " by " + this.toString());
+    return ce;
+  }
+
+}
Index: src/java/org/apache/lucene/search/QueryWrapperFilter.java
===================================================================
--- src/java/org/apache/lucene/search/QueryWrapperFilter.java	(revision 600387)
+++ src/java/org/apache/lucene/search/QueryWrapperFilter.java	(working copy)
@@ -34,7 +34,7 @@
  *
  * @version $Id:$
  */
-public class QueryWrapperFilter extends Filter {
+public class QueryWrapperFilter extends BitSetFilter {
   private Query query;
 
   /** Constructs a filter which only matches documents matching

Property changes on: src\java\org\apache\lucene\search\QueryWrapperFilter.java
___________________________________________________________________
Name: svn:eol-style
   + native

Index: src/java/org/apache/lucene/search/RemoteCachingBitSetFilter.java
===================================================================
--- src/java/org/apache/lucene/search/RemoteCachingBitSetFilter.java	(revision 0)
+++ src/java/org/apache/lucene/search/RemoteCachingBitSetFilter.java	(revision 0)
@@ -0,0 +1,58 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.BitSet;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * Provides caching of {@link BitSetFilter}s themselves on the remote end of an RMI connection.
+ * The cache is keyed on Filter's hashCode(), so if it sees the same filter twice
+ * it will reuse the original version.
+ * <p/>
+ * NOTE: This does NOT cache the Filter bits, but rather the Filter itself.
+ * Thus, this works hand-in-hand with {@link CachingBitSetFilter} to keep both
+ * file Filter cache and the Filter bits on the remote end, close to the searcher.
+ * <p/>
+ * Usage:
+ * <p/>
+ * To cache a result you must do something like 
+ * RemoteCachingBitSetFilter f = new RemoteCachingBitSetFilter(new CachingBitSetFilter(myFilter));
+ * <p/>
+ * @author Matt Ericson
+ */
+public class RemoteCachingBitSetFilter extends BitSetFilter {
+  protected BitSetFilter filter;
+
+  public RemoteCachingBitSetFilter(BitSetFilter filter) {
+    this.filter = filter;
+  }
+
+  /**
+   * Uses the {@link FilterManager} to keep the cache for a filter on the 
+   * searcher side of a remote connection.
+   * @param reader the index reader for the Filter
+   * @return the bitset
+   */
+  public BitSet bits(IndexReader reader) throws IOException {
+    BitSetFilter cachedFilter = FilterManager.getInstance().getFilter(filter);
+    return cachedFilter.bits(reader);
+  }
+}
Index: src/java/org/apache/lucene/search/RemoteCachingWrapperFilter.java
===================================================================
--- src/java/org/apache/lucene/search/RemoteCachingWrapperFilter.java	(revision 600387)
+++ src/java/org/apache/lucene/search/RemoteCachingWrapperFilter.java	(working copy)
@@ -37,6 +37,7 @@
  * RemoteCachingWrapperFilter f = new RemoteCachingWrapperFilter(new CachingWrapperFilter(myFilter));
  * <p/>
  * @author Matt Ericson
+ * @deprecated Use {@link RemoteCachingBitSetFilter} instead
  */
 public class RemoteCachingWrapperFilter extends Filter {
   protected Filter filter;
Index: src/java/org/apache/lucene/search/Scorer.java
===================================================================
--- src/java/org/apache/lucene/search/Scorer.java	(revision 600387)
+++ src/java/org/apache/lucene/search/Scorer.java	(working copy)
@@ -33,7 +33,7 @@
  * </p>
  * @see BooleanQuery#setAllowDocsOutOfOrder
  */
-public abstract class Scorer {
+public abstract class Scorer extends Matcher {
   private Similarity similarity;
 
   /** Constructs a Scorer.
@@ -76,64 +76,13 @@
     return true;
   }
 
-  /**
-   * Advances to the document matching this Scorer with the lowest doc Id
-   * greater than the current value of {@link #doc()} (or to the matching
-   * document with the lowest doc Id if next has never been called on
-   * this Scorer).
-   *
-   * <p>
-   * When this method is used the {@link #explain(int)} method should not
-   * be used.
-   * </p>
-   *
-   * @return true iff there is another document matching the query.
-   * @see BooleanQuery#setAllowDocsOutOfOrder
-   */
-  public abstract boolean next() throws IOException;
 
-  /** Returns the current document number matching the query.
-   * Initially invalid, until {@link #next()} is called the first time.
-   */
-  public abstract int doc();
-
   /** Returns the score of the current document matching the query.
    * Initially invalid, until {@link #next()} or {@link #skipTo(int)}
    * is called the first time.
    */
   public abstract float score() throws IOException;
 
-  /**
-   * Skips to the document matching this Scorer with the lowest doc Id
-   * greater than or equal to a given target.
-   *
-   * <p>
-   * The behavior of this method is undefined if the target specified is
-   * less than or equal to the current value of {@link #doc()}.
-   * <p>
-   * Behaves as if written:
-   * <pre>
-   *   boolean skipTo(int target) {
-   *     do {
-   *       if (!next())
-   * 	     return false;
-   *     } while (target > doc());
-   *     return true;
-   *   }
-   * </pre>
-   * Most implementations are considerably more efficient than that.
-   * </p>
-   *
-   * <p>
-   * When this method is used the {@link #explain(int)} method should not
-   * be used.
-   * </p>
-   *
-   * @param target The target document number.
-   * @return true iff there is such a match.
-   * @see BooleanQuery#setAllowDocsOutOfOrder
-   */
-  public abstract boolean skipTo(int target) throws IOException;
 
   /** Returns an explanation of the score for a document.
    * <br>When this method is used, the {@link #next()}, {@link #skipTo(int)} and
Index: src/java/org/apache/lucene/search/Searchable.java
===================================================================
--- src/java/org/apache/lucene/search/Searchable.java	(revision 600387)
+++ src/java/org/apache/lucene/search/Searchable.java	(working copy)
@@ -48,7 +48,7 @@
    * non-high-scoring hits.
    *
    * @param weight to match documents
-   * @param filter if non-null, a bitset used to eliminate some documents
+   * @param filter if non-null, used to permit documents to be collected.
    * @param results to receive hits
    * @throws BooleanQuery.TooManyClauses
    */
@@ -92,7 +92,8 @@
   TopDocs search(Weight weight, Filter filter, int n) throws IOException;
 
   /** Expert: Returns the stored fields of document <code>i</code>.
-   * Called by {@link HitCollector} implementations.
+   * Called by {@link HitCollector} and {@link MatchCollector} implementations,
+   * normally after the inner search loop.
    * @see IndexReader#document(int)
    * @throws CorruptIndexException if the index is corrupt
    * @throws IOException if there is a low-level IO error
Index: src/java/org/apache/lucene/search/Searcher.java
===================================================================
--- src/java/org/apache/lucene/search/Searcher.java	(revision 600387)
+++ src/java/org/apache/lucene/search/Searcher.java	(working copy)
@@ -109,7 +109,7 @@
    * non-high-scoring hits.
    *
    * @param query to match documents
-   * @param filter if non-null, a bitset used to eliminate some documents
+   * @param filter if non-null, used to permit documents to be collected.
    * @param results to receive hits
    * @throws BooleanQuery.TooManyClauses
    */
Index: src/java/org/apache/lucene/util/BitSetMatcher.java
===================================================================
--- src/java/org/apache/lucene/util/BitSetMatcher.java	(revision 0)
+++ src/java/org/apache/lucene/util/BitSetMatcher.java	(revision 0)
@@ -0,0 +1,57 @@
+package org.apache.lucene.util;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.BitSet;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Matcher;
+
+/** A Matcher constructed from a BitSet */
+public class BitSetMatcher extends Matcher {
+  private BitSet bitset;
+  private int docNr = -1;
+  
+  public BitSetMatcher(BitSet bitset) {
+    this.bitset = bitset;
+  }
+
+  public int doc() {
+    assert docNr != -1;
+    return docNr;
+  }
+  
+  public boolean next() {
+    // (docNr + 1) on next line requires -1 initial value for docNr:
+    return checkNextDocNr(bitset.nextSetBit(docNr + 1));
+  }
+
+  public boolean skipTo(int skipDocNr) {
+    return checkNextDocNr( bitset.nextSetBit(skipDocNr));
+  }
+
+  private boolean checkNextDocNr(int d) {
+    if (d == -1) { // -1 returned by BitSet.nextSetBit() when exhausted
+      docNr = Integer.MAX_VALUE;
+      return false;
+    } else {
+      docNr = d;
+      return true;
+    }
+  }
+}
+
Index: src/test/org/apache/lucene/search/CachingBitSetFilterHelper.java
===================================================================
--- src/test/org/apache/lucene/search/CachingBitSetFilterHelper.java	(revision 0)
+++ src/test/org/apache/lucene/search/CachingBitSetFilterHelper.java	(revision 0)
@@ -0,0 +1,80 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.WeakHashMap;
+
+import junit.framework.TestCase;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A unit test helper class to test when the filter is getting cached and when it is not.
+ */
+public class CachingBitSetFilterHelper extends CachingBitSetFilter {
+  
+  private boolean shouldHaveCache = false;
+
+  /**
+   * @param filter Filter to cache results of
+   */
+  public CachingBitSetFilterHelper(BitSetFilter filter) {
+    super(filter);
+  }
+  
+  public void setShouldHaveCache(boolean shouldHaveCache) {
+    this.shouldHaveCache = shouldHaveCache;
+  }
+  
+  public BitSet bits(IndexReader reader) throws IOException {
+    if (cache == null) {
+      cache = new WeakHashMap();
+    }
+    
+    synchronized (cache) {  // check cache
+      BitSet cached = (BitSet) cache.get(reader);
+      if (shouldHaveCache) {
+        TestCase.assertNotNull("Cache should have data ", cached);
+      } else {
+        TestCase.assertNull("Cache should be null " + cached , cached);
+      }
+      if (cached != null) {
+        return cached;
+      }
+    }
+
+    final BitSet bits = filter.bits(reader);
+
+    synchronized (cache) {  // update cache
+      cache.put(reader, bits);
+    }
+
+    return bits;
+  }
+
+  public String toString() {
+    return "CachingBitSetFilterHelper("+filter+")";
+  }
+
+  public boolean equals(Object o) {
+    if (!(o instanceof CachingBitSetFilterHelper)) return false;
+    return this.filter.equals((CachingBitSetFilterHelper)o);
+  }
+}
Index: src/test/org/apache/lucene/search/CachingWrapperFilterHelper.java
===================================================================
--- src/test/org/apache/lucene/search/CachingWrapperFilterHelper.java	(revision 600387)
+++ src/test/org/apache/lucene/search/CachingWrapperFilterHelper.java	(working copy)
@@ -27,6 +27,7 @@
 
 /**
  * A unit test helper class to test when the filter is getting cached and when it is not.
+ * @deprecated
  */
 public class CachingWrapperFilterHelper extends CachingWrapperFilter {
   
Index: src/test/org/apache/lucene/search/MockFilter.java
===================================================================
--- src/test/org/apache/lucene/search/MockFilter.java	(revision 600387)
+++ src/test/org/apache/lucene/search/MockFilter.java	(working copy)
@@ -20,7 +20,7 @@
 import org.apache.lucene.index.IndexReader;
 import java.util.BitSet;
 
-public class MockFilter extends Filter {
+public class MockFilter extends BitSetFilter {
   private boolean wasCalled;
 
   public BitSet bits(IndexReader reader) {
Index: src/test/org/apache/lucene/search/RemoteCachingBitSetFilterHelper.java
===================================================================
--- src/test/org/apache/lucene/search/RemoteCachingBitSetFilterHelper.java	(revision 0)
+++ src/test/org/apache/lucene/search/RemoteCachingBitSetFilterHelper.java	(revision 0)
@@ -0,0 +1,60 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.BitSet;
+
+import junit.framework.TestCase;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * A unit test helper class to help with RemoteCachingWrapperFilter testing and
+ * assert that it is working correctly.
+ * @author Matt Ericson
+ */
+public class RemoteCachingBitSetFilterHelper extends RemoteCachingBitSetFilter {
+
+  private boolean shouldHaveCache;
+
+  public RemoteCachingBitSetFilterHelper(BitSetFilter filter, boolean shouldHaveCache) {
+    super(filter);
+    this.shouldHaveCache = shouldHaveCache;
+  }
+  
+  public void shouldHaveCache(boolean shouldHaveCache) {
+    this.shouldHaveCache = shouldHaveCache;
+  }
+
+  public BitSet bits(IndexReader reader) throws IOException {
+    BitSetFilter cachedFilter = FilterManager.getInstance().getFilter(filter);
+    
+    TestCase.assertNotNull("Filter should not be null", cachedFilter);
+    if (!shouldHaveCache) {
+      TestCase.assertSame("First time filter should be the same ", filter, cachedFilter);
+    } else {
+      TestCase.assertNotSame("We should have a cached version of the filter", filter, cachedFilter);
+    }
+    
+    if (filter instanceof CachingBitSetFilterHelper) {
+      ((CachingBitSetFilterHelper)cachedFilter).setShouldHaveCache(shouldHaveCache);
+    }
+    return cachedFilter.bits(reader);
+  }
+}
Index: src/test/org/apache/lucene/search/RemoteCachingWrapperFilterHelper.java
===================================================================
--- src/test/org/apache/lucene/search/RemoteCachingWrapperFilterHelper.java	(revision 600387)
+++ src/test/org/apache/lucene/search/RemoteCachingWrapperFilterHelper.java	(working copy)
@@ -28,6 +28,7 @@
  * A unit test helper class to help with RemoteCachingWrapperFilter testing and
  * assert that it is working correctly.
  * @author Matt Ericson
+ * @deprecated
  */
 public class RemoteCachingWrapperFilterHelper extends RemoteCachingWrapperFilter {
 
Index: src/test/org/apache/lucene/search/TestCachingBitSetFilter.java
===================================================================
--- src/test/org/apache/lucene/search/TestCachingBitSetFilter.java	(revision 0)
+++ src/test/org/apache/lucene/search/TestCachingBitSetFilter.java	(revision 0)
@@ -0,0 +1,49 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+
+public class TestCachingBitSetFilter extends LuceneTestCase {
+  public void testCachingWorks() throws Exception {
+    Directory dir = new RAMDirectory();
+    IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), true);
+    writer.close();
+
+    IndexReader reader = IndexReader.open(dir);
+
+    MockFilter filter = new MockFilter();
+    CachingBitSetFilter cacher = new CachingBitSetFilter(filter);
+
+    // first time, nested filter is called
+    cacher.getMatcher(reader);
+    assertTrue("first time", filter.wasCalled());
+
+    // second time, nested filter should not be called
+    filter.clear();
+    cacher.getMatcher(reader);
+    assertFalse("second time", filter.wasCalled());
+
+    reader.close();
+ }
+}
Index: src/test/org/apache/lucene/search/TestCachingWrapperFilter.java
===================================================================
--- src/test/org/apache/lucene/search/TestCachingWrapperFilter.java	(revision 600387)
+++ src/test/org/apache/lucene/search/TestCachingWrapperFilter.java	(working copy)
@@ -24,6 +24,9 @@
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.analysis.standard.StandardAnalyzer;
 
+/** 
+ * @deprecated
+ */
 public class TestCachingWrapperFilter extends LuceneTestCase {
   public void testCachingWorks() throws Exception {
     Directory dir = new RAMDirectory();
@@ -36,12 +39,12 @@
     CachingWrapperFilter cacher = new CachingWrapperFilter(filter);
 
     // first time, nested filter is called
-    cacher.bits(reader);
+    cacher.getMatcher(reader);
     assertTrue("first time", filter.wasCalled());
 
     // second time, nested filter should not be called
     filter.clear();
-    cacher.bits(reader);
+    cacher.getMatcher(reader);
     assertFalse("second time", filter.wasCalled());
 
     reader.close();
Index: src/test/org/apache/lucene/search/TestRemoteCachingBitSetFilter.java
===================================================================
--- src/test/org/apache/lucene/search/TestRemoteCachingBitSetFilter.java	(revision 0)
+++ src/test/org/apache/lucene/search/TestRemoteCachingBitSetFilter.java	(revision 0)
@@ -0,0 +1,127 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.rmi.Naming;
+import java.rmi.registry.LocateRegistry;
+
+import org.apache.lucene.util.LuceneTestCase;
+
+import org.apache.lucene.analysis.SimpleAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.store.RAMDirectory;
+
+/**
+ * Tests that the index is cached on the searcher side of things.
+ * NOTE: This is copied from TestRemoteSearchable since it already had a remote index set up.
+ * @author Matt Ericson
+ */
+public class TestRemoteCachingBitSetFilter extends LuceneTestCase {
+  public TestRemoteCachingBitSetFilter(String name) {
+    super(name);
+  }
+
+  private static Searchable getRemote() throws Exception {
+    try {
+      return lookupRemote();
+    } catch (Throwable e) {
+      startServer();
+      return lookupRemote();
+    }
+  }
+
+  private static Searchable lookupRemote() throws Exception {
+    return (Searchable)Naming.lookup("//localhost/Searchable");
+  }
+
+  private static void startServer() throws Exception {
+    // construct an index
+    RAMDirectory indexStore = new RAMDirectory();
+    IndexWriter writer = new IndexWriter(indexStore,new SimpleAnalyzer(),true);
+    Document doc = new Document();
+    doc.add(new Field("test", "test text", Field.Store.YES, Field.Index.TOKENIZED));
+    doc.add(new Field("type", "A", Field.Store.YES, Field.Index.TOKENIZED));
+    doc.add(new Field("other", "other test text", Field.Store.YES, Field.Index.TOKENIZED));
+    writer.addDocument(doc);
+    //Need a second document to search for
+    doc = new Document();
+    doc.add(new Field("test", "test text", Field.Store.YES, Field.Index.TOKENIZED));
+    doc.add(new Field("type", "B", Field.Store.YES, Field.Index.TOKENIZED));
+    doc.add(new Field("other", "other test text", Field.Store.YES, Field.Index.TOKENIZED));
+    writer.addDocument(doc);
+    writer.optimize();
+    writer.close();
+
+    // publish it
+    LocateRegistry.createRegistry(1099);
+    Searchable local = new IndexSearcher(indexStore);
+    RemoteSearchable impl = new RemoteSearchable(local);
+    Naming.rebind("//localhost/Searchable", impl);
+  }
+
+  private static void search(Query query, Filter filter, int hitNumber, String typeValue) throws Exception {
+    Searchable[] searchables = { getRemote() };
+    Searcher searcher = new MultiSearcher(searchables);
+    Hits result = searcher.search(query,filter);
+    assertEquals(1, result.length());
+    Document document = result.doc(hitNumber);
+    assertTrue("document is null and it shouldn't be", document != null);
+    assertEquals(typeValue, document.get("type"));
+    assertTrue("document.getFields() Size: " + document.getFields().size() + " is not: " + 3, document.getFields().size() == 3);
+  }
+
+
+  public void testTermRemoteFilter() throws Exception {
+    CachingBitSetFilterHelper cwfh = new CachingBitSetFilterHelper(new QueryWrapperFilter(new TermQuery(new Term("type", "a"))));
+    
+    // This is what we are fixing - if one uses a CachingWrapperFilter(Helper) it will never 
+    // cache the filter on the remote site
+    cwfh.setShouldHaveCache(false);
+    search(new TermQuery(new Term("test", "test")), cwfh, 0, "A");
+    cwfh.setShouldHaveCache(false);
+    search(new TermQuery(new Term("test", "test")), cwfh, 0, "A");
+    
+    // This is how we fix caching - we wrap a Filter in the RemoteCachingWrapperFilter(Handler - for testing)
+    // to cache the Filter on the searcher (remote) side
+    RemoteCachingBitSetFilterHelper rcwfh = new RemoteCachingBitSetFilterHelper(cwfh, false);
+    search(new TermQuery(new Term("test", "test")), rcwfh, 0, "A");
+
+    // 2nd time we do the search, we should be using the cached Filter
+    rcwfh.shouldHaveCache(true);
+    search(new TermQuery(new Term("test", "test")), rcwfh, 0, "A");
+
+    // assert that we get the same cached Filter, even if we create a new instance of RemoteCachingWrapperFilter(Helper)
+    // this should pass because the Filter parameters are the same, and the cache uses Filter's hashCode() as cache keys,
+    // and Filters' hashCode() builds on Filter parameters, not the Filter instance itself
+    rcwfh = new RemoteCachingBitSetFilterHelper(new QueryWrapperFilter(new TermQuery(new Term("type", "a"))), false);
+    rcwfh.shouldHaveCache(false);
+    search(new TermQuery(new Term("test", "test")), rcwfh, 0, "A");
+
+    rcwfh = new RemoteCachingBitSetFilterHelper(new QueryWrapperFilter(new TermQuery(new Term("type", "a"))), false);
+    rcwfh.shouldHaveCache(true);
+    search(new TermQuery(new Term("test", "test")), rcwfh, 0, "A");
+
+    // assert that we get a non-cached version of the Filter because this is a new Query (type:b)
+    rcwfh = new RemoteCachingBitSetFilterHelper(new QueryWrapperFilter(new TermQuery(new Term("type", "b"))), false);
+    rcwfh.shouldHaveCache(false);
+    search(new TermQuery(new Term("type", "b")), rcwfh, 0, "B");
+  }
+}
Index: src/test/org/apache/lucene/search/TestRemoteCachingWrapperFilter.java
===================================================================
--- src/test/org/apache/lucene/search/TestRemoteCachingWrapperFilter.java	(revision 600387)
+++ src/test/org/apache/lucene/search/TestRemoteCachingWrapperFilter.java	(working copy)
@@ -33,6 +33,7 @@
  * Tests that the index is cached on the searcher side of things.
  * NOTE: This is copied from TestRemoteSearchable since it already had a remote index set up.
  * @author Matt Ericson
+ * @deprecated
  */
 public class TestRemoteCachingWrapperFilter extends LuceneTestCase {
   public TestRemoteCachingWrapperFilter(String name) {
