Index: lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java	(révision 1501578)
+++ lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java	(copie de travail)
@@ -287,15 +287,15 @@
   
   private void tChainedFilters(final boolean useRandomAccess) throws Exception {
     Query query = new FilteredQuery(new FilteredQuery(
-      new MatchAllDocsQuery(), new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "three")))), randomFilterStrategy(random(), useRandomAccess)),
-      new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "four")))), randomFilterStrategy(random(), useRandomAccess));
+      new MatchAllDocsQuery(), cache(new QueryWrapperFilter(new TermQuery(new Term("field", "three")))), randomFilterStrategy(random(), useRandomAccess)),
+      cache(new QueryWrapperFilter(new TermQuery(new Term("field", "four")))), randomFilterStrategy(random(), useRandomAccess));
     ScoreDoc[] hits = searcher.search(query, 10).scoreDocs;
     assertEquals(2, hits.length);
     QueryUtils.check(random(), query, searcher);    
 
     // one more:
     query = new FilteredQuery(query,
-      new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "five")))), randomFilterStrategy(random(), useRandomAccess));
+      cache(new QueryWrapperFilter(new TermQuery(new Term("field", "five")))), randomFilterStrategy(random(), useRandomAccess));
     hits = searcher.search(query, 10).scoreDocs;
     assertEquals(1, hits.length);
     QueryUtils.check(random(), query, searcher);    
Index: lucene/core/src/test/org/apache/lucene/search/TestQueryWrapperFilter.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestQueryWrapperFilter.java	(révision 1501578)
+++ lucene/core/src/test/org/apache/lucene/search/TestQueryWrapperFilter.java	(copie de travail)
@@ -48,7 +48,7 @@
     IndexSearcher searcher = newSearcher(reader);
     TopDocs hits = searcher.search(new MatchAllDocsQuery(), qwf, 10);
     assertEquals(1, hits.totalHits);
-    hits = searcher.search(new MatchAllDocsQuery(), new CachingWrapperFilter(qwf), 10);
+    hits = searcher.search(new MatchAllDocsQuery(), cache(qwf), 10);
     assertEquals(1, hits.totalHits);
 
     // should not throw exception with complex primitive query
@@ -60,7 +60,7 @@
 
     hits = searcher.search(new MatchAllDocsQuery(), qwf, 10);
     assertEquals(1, hits.totalHits);
-    hits = searcher.search(new MatchAllDocsQuery(), new CachingWrapperFilter(qwf), 10);
+    hits = searcher.search(new MatchAllDocsQuery(), cache(qwf), 10);
     assertEquals(1, hits.totalHits);
 
     // should not throw exception with non primitive Query (doesn't implement
@@ -69,7 +69,7 @@
 
     hits = searcher.search(new MatchAllDocsQuery(), qwf, 10);
     assertEquals(1, hits.totalHits);
-    hits = searcher.search(new MatchAllDocsQuery(), new CachingWrapperFilter(qwf), 10);
+    hits = searcher.search(new MatchAllDocsQuery(), cache(qwf), 10);
     assertEquals(1, hits.totalHits);
 
     // test a query with no hits
@@ -77,7 +77,7 @@
     qwf = new QueryWrapperFilter(termQuery);
     hits = searcher.search(new MatchAllDocsQuery(), qwf, 10);
     assertEquals(0, hits.totalHits);
-    hits = searcher.search(new MatchAllDocsQuery(), new CachingWrapperFilter(qwf), 10);
+    hits = searcher.search(new MatchAllDocsQuery(), cache(qwf), 10);
     assertEquals(0, hits.totalHits);
     reader.close();
     dir.close();
Index: lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java	(révision 1501578)
+++ lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java	(copie de travail)
@@ -142,13 +142,13 @@
     IndexReader r = w.getReader();
     w.close();
 
-    Filter filterB = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "b"))));
+    Filter filterB = cache(new QueryWrapperFilter(new TermQuery(new Term("field", "b"))));
     Query query = new ConstantScoreQuery(filterB);
 
     IndexSearcher s = newSearcher(r);
     assertEquals(1, s.search(query, filterB, 1).totalHits); // Query for field:b, Filter field:b
 
-    Filter filterA = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("field", "a"))));
+    Filter filterA = cache(new QueryWrapperFilter(new TermQuery(new Term("field", "a"))));
     query = new ConstantScoreQuery(filterA);
 
     assertEquals(0, s.search(query, filterB, 1).totalHits); // Query field:b, Filter field:a
Index: lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java	(révision 1501578)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java	(copie de travail)
@@ -99,8 +99,7 @@
   /** Create a ToParentBlockJoinQuery.
    * 
    * @param childQuery Query matching child documents.
-   * @param parentsFilter Filter (must produce FixedBitSet
-   * per-segment) identifying the parent documents.
+   * @param parentsFilter Filter identifying the parent documents.
    * @param scoreMode How to aggregate multiple child scores
    * into a single parent score.
    **/
@@ -179,15 +178,13 @@
       // not return a FixedBitSet but rather a
       // BitsFilteredDocIdSet.  Instead, we filter by
       // acceptDocs when we score:
-      final DocIdSet parents = parentsFilter.getDocIdSet(readerContext, null);
+      final DocIdSet parents = JoinUtil.toFixedBitSet(readerContext.reader().maxDoc(), parentsFilter.getDocIdSet(readerContext, null));
 
       if (parents == null) {
         // No matches
         return null;
       }
-      if (!(parents instanceof FixedBitSet)) {
-        throw new IllegalStateException("parentFilter must return FixedBitSet; got " + parents);
-      }
+      assert parents instanceof FixedBitSet;
 
       return new BlockJoinScorer(this, childScorer, (FixedBitSet) parents, firstChildDoc, scoreMode, acceptDocs);
     }
Index: lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java	(révision 1501578)
+++ lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java	(copie de travail)
@@ -17,8 +17,11 @@
  * limitations under the License.
  */
 
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
+import org.apache.lucene.util.FixedBitSet;
 
 import java.io.IOException;
 import java.util.Locale;
@@ -90,4 +93,18 @@
     }
   }
 
+  static FixedBitSet toFixedBitSet(int maxDoc, DocIdSet set) throws IOException {
+    if (set == null || set instanceof FixedBitSet) {
+      return (FixedBitSet) set;
+    }
+    final DocIdSetIterator it = set.iterator();
+    if (it == null || it.nextDoc() == DocIdSetIterator.NO_MORE_DOCS) {
+      return null;
+    }
+    final FixedBitSet fixedSet = new FixedBitSet(maxDoc);
+    fixedSet.set(it.docID());
+    fixedSet.or(it);
+    return fixedSet;
+  }
+
 }
Index: lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java	(révision 1501578)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java	(copie de travail)
@@ -24,7 +24,6 @@
 
 import org.apache.lucene.index.AtomicReaderContext;
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriter;       // javadocs
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.DocIdSet;
 import org.apache.lucene.search.Explanation;
@@ -32,7 +31,6 @@
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Scorer.ChildScorer;
 import org.apache.lucene.search.Weight;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.FixedBitSet;
@@ -63,8 +61,7 @@
    * Create a ToChildBlockJoinQuery.
    * 
    * @param parentQuery Query that matches parent documents
-   * @param parentsFilter Filter (must produce FixedBitSet
-   * per-segment) identifying the parent documents.
+   * @param parentsFilter Filter identifying the parent documents.
    * @param doScores true if parent scores should be calculated
    */
   public ToChildBlockJoinQuery(Query parentQuery, Filter parentsFilter, boolean doScores) {
@@ -136,15 +133,13 @@
       // not return a FixedBitSet but rather a
       // BitsFilteredDocIdSet.  Instead, we filter by
       // acceptDocs when we score:
-      final DocIdSet parents = parentsFilter.getDocIdSet(readerContext, null);
+      final DocIdSet parents = JoinUtil.toFixedBitSet(readerContext.reader().maxDoc(), parentsFilter.getDocIdSet(readerContext, null));
 
       if (parents == null) {
         // No matches
         return null;
       }
-      if (!(parents instanceof FixedBitSet)) {
-        throw new IllegalStateException("parentFilter must return FixedBitSet; got " + parents);
-      }
+      assert parents instanceof FixedBitSet;
 
       return new ToChildBlockJoinScorer(this, parentScorer, (FixedBitSet) parents, doScores, acceptDocs);
     }
Index: lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
===================================================================
--- lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java	(révision 1501578)
+++ lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java	(copie de travail)
@@ -93,7 +93,7 @@
     w.close();
     assertTrue(r.leaves().size() > 1);
     IndexSearcher s = new IndexSearcher(r);
-    Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
+    Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
 
     BooleanQuery childQuery = new BooleanQuery();
     childQuery.add(new BooleanClause(new TermQuery(new Term("skill", "java")), Occur.MUST));
@@ -145,7 +145,7 @@
     IndexSearcher s = newSearcher(r);
 
     // Create a filter that defines "parent" documents in the index - in this case resumes
-    Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
+    Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
 
     // Define child document criteria (finds an example of relevant work experience)
     BooleanQuery childQuery = new BooleanQuery();
@@ -249,7 +249,7 @@
     IndexSearcher s = newSearcher(r);
 
     // Create a filter that defines "parent" documents in the index - in this case resumes
-    Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
+    Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
 
     // Define child document criteria (finds an example of relevant work experience)
     BooleanQuery childQuery = new BooleanQuery();
@@ -269,7 +269,7 @@
     assertEquals("dummy filter passes everyone ", 2, s.search(childJoinQuery, new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))), 10).totalHits);
       
     // not found test
-    assertEquals("noone live there", 0, s.search(childJoinQuery, new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("country", "Oz")))), 1).totalHits);
+    assertEquals("noone live there", 0, s.search(childJoinQuery, cache(new QueryWrapperFilter(new TermQuery(new Term("country", "Oz")))), 1).totalHits);
     assertEquals("noone live there", 0, s.search(childJoinQuery, new QueryWrapperFilter(new TermQuery(new Term("country", "Oz"))), 1).totalHits);
       
     // apply the UK filter by the searcher
@@ -311,7 +311,7 @@
     final List<AtomicReaderContext> leaves = reader.leaves();
     final int subIndex = ReaderUtil.subIndex(childDocID, leaves);
     final AtomicReaderContext leaf = leaves.get(subIndex);
-    final FixedBitSet bits = (FixedBitSet) parents.getDocIdSet(leaf, null);
+    final FixedBitSet bits = JoinUtil.toFixedBitSet(leaf.reader().maxDoc(), parents.getDocIdSet(leaf, null));
     return leaf.reader().document(bits.nextSetBit(childDocID - leaf.docBase));
   }
   
@@ -362,7 +362,7 @@
 
     ToParentBlockJoinQuery q = new ToParentBlockJoinQuery(
         NumericRangeQuery.newIntRange("year", 1990, 2010, true, true),
-        new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume")))),
+        cache(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume")))),
         ScoreMode.Total
     );
 
@@ -565,7 +565,7 @@
 
     final IndexSearcher joinS = new IndexSearcher(joinR);
 
-    final Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("isParent", "x"))));
+    final Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("isParent", "x"))));
 
     final int iters = 200*RANDOM_MULTIPLIER;
 
@@ -830,8 +830,7 @@
         if (random().nextBoolean()) { // filtered case
           childJoinQuery2 = parentJoinQuery2;
           final Filter f = new QueryWrapperFilter(new TermQuery(childTerm));
-          childJoinFilter2 = random().nextBoolean()
-                  ? new CachingWrapperFilter(f): f;
+          childJoinFilter2 = maybeCache(f);
         } else {
           childJoinFilter2 = null;
           // AND child field w/ parent query:
@@ -851,8 +850,7 @@
         if (random().nextBoolean()) { // filtered case
           childQuery2 = parentQuery2;
           final Filter f = new QueryWrapperFilter(new TermQuery(childTerm));
-          childFilter2 = random().nextBoolean()
-                  ? new CachingWrapperFilter(f): f;
+          childFilter2 = maybeCache(f);
         } else {
           childFilter2 = null;
           final BooleanQuery bq2 = new BooleanQuery();
@@ -991,7 +989,7 @@
     IndexSearcher s = newSearcher(r);
 
     // Create a filter that defines "parent" documents in the index - in this case resumes
-    Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
+    Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
 
     // Define child document criteria (finds an example of relevant work experience)
     BooleanQuery childJobQuery = new BooleanQuery();
@@ -1072,7 +1070,7 @@
     w.close();
     IndexSearcher s = newSearcher(r);
     Query tq = new TermQuery(new Term("child", "1"));
-    Filter parentFilter = new CachingWrapperFilter(
+    Filter parentFilter = cache(
                             new QueryWrapperFilter(
                               new TermQuery(new Term("parent", "1"))));
 
@@ -1106,7 +1104,7 @@
     w.close();
     IndexSearcher s = newSearcher(r);
     Query tq = new TermQuery(new Term("child", "2"));
-    Filter parentFilter = new CachingWrapperFilter(
+    Filter parentFilter = cache(
                             new QueryWrapperFilter(
                               new TermQuery(new Term("isparent", "yes"))));
 
@@ -1140,7 +1138,7 @@
     IndexSearcher s = new IndexSearcher(r);
 
     // Create a filter that defines "parent" documents in the index - in this case resumes
-    Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
+    Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
 
     // Define child document criteria (finds an example of relevant work experience)
     BooleanQuery childQuery = new BooleanQuery();
@@ -1244,7 +1242,7 @@
     w.close();
 
     Query childQuery = new TermQuery(new Term("childText", "text"));
-    Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("isParent", "yes"))));
+    Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("isParent", "yes"))));
     ToParentBlockJoinQuery childJoinQuery = new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Avg);
     BooleanQuery parentQuery = new BooleanQuery();
     parentQuery.add(childJoinQuery, Occur.SHOULD);
@@ -1310,7 +1308,7 @@
 
     // never matches:
     Query childQuery = new TermQuery(new Term("childText", "bogus"));
-    Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("isParent", "yes"))));
+    Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("isParent", "yes"))));
     ToParentBlockJoinQuery childJoinQuery = new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Avg);
     BooleanQuery parentQuery = new BooleanQuery();
     parentQuery.add(childJoinQuery, Occur.SHOULD);
@@ -1376,7 +1374,7 @@
 
     // illegally matches parent:
     Query childQuery = new TermQuery(new Term("parentText", "text"));
-    Filter parentsFilter = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("isParent", "yes"))));
+    Filter parentsFilter = cache(new QueryWrapperFilter(new TermQuery(new Term("isParent", "yes"))));
     ToParentBlockJoinQuery childJoinQuery = new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Avg);
     BooleanQuery parentQuery = new BooleanQuery();
     parentQuery.add(childJoinQuery, Occur.SHOULD);
Index: lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSorting.java
===================================================================
--- lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSorting.java	(révision 1501578)
+++ lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSorting.java	(copie de travail)
@@ -214,13 +214,13 @@
     Filter childFilter = new QueryWrapperFilter(new PrefixQuery(new Term("field2")));
     ToParentBlockJoinQuery query = new ToParentBlockJoinQuery(
         new FilteredQuery(new MatchAllDocsQuery(), childFilter),
-        new CachingWrapperFilter(parentFilter),
+        cache(parentFilter),
         ScoreMode.None
     );
 
     // Sort by field ascending, order first
     ToParentBlockJoinSortField sortField = new ToParentBlockJoinSortField(
-        "field2", SortField.Type.STRING, false, wrap(parentFilter), wrap(childFilter)
+        "field2", SortField.Type.STRING, false, maybeCache(parentFilter), maybeCache(childFilter)
     );
     Sort sort = new Sort(sortField);
     TopFieldDocs topDocs = searcher.search(query, 5, sort);
@@ -239,7 +239,7 @@
 
     // Sort by field ascending, order last
     sortField = new ToParentBlockJoinSortField(
-        "field2", SortField.Type.STRING, false, true, wrap(parentFilter), wrap(childFilter)
+        "field2", SortField.Type.STRING, false, true, maybeCache(parentFilter), maybeCache(childFilter)
     );
     sort = new Sort(sortField);
     topDocs = searcher.search(query, 5, sort);
@@ -258,7 +258,7 @@
 
     // Sort by field descending, order last
     sortField = new ToParentBlockJoinSortField(
-        "field2", SortField.Type.STRING, true, wrap(parentFilter), wrap(childFilter)
+        "field2", SortField.Type.STRING, true, maybeCache(parentFilter), maybeCache(childFilter)
     );
     sort = new Sort(sortField);
     topDocs = searcher.search(query, 5, sort);
@@ -279,11 +279,11 @@
     childFilter = new QueryWrapperFilter(new TermQuery((new Term("filter_1", "T"))));
     query = new ToParentBlockJoinQuery(
         new FilteredQuery(new MatchAllDocsQuery(), childFilter),
-        new CachingWrapperFilter(parentFilter),
+        cache(parentFilter),
         ScoreMode.None
     );
     sortField = new ToParentBlockJoinSortField(
-        "field2", SortField.Type.STRING, true, wrap(parentFilter), wrap(childFilter)
+        "field2", SortField.Type.STRING, true, maybeCache(parentFilter), maybeCache(childFilter)
     );
     sort = new Sort(sortField);
     topDocs = searcher.search(query, 5, sort);
@@ -304,8 +304,4 @@
     dir.close();
   }
 
-  private Filter wrap(Filter filter) {
-    return random().nextBoolean() ? new CachingWrapperFilter(filter) : filter;
-  }
-
 }
Index: lucene/queries/src/test/org/apache/lucene/queries/ChainedFilterTest.java
===================================================================
--- lucene/queries/src/test/org/apache/lucene/queries/ChainedFilterTest.java	(révision 1501578)
+++ lucene/queries/src/test/org/apache/lucene/queries/ChainedFilterTest.java	(copie de travail)
@@ -201,11 +201,11 @@
     Query query = new TermQuery(new Term("none", "none"));
   
     QueryWrapperFilter queryFilter = new QueryWrapperFilter(query);
-    CachingWrapperFilter cachingFilter = new CachingWrapperFilter(queryFilter);
+    CachingWrapperFilter cachingFilter = cache(queryFilter);
   
     searcher.search(query, cachingFilter, 1);
   
-    CachingWrapperFilter cachingFilter2 = new CachingWrapperFilter(queryFilter);
+    CachingWrapperFilter cachingFilter2 = cache(queryFilter);
     Filter[] chain = new Filter[2];
     chain[0] = cachingFilter;
     chain[1] = cachingFilter2;
Index: lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java
===================================================================
--- lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java	(révision 1501578)
+++ lucene/grouping/src/test/org/apache/lucene/search/grouping/TestGrouping.java	(copie de travail)
@@ -778,7 +778,7 @@
         // group, so we can use single pass collector
         dirBlocks = newDirectory();
         rBlocks = getDocBlockReader(dirBlocks, groupDocs);
-        final Filter lastDocInBlock = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("groupend", "x"))));
+        final Filter lastDocInBlock = cache(new QueryWrapperFilter(new TermQuery(new Term("groupend", "x"))));
         final FieldCache.Ints docIDToIDBlocks = FieldCache.DEFAULT.getInts(new SlowCompositeReaderWrapper(rBlocks), "id", false);
 
         final IndexSearcher sBlocks = newSearcher(rBlocks);
Index: lucene/grouping/src/test/org/apache/lucene/search/grouping/GroupingSearchTest.java
===================================================================
--- lucene/grouping/src/test/org/apache/lucene/search/grouping/GroupingSearchTest.java	(révision 1501578)
+++ lucene/grouping/src/test/org/apache/lucene/search/grouping/GroupingSearchTest.java	(copie de travail)
@@ -160,7 +160,7 @@
     assertEquals(1, group.scoreDocs.length);
     assertEquals(6, group.scoreDocs[0].doc);
 
-    Filter lastDocInBlock = new CachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("groupend", "x"))));
+    Filter lastDocInBlock = cache(new QueryWrapperFilter(new TermQuery(new Term("groupend", "x"))));
     groupingSearch = new GroupingSearch(lastDocInBlock);
     groups = groupingSearch.search(indexSearcher, null, new TermQuery(new Term("content", "random")), 0, 10);
 
Index: lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java	(révision 1501578)
+++ lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java	(copie de travail)
@@ -2013,4 +2013,50 @@
     
     assertEquals(info, left, right);
   }
+
+  /** Cache the given filter. This method may disable random-access from the provided set. */
+  public static CachingWrapperFilter cache(Filter filter) {
+    return new CachingWrapperFilter(filter) {
+      @Override
+      protected DocIdSet docIdSetToCache(DocIdSet docIdSet, AtomicReader reader)
+          throws IOException {
+        final DocIdSet cachedSet = super.docIdSetToCache(docIdSet, reader);
+        if (random().nextBoolean()) {
+          // return the set as-is
+          return cachedSet;
+        }
+        // otherwise filter the doc ID set
+        final boolean randomAccess = random().nextBoolean(); // randomly disable random-access
+        return new DocIdSet() {
+
+          @Override
+          public Bits bits() throws IOException {
+            if (randomAccess) {
+              return super.bits();
+            } else {
+              return null;
+            }
+          }
+
+          @Override
+          public boolean isCacheable() {
+            return cachedSet.isCacheable();
+          }
+
+          @Override
+          public DocIdSetIterator iterator() throws IOException {
+            return cachedSet.iterator();
+          }
+        };
+      }
+    };
+  }
+
+  /** Maybe cache the given filter. */
+  public static Filter maybeCache(Filter filter) {
+    if (random().nextBoolean()) {
+      filter = cache(filter);
+    }
+    return filter;
+  }
 }
Index: lucene/CHANGES.txt
===================================================================
--- lucene/CHANGES.txt	(révision 1501578)
+++ lucene/CHANGES.txt	(copie de travail)
@@ -51,6 +51,12 @@
 * LUCENE-5084: Added new Elias-Fano encoder, decoder and DocIdSet
   implementations. (Paul Elschot via Adrien Grand)
 
+Bug fixes
+
+* LUCENE-5092: To(Parent|Child)BlockJoinQuery can now work even if the filter
+  implementation returns filters which are not FixedBitSet instances.
+  (Robert Muir, Uwe Schindler, Adrien Grand)
+
 ======================= Lucene 4.4.0 =======================
 
 Changes in backwards compatibility policy
