diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
deleted file mode 100644
index f81b943..0000000
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * 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.
- */
-package org.apache.lucene.search.join;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Queue;
-
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.Collector;
-import org.apache.lucene.search.FieldComparator;
-import org.apache.lucene.search.FieldValueHitQueue;
-import org.apache.lucene.search.LeafCollector;
-import org.apache.lucene.search.LeafFieldComparator;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreCachingWrappingScorer;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Scorer.ChildScorer;
-import org.apache.lucene.search.Sort;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.search.TopDocsCollector;
-import org.apache.lucene.search.TopFieldCollector;
-import org.apache.lucene.search.TopScoreDocCollector;
-import org.apache.lucene.search.grouping.GroupDocs;
-import org.apache.lucene.search.grouping.TopGroups;
-import org.apache.lucene.util.ArrayUtil;
-
-
-/** Collects parent document hits for a Query containing one more more
- *  BlockJoinQuery clauses, sorted by the
- *  specified parent Sort.  Note that this cannot perform
- *  arbitrary joins; rather, it requires that all joined
- *  documents are indexed as a doc block (using {@link
- *  IndexWriter#addDocuments} or {@link
- *  IndexWriter#updateDocuments}).  Ie, the join is computed
- *  at index time.
- *
- *  <p>This collector MUST be used with {@link ToParentBlockJoinIndexSearcher},
- *  in order to work correctly.
- *
- *  <p>The parent Sort must only use
- *  fields from the parent documents; sorting by field in
- *  the child documents is not supported.</p>
- *
- *  <p>You should only use this
- *  collector if one or more of the clauses in the query is
- *  a {@link ToParentBlockJoinQuery}.  This collector will find those query
- *  clauses and record the matching child documents for the
- *  top scoring parent documents.</p>
- *
- *  <p>Multiple joins (star join) and nested joins and a mix
- *  of the two are allowed, as long as in all cases the
- *  documents corresponding to a single row of each joined
- *  parent table were indexed as a doc block.</p>
- *
- *  <p>For the simple star join you can retrieve the
- *  {@link TopGroups} instance containing each {@link ToParentBlockJoinQuery}'s
- *  matching child documents for the top parent groups,
- *  using {@link #getTopGroups}.  Ie,
- *  a single query, which will contain two or more
- *  {@link ToParentBlockJoinQuery}'s as clauses representing the star join,
- *  can then retrieve two or more {@link TopGroups} instances.</p>
- *
- *  <p>For nested joins, the query will run correctly (ie,
- *  match the right parent and child documents), however,
- *  because TopGroups is currently unable to support nesting
- *  (each group is not able to hold another TopGroups), you
- *  are only able to retrieve the TopGroups of the first
- *  join.  The TopGroups of the nested joins will not be
- *  correct.
- *
- *  See {@link org.apache.lucene.search.join} for a code
- *  sample.
- *
- * @lucene.experimental
- */
-public class ToParentBlockJoinCollector implements Collector {
-
-  private final Sort sort;
-
-  // Maps each BlockJoinQuery instance to its "slot" in
-  // joinScorers and in OneGroup's cached doc/scores/count:
-  private final Map<Query,Integer> joinQueryID = new HashMap<>();
-  private final int numParentHits;
-  private final FieldValueHitQueue<OneGroup> queue;
-  private final FieldComparator<?>[] comparators;
-  private final boolean trackMaxScore;
-  private final boolean trackScores;
-
-  private ToParentBlockJoinQuery.BlockJoinScorer[] joinScorers = new ToParentBlockJoinQuery.BlockJoinScorer[0];
-  private boolean queueFull;
-
-  private OneGroup bottom;
-  private int totalHitCount;
-  private float maxScore = Float.NaN;
-
-  /**  Creates a ToParentBlockJoinCollector.  The provided sort must
-   *  not be null.  If you pass true trackScores, all
-   *  ToParentBlockQuery instances must not use
-   *  ScoreMode.None. */
-  public ToParentBlockJoinCollector(Sort sort, int numParentHits, boolean trackScores, boolean trackMaxScore) {
-    // TODO: allow null sort to be specialized to relevance
-    // only collector
-    this.sort = sort;
-    this.trackMaxScore = trackMaxScore;
-    if (trackMaxScore) {
-      maxScore = Float.MIN_VALUE;
-    }
-    //System.out.println("numParentHits=" + numParentHits);
-    this.trackScores = trackScores;
-    this.numParentHits = numParentHits;
-    queue = FieldValueHitQueue.create(sort.getSort(), numParentHits);
-    comparators = queue.getComparators();
-  }
-  
-  private static final class OneGroup extends FieldValueHitQueue.Entry {
-    public OneGroup(int comparatorSlot, int parentDoc, float parentScore, int numJoins, boolean doScores) {
-      super(comparatorSlot, parentDoc, parentScore);
-      //System.out.println("make OneGroup parentDoc=" + parentDoc);
-      docs = new int[numJoins][];
-      for(int joinID=0;joinID<numJoins;joinID++) {
-        docs[joinID] = new int[5];
-      }
-      if (doScores) {
-        scores = new float[numJoins][];
-        for(int joinID=0;joinID<numJoins;joinID++) {
-          scores[joinID] = new float[5];
-        }
-      }
-      counts = new int[numJoins];
-    }
-    LeafReaderContext readerContext;
-    int[][] docs;
-    float[][] scores;
-    int[] counts;
-  }
-
-  @Override
-  public LeafCollector getLeafCollector(final LeafReaderContext context)
-      throws IOException {
-    final LeafFieldComparator[] comparators = queue.getComparators(context);
-    final int[] reverseMul = queue.getReverseMul();
-    final int docBase = context.docBase;
-    return new LeafCollector() {
-
-      private Scorer scorer;
-
-      @Override
-      public void setScorer(Scorer scorer) throws IOException {
-        //System.out.println("C.setScorer scorer=" + scorer);
-        // Since we invoke .score(), and the comparators likely
-        // do as well, cache it so it's only "really" computed
-        // once:
-        if (scorer instanceof ScoreCachingWrappingScorer == false) {
-          scorer = new ScoreCachingWrappingScorer(scorer);
-        }
-        this.scorer = scorer;
-        for (LeafFieldComparator comparator : comparators) {
-          comparator.setScorer(scorer);
-        }
-        Arrays.fill(joinScorers, null);
-
-        Queue<Scorer> queue = new LinkedList<>();
-        //System.out.println("\nqueue: add top scorer=" + scorer);
-        queue.add(scorer);
-        while ((scorer = queue.poll()) != null) {
-          //System.out.println("  poll: " + scorer + "; " + scorer.getWeight().getQuery());
-          if (scorer instanceof ToParentBlockJoinQuery.BlockJoinScorer) {
-            enroll((ToParentBlockJoinQuery) scorer.getWeight().getQuery(), (ToParentBlockJoinQuery.BlockJoinScorer) scorer);
-          }
-
-          for (ChildScorer sub : scorer.getChildren()) {
-            //System.out.println("  add sub: " + sub.child + "; " + sub.child.getWeight().getQuery());
-            queue.add(sub.child);
-          }
-        }
-      }
-      
-      @Override
-      public void collect(int parentDoc) throws IOException {
-      //System.out.println("\nC parentDoc=" + parentDoc);
-        totalHitCount++;
-
-        float score = Float.NaN;
-
-        if (trackMaxScore) {
-          score = scorer.score();
-          maxScore = Math.max(maxScore, score);
-        }
-
-        // TODO: we could sweep all joinScorers here and
-        // aggregate total child hit count, so we can fill this
-        // in getTopGroups (we wire it to 0 now)
-
-        if (queueFull) {
-          //System.out.println("  queueFull");
-          // Fastmatch: return if this hit is not competitive
-          int c = 0;
-          for (int i = 0; i < comparators.length; ++i) {
-            c = reverseMul[i] * comparators[i].compareBottom(parentDoc);
-            if (c != 0) {
-              break;
-            }
-          }
-          if (c <= 0) { // in case of equality, this hit is not competitive as docs are visited in order
-            // Definitely not competitive.
-            //System.out.println("    skip");
-            return;
-          }
-
-          //System.out.println("    competes!  doc=" + (docBase + parentDoc));
-
-          // This hit is competitive - replace bottom element in queue & adjustTop
-          for (LeafFieldComparator comparator : comparators) {
-            comparator.copy(bottom.slot, parentDoc);
-          }
-          if (!trackMaxScore && trackScores) {
-            score = scorer.score();
-          }
-          bottom.doc = docBase + parentDoc;
-          bottom.readerContext = context;
-          bottom.score = score;
-          copyGroups(bottom);
-          bottom = queue.updateTop();
-
-          for (LeafFieldComparator comparator : comparators) {
-            comparator.setBottom(bottom.slot);
-          }
-        } else {
-          // Startup transient: queue is not yet full:
-          final int comparatorSlot = totalHitCount - 1;
-
-          // Copy hit into queue
-          for (LeafFieldComparator comparator : comparators) {
-            comparator.copy(comparatorSlot, parentDoc);
-          }
-          //System.out.println("  startup: new OG doc=" + (docBase+parentDoc));
-          if (!trackMaxScore && trackScores) {
-            score = scorer.score();
-          }
-          final OneGroup og = new OneGroup(comparatorSlot, docBase+parentDoc, score, joinScorers.length, trackScores);
-          og.readerContext = context;
-          copyGroups(og);
-          bottom = queue.add(og);
-          queueFull = totalHitCount == numParentHits;
-          if (queueFull) {
-            // End of startup transient: queue just filled up:
-            for (LeafFieldComparator comparator : comparators) {
-              comparator.setBottom(bottom.slot);
-            }
-          }
-        }
-      }
-      
-      // Pulls out child doc and scores for all join queries:
-      private void copyGroups(OneGroup og) {
-        // While rare, it's possible top arrays could be too
-        // short if join query had null scorer on first
-        // segment(s) but then became non-null on later segments
-        final int numSubScorers = joinScorers.length;
-        if (og.docs.length < numSubScorers) {
-          // While rare, this could happen if join query had
-          // null scorer on first segment(s) but then became
-          // non-null on later segments
-          og.docs = ArrayUtil.grow(og.docs, numSubScorers);
-        }
-        if (og.counts.length < numSubScorers) {
-          og.counts = ArrayUtil.grow(og.counts);
-        }
-        if (trackScores && og.scores.length < numSubScorers) {
-          og.scores = ArrayUtil.grow(og.scores, numSubScorers);
-        }
-
-        //System.out.println("\ncopyGroups parentDoc=" + og.doc);
-        for(int scorerIDX = 0;scorerIDX < numSubScorers;scorerIDX++) {
-          final ToParentBlockJoinQuery.BlockJoinScorer joinScorer = joinScorers[scorerIDX];
-          //System.out.println("  scorer=" + joinScorer);
-          if (joinScorer != null && docBase + joinScorer.getParentDoc() == og.doc) {
-            og.counts[scorerIDX] = joinScorer.getChildCount();
-            //System.out.println("    count=" + og.counts[scorerIDX]);
-            og.docs[scorerIDX] = joinScorer.swapChildDocs(og.docs[scorerIDX]);
-            assert og.docs[scorerIDX].length >= og.counts[scorerIDX]: "length=" + og.docs[scorerIDX].length + " vs count=" + og.counts[scorerIDX];
-            //System.out.println("    len=" + og.docs[scorerIDX].length);
-            /*
-              for(int idx=0;idx<og.counts[scorerIDX];idx++) {
-              System.out.println("    docs[" + idx + "]=" + og.docs[scorerIDX][idx]);
-              }
-            */
-            if (trackScores) {
-              //System.out.println("    copy scores");
-              og.scores[scorerIDX] = joinScorer.swapChildScores(og.scores[scorerIDX]);
-              assert og.scores[scorerIDX].length >= og.counts[scorerIDX]: "length=" + og.scores[scorerIDX].length + " vs count=" + og.counts[scorerIDX];
-            }
-          } else {
-            og.counts[scorerIDX] = 0;
-          }
-        }
-      }
-    };
-  }
-
-  private void enroll(ToParentBlockJoinQuery query, ToParentBlockJoinQuery.BlockJoinScorer scorer) {
-    scorer.trackPendingChildHits();
-    final Integer slot = joinQueryID.get(query);
-    if (slot == null) {
-      joinQueryID.put(query, joinScorers.length);
-      //System.out.println("found JQ: " + query + " slot=" + joinScorers.length);
-      final ToParentBlockJoinQuery.BlockJoinScorer[] newArray = new ToParentBlockJoinQuery.BlockJoinScorer[1+joinScorers.length];
-      System.arraycopy(joinScorers, 0, newArray, 0, joinScorers.length);
-      joinScorers = newArray;
-      joinScorers[joinScorers.length-1] = scorer;
-    } else {
-      joinScorers[slot] = scorer;
-    }
-  }
-
-  private OneGroup[] sortedGroups;
-
-  private void sortQueue() {
-    sortedGroups = new OneGroup[queue.size()];
-    for(int downTo=queue.size()-1;downTo>=0;downTo--) {
-      sortedGroups[downTo] = queue.pop();
-    }
-  }
-
-  /** Returns the TopGroups for the specified
-   *  BlockJoinQuery. The groupValue of each GroupDocs will
-   *  be the parent docID for that group.
-   *  The number of documents within each group is calculated as minimum of <code>maxDocsPerGroup</code>
-   *  and number of matched child documents for that group.
-   *  Returns null if no groups matched.
-   *
-   * @param query Search query
-   * @param withinGroupSort Sort criteria within groups
-   * @param offset Parent docs offset
-   * @param maxDocsPerGroup Upper bound of documents per group number
-   * @param withinGroupOffset Offset within each group of child docs
-   * @param fillSortFields Specifies whether to add sort fields or not
-   * @return TopGroups for specified query
-   * @throws IOException if there is a low-level I/O error
-   */
-  public TopGroups<Integer> getTopGroups(ToParentBlockJoinQuery query, Sort withinGroupSort, int offset,
-                                         int maxDocsPerGroup, int withinGroupOffset, boolean fillSortFields)
-    throws IOException {
-
-    final Integer _slot = joinQueryID.get(query);
-    if (_slot == null && totalHitCount == 0) {
-      return null;
-    }
-
-    if (sortedGroups == null) {
-      if (offset >= queue.size()) {
-        return null;
-      }
-      sortQueue();
-    } else if (offset > sortedGroups.length) {
-      return null;
-    }
-
-    return accumulateGroups(_slot == null ? -1 : _slot.intValue(), offset, maxDocsPerGroup, withinGroupOffset, withinGroupSort, fillSortFields);
-  }
-
-  /**
-   *  Accumulates groups for the BlockJoinQuery specified by its slot.
-   *
-   * @param slot Search query's slot
-   * @param offset Parent docs offset
-   * @param maxDocsPerGroup Upper bound of documents per group number
-   * @param withinGroupOffset Offset within each group of child docs
-   * @param withinGroupSort Sort criteria within groups
-   * @param fillSortFields Specifies whether to add sort fields or not
-   * @return TopGroups for the query specified by slot
-   * @throws IOException if there is a low-level I/O error
-   */
-  @SuppressWarnings({"unchecked","rawtypes"})
-  private TopGroups<Integer> accumulateGroups(int slot, int offset, int maxDocsPerGroup,
-                                              int withinGroupOffset, Sort withinGroupSort, boolean fillSortFields) throws IOException {
-    final GroupDocs<Integer>[] groups = new GroupDocs[sortedGroups.length - offset];
-    final FakeScorer fakeScorer = new FakeScorer();
-
-    int totalGroupedHitCount = 0;
-    //System.out.println("slot=" + slot);
-
-    for(int groupIDX=offset;groupIDX<sortedGroups.length;groupIDX++) {
-      final OneGroup og = sortedGroups[groupIDX];
-      final int numChildDocs;
-      if (slot == -1 || slot >= og.counts.length) {
-        numChildDocs = 0;
-      } else {
-        numChildDocs = og.counts[slot];
-      }
-
-      // Number of documents in group should be bounded to prevent redundant memory allocation
-      final int numDocsInGroup = Math.max(1, Math.min(numChildDocs, maxDocsPerGroup));
-      //System.out.println("parent doc=" + og.doc + " numChildDocs=" + numChildDocs + " maxDocsPG=" + maxDocsPerGroup);
-
-      // At this point we hold all docs w/ in each group,
-      // unsorted; we now sort them:
-      final TopDocsCollector<?> collector;
-      if (withinGroupSort == null) {
-        //System.out.println("sort by score");
-        // Sort by score
-        if (!trackScores) {
-          throw new IllegalArgumentException("cannot sort by relevance within group: trackScores=false");
-        }
-        collector = TopScoreDocCollector.create(numDocsInGroup);
-      } else {
-        // Sort by fields
-        collector = TopFieldCollector.create(withinGroupSort, numDocsInGroup, fillSortFields, trackScores, trackMaxScore);
-      }
-
-      LeafCollector leafCollector = collector.getLeafCollector(og.readerContext);
-      leafCollector.setScorer(fakeScorer);
-      for(int docIDX=0;docIDX<numChildDocs;docIDX++) {
-        //System.out.println("docIDX=" + docIDX + " vs " + og.docs[slot].length);
-        final int doc = og.docs[slot][docIDX];
-        fakeScorer.doc = doc;
-        if (trackScores) {
-          fakeScorer.score = og.scores[slot][docIDX];
-        }
-        leafCollector.collect(doc);
-      }
-      totalGroupedHitCount += numChildDocs;
-
-      final Object[] groupSortValues;
-
-      if (fillSortFields) {
-        groupSortValues = new Object[comparators.length];
-        for(int sortFieldIDX=0;sortFieldIDX<comparators.length;sortFieldIDX++) {
-          groupSortValues[sortFieldIDX] = comparators[sortFieldIDX].value(og.slot);
-        }
-      } else {
-        groupSortValues = null;
-      }
-
-      final TopDocs topDocs = collector.topDocs(withinGroupOffset, numDocsInGroup);
-
-      groups[groupIDX-offset] = new GroupDocs<>(og.score,
-                                                       topDocs.getMaxScore(),
-                                                       numChildDocs,
-                                                       topDocs.scoreDocs,
-                                                       og.doc,
-                                                       groupSortValues);
-    }
-
-    return new TopGroups<>(new TopGroups<>(sort.getSort(),
-                                                       withinGroupSort == null ? null : withinGroupSort.getSort(),
-                                                       0, totalGroupedHitCount, groups, maxScore),
-                                  totalHitCount);
-  }
-
-  /** Returns the TopGroups for the specified BlockJoinQuery.
-   *  The groupValue of each GroupDocs will be the parent docID for that group.
-   *  The number of documents within each group
-   *  equals to the total number of matched child documents for that group.
-   *  Returns null if no groups matched.
-   *
-   * @param query Search query
-   * @param withinGroupSort Sort criteria within groups
-   * @param offset Parent docs offset
-   * @param withinGroupOffset Offset within each group of child docs
-   * @param fillSortFields Specifies whether to add sort fields or not
-   * @return TopGroups for specified query
-   * @throws IOException if there is a low-level I/O error
-   */
-  public TopGroups<Integer> getTopGroupsWithAllChildDocs(ToParentBlockJoinQuery query, Sort withinGroupSort, int offset,
-                                                         int withinGroupOffset, boolean fillSortFields)
-    throws IOException {
-
-    return getTopGroups(query, withinGroupSort, offset, Integer.MAX_VALUE, withinGroupOffset, fillSortFields);
-  }
-  
-  /**
-   * Returns the highest score across all collected parent hits, as long as
-   * <code>trackMaxScores=true</code> was passed
-   * {@link #ToParentBlockJoinCollector(Sort, int, boolean, boolean) on
-   * construction}. Else, this returns <code>Float.NaN</code>
-   */
-  public float getMaxScore() {
-    return maxScore;
-  }
-
-  @Override
-  public boolean needsScores() {
-    // needed so that eg. BooleanQuery does not rewrite its MUST clauses to
-    // FILTER since the filter scorers are hidden in Scorer.getChildren().
-    return true;
-  }
-}
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinIndexSearcher.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinIndexSearcher.java
deleted file mode 100644
index 84a02a3..0000000
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinIndexSearcher.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.
- */
-package org.apache.lucene.search.join;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.Collector;
-import org.apache.lucene.search.DocIdSetIterator;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.LeafCollector;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.Weight;
-import org.apache.lucene.util.Bits;
-
-/**
- * An {@link IndexSearcher} to use in conjunction with
- * {@link ToParentBlockJoinCollector}.
- */
-public class ToParentBlockJoinIndexSearcher extends IndexSearcher {
-
-  /** Creates a searcher searching the provided index. Search on individual
-   *  segments will be run in the provided {@link ExecutorService}.
-   * @see IndexSearcher#IndexSearcher(IndexReader, ExecutorService) */
-  public ToParentBlockJoinIndexSearcher(IndexReader r, ExecutorService executor) {
-    super(r, executor);
-  }
-
-  /** Creates a searcher searching the provided index.
-   * @see IndexSearcher#IndexSearcher(IndexReader) */
-  public ToParentBlockJoinIndexSearcher(IndexReader r) {
-    super(r);
-  }
-
-  @Override
-  protected void search(List<LeafReaderContext> leaves, Weight weight, Collector collector) throws IOException {
-    for (LeafReaderContext ctx : leaves) { // search each subreader
-      // we force the use of Scorer (not BulkScorer) to make sure
-      // that the scorer passed to LeafCollector.setScorer supports
-      // Scorer.getChildren
-      Scorer scorer = weight.scorer(ctx);
-      if (scorer != null) {
-        final LeafCollector leafCollector = collector.getLeafCollector(ctx);
-        leafCollector.setScorer(scorer);
-        final Bits liveDocs = ctx.reader().getLiveDocs();
-        final DocIdSetIterator it = scorer.iterator();
-        for (int doc = it.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = it.nextDoc()) {
-          if (liveDocs == null || liveDocs.get(doc)) {
-            leafCollector.collect(doc);
-          }
-        }
-      }
-    }
-  }
-
-}
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
index 432ebcc..6369eea 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
@@ -30,8 +30,6 @@ import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Weight;
-import org.apache.lucene.search.grouping.TopGroups;
-import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.BitSet;
 
 /**
@@ -57,20 +55,6 @@ import org.apache.lucene.util.BitSet;
  * documents: the wrapped child query must never
  * return a parent document.</p>
  *
- * If you'd like to retrieve {@link TopGroups} for the
- * resulting query, use the {@link ToParentBlockJoinCollector}.
- * Note that this is not necessary, ie, if you simply want
- * to collect the parent documents and don't need to see
- * which child documents matched under that parent, then
- * you can use any collector.
- *
- * <p><b>NOTE</b>: If the overall query contains parent-only
- * matches, for example you OR a parent-only query with a
- * joined child-only query, then the resulting collected documents
- * will be correct, however the {@link TopGroups} you get
- * from {@link ToParentBlockJoinCollector} will not contain every
- * child for parents that had matched.
- *
  * <p>See {@link org.apache.lucene.search.join} for an
  * overview. </p>
  *
@@ -171,39 +155,7 @@ public class ToParentBlockJoinQuery extends Query {
     }
   }
   
-  /** 
-   * Ascendant for {@link ToParentBlockJoinQuery}'s scorer. 
-   * @lucene.experimental it might be removed at <b>6.0</b>
-   * */
-  public static abstract class ChildrenMatchesScorer extends Scorer{
-    
-    /** inherited constructor */
-    protected ChildrenMatchesScorer(Weight weight) {
-      super(weight);
-    }
-    
-    /** 
-     * enables children matches recording 
-     * */
-    public abstract void trackPendingChildHits() ;
-    
-    /**
-     * reports matched children 
-     * @return number of recorded matched children docs 
-     * */
-    public abstract int getChildCount() ;
-    
-    /**
-     * reports matched children 
-     * @param other array for recording matching children docs of next parent,
-     * it might be null (that's slower) or the same array which was returned 
-     * from the previous call
-     * @return array with {@link #getChildCount()} matched children docnums
-     *  */
-    public abstract int[] swapChildDocs(int[] other);
-  }
-  
-  static class BlockJoinScorer extends ChildrenMatchesScorer {
+  static class BlockJoinScorer extends Scorer {
     private final Scorer childScorer;
     private final BitSet parentBits;
     private final ScoreMode scoreMode;
@@ -212,8 +164,6 @@ public class ToParentBlockJoinQuery extends Query {
     private float parentScore;
     private int parentFreq;
     private int nextChildDoc;
-    private int[] pendingChildDocs;
-    private float[] pendingChildScores;
     private int childDocUpto;
 
     public BlockJoinScorer(Weight weight, Scorer childScorer, BitSet parentBits, int firstChildDoc, ScoreMode scoreMode) {
@@ -231,39 +181,6 @@ public class ToParentBlockJoinQuery extends Query {
     }
 
     @Override
-    public int getChildCount() {
-      return childDocUpto;
-    }
-
-    int getParentDoc() {
-      return parentDoc;
-    }
-
-    @Override
-    public int[] swapChildDocs(int[] other) {
-      final int[] ret = pendingChildDocs;
-      if (other == null) {
-        pendingChildDocs = new int[5];
-      } else {
-        pendingChildDocs = other;
-      }
-      return ret;
-    }
-
-    float[] swapChildScores(float[] other) {
-      if (scoreMode == ScoreMode.None) {
-        throw new IllegalStateException("ScoreMode is None; you must pass trackScores=false to ToParentBlockJoinCollector");
-      }
-      final float[] ret = pendingChildScores;
-      if (other == null) {
-        pendingChildScores = new float[5];
-      } else {
-        pendingChildScores = other;
-      }
-      return ret;
-    }
-
-    @Override
     public DocIdSetIterator iterator() {
       return new DocIdSetIterator() {
         final DocIdSetIterator childIt = childScorer.iterator();
@@ -297,22 +214,10 @@ public class ToParentBlockJoinQuery extends Query {
           do {
 
             //System.out.println("  c=" + nextChildDoc);
-            if (pendingChildDocs != null && pendingChildDocs.length == childDocUpto) {
-              pendingChildDocs = ArrayUtil.grow(pendingChildDocs);
-            }
-            if (pendingChildScores != null && scoreMode != ScoreMode.None && pendingChildScores.length == childDocUpto) {
-              pendingChildScores = ArrayUtil.grow(pendingChildScores);
-            }
-            if (pendingChildDocs != null) {
-              pendingChildDocs[childDocUpto] = nextChildDoc;
-            }
             if (scoreMode != ScoreMode.None) {
               // TODO: specialize this into dedicated classes per-scoreMode
               final float childScore = childScorer.score();
               final int childFreq = childScorer.freq();
-              if (pendingChildScores != null) {
-                pendingChildScores[childDocUpto] = childScore;
-              }
               maxScore = Math.max(childScore, maxScore);
               minScore = Math.min(childScore, minScore);
               totalScore += childScore;
@@ -440,17 +345,6 @@ public class ToParentBlockJoinQuery extends Query {
           "Score based on %d child docs in range from %d to %d, best match:", matches, start, end), bestChild
       );
     }
-
-    /**
-     * Instructs this scorer to keep track of the child docIds and score ids for retrieval purposes.
-     */
-    @Override
-    public void trackPendingChildHits() {
-      pendingChildDocs = new int[5];
-      if (scoreMode != ScoreMode.None) {
-        pendingChildScores = new float[5];
-      }
-    }
   }
 
   @Override
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/package-info.java b/lucene/join/src/java/org/apache/lucene/search/join/package-info.java
index 6133f99..0f6de2e 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/package-info.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/package-info.java
@@ -40,15 +40,7 @@
  *   space.  The
  *   resulting query can then be used as a clause in any query that
  *   matches parent.</p>
- * 
- * <p>If you only care about the parent documents matching the query, you
- *   can use any collector to collect the parent hits, but if you'd also
- *   like to see which child documents match for each parent document,
- *   use the {@link org.apache.lucene.search.join.ToParentBlockJoinCollector} to collect the hits. Once the
- *   search is done, you retrieve a {@link
- *   org.apache.lucene.search.grouping.TopGroups} instance from the
- *   {@link org.apache.lucene.search.join.ToParentBlockJoinCollector#getTopGroups ToParentBlockJoinCollector.getTopGroups()} method.</p>
- * 
+ *
  * <p>To map/join in the opposite direction, use {@link
  *   org.apache.lucene.search.join.ToChildBlockJoinQuery}.  This wraps
  *   any query matching parent documents, creating the joined query
diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
index cf21fa4..7dedba1 100644
--- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
+++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
@@ -69,8 +69,6 @@ import org.apache.lucene.search.SortField;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.search.TopDocs;
 import org.apache.lucene.search.Weight;
-import org.apache.lucene.search.grouping.GroupDocs;
-import org.apache.lucene.search.grouping.TopGroups;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.BitSet;
 import org.apache.lucene.util.Bits;
@@ -170,18 +168,10 @@ public class TestBlockJoin extends LuceneTestCase {
     BooleanQuery.Builder fullQuery = new BooleanQuery.Builder();
     fullQuery.add(new BooleanClause(childJoinQuery, Occur.MUST));
     fullQuery.add(new BooleanClause(new MatchAllDocsQuery(), Occur.MUST));
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(Sort.RELEVANCE, 1, true, true);
-    s.search(fullQuery.build(), c);
-    TopGroups<Integer> results = c.getTopGroups(childJoinQuery, null, 0, 10, 0, true);
-    assertFalse(Float.isNaN(results.maxScore));
-    assertEquals(1, results.totalGroupedHitCount);
-    assertEquals(1, results.groups.length);
-    final GroupDocs<Integer> group = results.groups[0];
-    Document childDoc = s.doc(group.scoreDocs[0].doc);
-    assertEquals("java", childDoc.get("skill"));
-    assertNotNull(group.groupValue);
-    Document parentDoc = s.doc(group.groupValue);
-    assertEquals("Lisa", parentDoc.get("name"));
+    TopDocs topDocs = s.search(fullQuery.build(), 2);
+    assertEquals(2, topDocs.totalHits);
+    assertEquals(asSet("Lisa", "Frank"),
+        asSet(s.doc(topDocs.scoreDocs[0].doc).get("name"), s.doc(topDocs.scoreDocs[1].doc).get("name")));
 
     r.close();
     dir.close();
@@ -207,8 +197,7 @@ public class TestBlockJoin extends LuceneTestCase {
 
     IndexReader r = w.getReader();
     w.close();
-    IndexSearcher s = new ToParentBlockJoinIndexSearcher(r);
-    //IndexSearcher s = newSearcher(r, false);
+    IndexSearcher s = newSearcher(r, false);
     //IndexSearcher s = new IndexSearcher(r);
 
     // Create a filter that defines "parent" documents in the index - in this case resumes
@@ -232,23 +221,10 @@ public class TestBlockJoin extends LuceneTestCase {
     fullQuery.add(new BooleanClause(parentQuery, Occur.SHOULD));
     fullQuery.add(new BooleanClause(childJoinQuery, Occur.SHOULD));
 
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(Sort.RELEVANCE, 1, true, true);
-    s.search(fullQuery.build(), c);
-    TopGroups<Integer> results = c.getTopGroups(childJoinQuery, null, 0, 10, 0, true);
-    assertEquals(1, results.totalGroupedHitCount);
-    assertEquals(1, results.groups.length);
-
-    final GroupDocs<Integer> group = results.groups[0];
-    assertEquals(1, group.totalHits);
-    assertFalse(Float.isNaN(group.score));
-
-    Document childDoc = s.doc(group.scoreDocs[0].doc);
-    //System.out.println("  doc=" + group.scoreDocs[0].doc);
-    assertEquals("java", childDoc.get("skill"));
-    assertNotNull(group.groupValue);
-    Document parentDoc = s.doc(group.groupValue);
-    assertEquals("Lisa", parentDoc.get("name"));
-    
+    final TopDocs topDocs = s.search(fullQuery.build(), 2);
+    assertEquals(2, topDocs.totalHits);
+    assertEquals(asSet("Lisa", "Frank"),
+        asSet(s.doc(topDocs.scoreDocs[0].doc).get("name"), s.doc(topDocs.scoreDocs[1].doc).get("name")));
     
     r.close();
     dir.close();
@@ -297,28 +273,13 @@ public class TestBlockJoin extends LuceneTestCase {
     fullQuery.add(new BooleanClause(parentQuery, Occur.MUST));
     fullQuery.add(new BooleanClause(childJoinQuery, Occur.MUST));
 
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(Sort.RELEVANCE, 1, true, true);
-
     CheckHits.checkHitCollector(random(), fullQuery.build(), "country", s, new int[] {2});
 
-    s.search(fullQuery.build(), c);
-
-    TopGroups<Integer> results = c.getTopGroups(childJoinQuery, null, 0, 10, 0, true);
-    assertFalse(Float.isNaN(results.maxScore));
+    TopDocs topDocs = s.search(fullQuery.build(), 1);
 
     //assertEquals(1, results.totalHitCount);
-    assertEquals(1, results.totalGroupedHitCount);
-    assertEquals(1, results.groups.length);
-
-    final GroupDocs<Integer> group = results.groups[0];
-    assertEquals(1, group.totalHits);
-    assertFalse(Float.isNaN(group.score));
-
-    Document childDoc = s.doc(group.scoreDocs[0].doc);
-    //System.out.println("  doc=" + group.scoreDocs[0].doc);
-    assertEquals("java", childDoc.get("skill"));
-    assertNotNull(group.groupValue);
-    Document parentDoc = s.doc(group.groupValue);
+    assertEquals(1, topDocs.totalHits);
+    Document parentDoc = s.doc(topDocs.scoreDocs[0].doc);
     assertEquals("Lisa", parentDoc.get("name"));
 
 
@@ -333,7 +294,7 @@ public class TestBlockJoin extends LuceneTestCase {
     //System.out.println("FULL: " + fullChildQuery);
     TopDocs hits = s.search(fullChildQuery.build(), 10);
     assertEquals(1, hits.totalHits);
-    childDoc = s.doc(hits.scoreDocs[0].doc);
+    Document childDoc = s.doc(hits.scoreDocs[0].doc);
     //System.out.println("CHILD = " + childDoc + " docID=" + hits.scoreDocs[0].doc);
     assertEquals("java", childDoc.get("skill"));
     assertEquals(2007, childDoc.getField("year").numericValue());
@@ -347,72 +308,6 @@ public class TestBlockJoin extends LuceneTestCase {
     dir.close();
   }
 
-  public void testBugCausedByRewritingTwice() throws IOException {
-    final Directory dir = newDirectory();
-    final RandomIndexWriter w = new RandomIndexWriter(random(), dir);
-
-    final List<Document> docs = new ArrayList<>();
-
-    for (int i=0;i<10;i++) {
-      docs.clear();
-      docs.add(makeJob("ruby", i));
-      docs.add(makeJob("java", 2007));
-      docs.add(makeResume("Frank", "United States"));
-      w.addDocuments(docs);
-    }
-
-    IndexReader r = w.getReader();
-    w.close();
-    IndexSearcher s = newSearcher(r, false);
-
-    // Hacky: this causes the query to need 2 rewrite
-    // iterations:
-    BooleanQuery.Builder builder = new BooleanQuery.Builder();
-    builder.add(IntPoint.newExactQuery("year", 2007), BooleanClause.Occur.MUST);
-    Query qc = new Query() {
-      @Override
-      public Query rewrite(IndexReader reader) throws IOException {
-        return builder.build();
-      }
-
-      @Override
-      public String toString(String field) {
-        return "hack!";
-      }
-
-      @Override
-      public boolean equals(Object o) {
-        return o == this;
-      }
-
-      @Override
-      public int hashCode() {
-        return System.identityHashCode(this);
-      }
-    };
-
-    BitSetProducer parentsFilter = new QueryBitSetProducer(new TermQuery(new Term("docType", "resume")));
-    CheckJoinIndex.check(r, parentsFilter);
-
-    Query qw1 = qc.rewrite(r);
-    Query qw2 = qw1.rewrite(r);
-
-    assertNotSame(qc, qw1);
-    assertNotSame(qw1, qw2);
-
-    ToParentBlockJoinQuery qp = new ToParentBlockJoinQuery(qc, parentsFilter, ScoreMode.Max);
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(Sort.RELEVANCE, 10, true, true);
-
-    s.search(qp, c);
-    TopGroups<Integer> groups = c.getTopGroups(qp, Sort.INDEXORDER, 0, 10, 0, true);
-    for (GroupDocs<Integer> group : groups.groups) {
-      assertEquals(1, group.totalHits);
-    }
-
-    r.close();
-    dir.close();
-  }
-
   protected Query skill(String skill) {
     return new TermQuery(new Term("skill", skill));
   }
@@ -752,7 +647,7 @@ public class TestBlockJoin extends LuceneTestCase {
 
     final IndexSearcher s = newSearcher(r, false);
 
-    final IndexSearcher joinS = new IndexSearcher(joinR);
+    final IndexSearcher joinS = newSearcher(joinR);
 
     final BitSetProducer parentsFilter = new QueryBitSetProducer(new TermQuery(new Term("isParent", "x")));
     CheckJoinIndex.check(joinS.getIndexReader(), parentsFilter);
@@ -880,57 +775,7 @@ public class TestBlockJoin extends LuceneTestCase {
         }
       }
 
-      final boolean trackScores;
-      final boolean trackMaxScore;
-      if (agg == ScoreMode.None) {
-        trackScores = false;
-        trackMaxScore = false;
-      } else {
-        trackScores = random().nextBoolean();
-        trackMaxScore = random().nextBoolean();
-      }
-      final ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(parentSort, 10, trackScores, trackMaxScore);
-
-      joinS.search(parentJoinQuery, c);
-
-      final int hitsPerGroup = TestUtil.nextInt(random(), 1, 20);
-      //final int hitsPerGroup = 100;
-      final TopGroups<Integer> joinResults = c.getTopGroups(childJoinQuery, childSort, 0, hitsPerGroup, 0, true);
-
-      if (VERBOSE) {
-        System.out.println("\nTEST: block join index gets " + (joinResults == null ? 0 : joinResults.groups.length) + " groups; hitsPerGroup=" + hitsPerGroup);
-        if (joinResults != null) {
-          final GroupDocs<Integer>[] groups = joinResults.groups;
-          for(int groupIDX=0;groupIDX<groups.length;groupIDX++) {
-            final GroupDocs<Integer> group = groups[groupIDX];
-            if (group.groupSortValues != null) {
-              System.out.print("  ");
-              for(Object o : group.groupSortValues) {
-                if (o instanceof BytesRef) {
-                  System.out.print(((BytesRef) o).utf8ToString() + " ");
-                } else {
-                  System.out.print(o + " ");
-                }
-              }
-              System.out.println();
-            }
-
-            assertNotNull(group.groupValue);
-            final Document parentDoc = joinS.doc(group.groupValue);
-            System.out.println("  group parentID=" + parentDoc.get("parentID") + " (docID=" + group.groupValue + ")");
-            for(int hitIDX=0;hitIDX<group.scoreDocs.length;hitIDX++) {
-              final Document doc = joinS.doc(group.scoreDocs[hitIDX].doc);
-              //System.out.println("    score=" + group.scoreDocs[hitIDX].score + " childID=" + doc.get("childID") + " (docID=" + group.scoreDocs[hitIDX].doc + ")");
-              System.out.println("    childID=" + doc.get("childID") + " child0=" + doc.get("child0") + " (docID=" + group.scoreDocs[hitIDX].doc + ")");
-            }
-          }
-        }
-      }
-
-      if (results.totalHits == 0) {
-        assertNull(joinResults);
-      } else {
-        compareHits(r, joinR, results, joinResults);
+      if (results.totalHits != 0) {
         TopDocs b = joinS.search(childJoinQuery, 10);
         for (ScoreDoc hit : b.scoreDocs) {
           Explanation explanation = joinS.explain(childJoinQuery, hit.doc);
@@ -1115,46 +960,6 @@ public class TestBlockJoin extends LuceneTestCase {
     }
   }
 
-  private void compareHits(IndexReader r, IndexReader joinR, TopDocs results, TopGroups<Integer> joinResults) throws Exception {
-    // results is 'complete'; joinResults is a subset
-    int resultUpto = 0;
-    int joinGroupUpto = 0;
-
-    final ScoreDoc[] hits = results.scoreDocs;
-    final GroupDocs<Integer>[] groupDocs = joinResults.groups;
-
-    while(joinGroupUpto < groupDocs.length) {
-      final GroupDocs<Integer> group = groupDocs[joinGroupUpto++];
-      final ScoreDoc[] groupHits = group.scoreDocs;
-      assertNotNull(group.groupValue);
-      final Document parentDoc = joinR.document(group.groupValue);
-      final String parentID = parentDoc.get("parentID");
-      //System.out.println("GROUP groupDoc=" + group.groupDoc + " parent=" + parentDoc);
-      assertNotNull(parentID);
-      assertTrue(groupHits.length > 0);
-      for(int hitIDX=0;hitIDX<groupHits.length;hitIDX++) {
-        final Document nonJoinHit = r.document(hits[resultUpto++].doc);
-        final Document joinHit = joinR.document(groupHits[hitIDX].doc);
-        assertEquals(parentID,
-                     nonJoinHit.get("parentID"));
-        assertEquals(joinHit.get("childID"),
-                     nonJoinHit.get("childID"));
-      }
-
-      if (joinGroupUpto < groupDocs.length) {
-        // Advance non-join hit to the next parentID:
-        //System.out.println("  next joingroupUpto=" + joinGroupUpto + " gd.length=" + groupDocs.length + " parentID=" + parentID);
-        while(true) {
-          assertTrue(resultUpto < hits.length);
-          if (!parentID.equals(r.document(hits[resultUpto].doc).get("parentID"))) {
-            break;
-          }
-          resultUpto++;
-        }
-      }
-    }
-  }
-
   public void testMultiChildTypes() throws Exception {
 
     final Directory dir = newDirectory();
@@ -1200,42 +1005,9 @@ public class TestBlockJoin extends LuceneTestCase {
     fullQuery.add(new BooleanClause(childJobJoinQuery, Occur.MUST));
     fullQuery.add(new BooleanClause(childQualificationJoinQuery, Occur.MUST));
 
-    // Collects all job and qualification child docs for
-    // each resume hit in the top N (sorted by score):
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(Sort.RELEVANCE, 10, true, false);
-
-    s.search(fullQuery.build(), c);
-
-    // Examine "Job" children
-    TopGroups<Integer> jobResults = c.getTopGroups(childJobJoinQuery, null, 0, 10, 0, true);
-
-    //assertEquals(1, results.totalHitCount);
-    assertEquals(1, jobResults.totalGroupedHitCount);
-    assertEquals(1, jobResults.groups.length);
-
-    final GroupDocs<Integer> group = jobResults.groups[0];
-    assertEquals(1, group.totalHits);
-
-    Document childJobDoc = s.doc(group.scoreDocs[0].doc);
-    //System.out.println("  doc=" + group.scoreDocs[0].doc);
-    assertEquals("java", childJobDoc.get("skill"));
-    assertNotNull(group.groupValue);
-    Document parentDoc = s.doc(group.groupValue);
-    assertEquals("Lisa", parentDoc.get("name"));
-
-    // Now Examine qualification children
-    TopGroups<Integer> qualificationResults = c.getTopGroups(childQualificationJoinQuery, null, 0, 10, 0, true);
-
-    assertEquals(1, qualificationResults.totalGroupedHitCount);
-    assertEquals(1, qualificationResults.groups.length);
-
-    final GroupDocs<Integer> qGroup = qualificationResults.groups[0];
-    assertEquals(1, qGroup.totalHits);
-
-    Document childQualificationDoc = s.doc(qGroup.scoreDocs[0].doc);
-    assertEquals("maths", childQualificationDoc.get("qualification"));
-    assertNotNull(qGroup.groupValue);
-    parentDoc = s.doc(qGroup.groupValue);
+    final TopDocs topDocs = s.search(fullQuery.build(), 10);
+    assertEquals(1, topDocs.totalHits);
+    Document parentDoc = s.doc(topDocs.scoreDocs[0].doc);
     assertEquals("Lisa", parentDoc.get("name"));
 
     r.close();
@@ -1300,165 +1072,6 @@ public class TestBlockJoin extends LuceneTestCase {
     dir.close();
   }
 
-  public void testGetTopGroups() throws Exception {
-
-    final Directory dir = newDirectory();
-    final RandomIndexWriter w = new RandomIndexWriter(random(), dir);
-
-    final List<Document> docs = new ArrayList<>();
-    docs.add(makeJob("ruby", 2005));
-    docs.add(makeJob("java", 2006));
-    docs.add(makeJob("java", 2010));
-    docs.add(makeJob("java", 2012));
-    Collections.shuffle(docs, random());
-    docs.add(makeResume("Frank", "United States"));
-
-    addSkillless(w);
-    w.addDocuments(docs);
-    addSkillless(w);
-
-    IndexReader r = w.getReader();
-    w.close();
-    IndexSearcher s = new IndexSearcher(r);
-
-    // Create a filter that defines "parent" documents in the index - in this case resumes
-    BitSetProducer parentsFilter = new QueryBitSetProducer(new TermQuery(new Term("docType", "resume")));
-    CheckJoinIndex.check(s.getIndexReader(), parentsFilter);
-
-    // Define child document criteria (finds an example of relevant work experience)
-    BooleanQuery.Builder childQuery = new BooleanQuery.Builder();
-    childQuery.add(new BooleanClause(new TermQuery(new Term("skill", "java")), Occur.MUST));
-    childQuery.add(new BooleanClause(IntPoint.newRangeQuery("year", 2006, 2011), Occur.MUST));
-
-    // Wrap the child document query to 'join' any matches
-    // up to corresponding parent:
-    ToParentBlockJoinQuery childJoinQuery = new ToParentBlockJoinQuery(childQuery.build(), parentsFilter, ScoreMode.Avg);
-
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(Sort.RELEVANCE, 2, true, true);
-    s.search(childJoinQuery, c);
-
-    //Get all child documents within groups
-    @SuppressWarnings({"unchecked","rawtypes"})
-    TopGroups<Integer>[] getTopGroupsResults = new TopGroups[2];
-    getTopGroupsResults[0] = c.getTopGroups(childJoinQuery, null, 0, 10, 0, true);
-    getTopGroupsResults[1] = c.getTopGroupsWithAllChildDocs(childJoinQuery, null, 0, 0, true);
-
-    for (TopGroups<Integer> results : getTopGroupsResults) {
-      assertFalse(Float.isNaN(results.maxScore));
-      assertEquals(2, results.totalGroupedHitCount);
-      assertEquals(1, results.groups.length);
-
-      final GroupDocs<Integer> group = results.groups[0];
-      assertEquals(2, group.totalHits);
-      assertFalse(Float.isNaN(group.score));
-      assertNotNull(group.groupValue);
-      Document parentDoc = s.doc(group.groupValue);
-      assertEquals("Frank", parentDoc.get("name"));
-
-      assertEquals(2, group.scoreDocs.length); //all matched child documents collected
-
-      for (ScoreDoc scoreDoc : group.scoreDocs) {
-        Document childDoc = s.doc(scoreDoc.doc);
-        assertEquals("java", childDoc.get("skill"));
-        int year = Integer.parseInt(childDoc.get("year"));
-        assertTrue(year >= 2006 && year <= 2011);
-      }
-    }
-
-    //Get part of child documents
-    TopGroups<Integer> boundedResults = c.getTopGroups(childJoinQuery, null, 0, 1, 0, true);
-    assertFalse(Float.isNaN(boundedResults.maxScore));
-    assertEquals(2, boundedResults.totalGroupedHitCount);
-    assertEquals(1, boundedResults.groups.length);
-
-    final GroupDocs<Integer> group = boundedResults.groups[0];
-    assertEquals(2, group.totalHits);
-    assertFalse(Float.isNaN(group.score));
-    assertNotNull(group.groupValue);
-    Document parentDoc = s.doc(group.groupValue);
-    assertEquals("Frank", parentDoc.get("name"));
-
-    assertEquals(1, group.scoreDocs.length); //not all matched child documents collected
-
-    for (ScoreDoc scoreDoc : group.scoreDocs) {
-      Document childDoc = s.doc(scoreDoc.doc);
-      assertEquals("java", childDoc.get("skill"));
-      int year = Integer.parseInt(childDoc.get("year"));
-      assertTrue(year >= 2006 && year <= 2011);
-    }
-
-    r.close();
-    dir.close();
-  }
-
-  // LUCENE-4968
-  public void testSometimesParentOnlyMatches() throws Exception {
-    Directory d = newDirectory();
-    RandomIndexWriter w = new RandomIndexWriter(random(), d);
-    Document parent = new Document();
-    parent.add(new StoredField("parentID", "0"));
-    parent.add(new SortedDocValuesField("parentID", new BytesRef("0")));
-    parent.add(newTextField("parentText", "text", Field.Store.NO));
-    parent.add(newStringField("isParent", "yes", Field.Store.NO));
-
-    List<Document> docs = new ArrayList<>();
-
-    Document child = new Document();
-    docs.add(child);
-    child.add(new StoredField("childID", "0"));
-    child.add(newTextField("childText", "text", Field.Store.NO));
-
-    // parent last:
-    docs.add(parent);
-    w.addDocuments(docs);
-
-    docs.clear();
-
-    parent = new Document();
-    parent.add(newTextField("parentText", "text", Field.Store.NO));
-    parent.add(newStringField("isParent", "yes", Field.Store.NO));
-    parent.add(new StoredField("parentID", "1"));
-    parent.add(new SortedDocValuesField("parentID", new BytesRef("1")));
-
-    // parent last:
-    docs.add(parent);
-    w.addDocuments(docs);
-
-    IndexReader r = w.getReader();
-    w.close();
-
-    IndexSearcher searcher = new ToParentBlockJoinIndexSearcher(r);
-    Query childQuery = new TermQuery(new Term("childText", "text"));
-    BitSetProducer parentsFilter = new QueryBitSetProducer(new TermQuery(new Term("isParent", "yes")));
-    CheckJoinIndex.check(r, parentsFilter);
-    ToParentBlockJoinQuery childJoinQuery = new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Avg);
-    BooleanQuery.Builder parentQuery = new BooleanQuery.Builder();
-    parentQuery.add(childJoinQuery, Occur.SHOULD);
-    parentQuery.add(new TermQuery(new Term("parentText", "text")), Occur.SHOULD);
-
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(new Sort(new SortField("parentID", SortField.Type.STRING)),
-                                                                  10, true, true);
-    searcher.search(parentQuery.build(), c);
-    TopGroups<Integer> groups = c.getTopGroups(childJoinQuery, null, 0, 10, 0, false);
-
-    // Two parents:
-    assertEquals(2, groups.totalGroupCount.intValue());
-
-    // One child docs:
-    assertEquals(1, groups.totalGroupedHitCount);
-
-    GroupDocs<Integer> group = groups.groups[0];
-    Document doc = r.document(group.groupValue.intValue());
-    assertEquals("0", doc.get("parentID"));
-
-    group = groups.groups[1];
-    doc = r.document(group.groupValue.intValue());
-    assertEquals("1", doc.get("parentID"));
-
-    r.close();
-    d.close();
-  }
-
   // LUCENE-4968
   public void testChildQueryNeverMatches() throws Exception {
     Directory d = newDirectory();
@@ -1496,90 +1109,25 @@ public class TestBlockJoin extends LuceneTestCase {
     IndexReader r = w.getReader();
     w.close();
 
-    IndexSearcher searcher = new ToParentBlockJoinIndexSearcher(r);
+    IndexSearcher searcher = newSearcher(r);
 
     // never matches:
     Query childQuery = new TermQuery(new Term("childText", "bogus"));
     BitSetProducer parentsFilter = new QueryBitSetProducer(new TermQuery(new Term("isParent", "yes")));
     CheckJoinIndex.check(r, parentsFilter);
     ToParentBlockJoinQuery childJoinQuery = new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Avg);
-    BooleanQuery.Builder parentQuery = new BooleanQuery.Builder();
-    parentQuery.add(childJoinQuery, Occur.SHOULD);
-    parentQuery.add(new TermQuery(new Term("parentText", "text")), Occur.SHOULD);
-
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(new Sort(new SortField("parentID", SortField.Type.STRING)),
-                                                                  10, true, true);
-    searcher.search(parentQuery.build(), c);
-    TopGroups<Integer> groups = c.getTopGroups(childJoinQuery, null, 0, 10, 0, false);
-
-    // Two parents:
-    assertEquals(2, groups.totalGroupCount.intValue());
-
-    // One child docs:
-    assertEquals(0, groups.totalGroupedHitCount);
-
-    GroupDocs<Integer> group = groups.groups[0];
-    Document doc = r.document(group.groupValue.intValue());
-    assertEquals("0", doc.get("parentID"));
-
-    group = groups.groups[1];
-    doc = r.document(group.groupValue.intValue());
-    assertEquals("1", doc.get("parentID"));
-
-    r.close();
-    d.close();
-  }
-
-  // LUCENE-4968
-  public void testChildQueryMatchesParent() throws Exception {
-    Directory d = newDirectory();
-    RandomIndexWriter w = new RandomIndexWriter(random(), d);
-    Document parent = new Document();
-    parent.add(new StoredField("parentID", "0"));
-    parent.add(newTextField("parentText", "text", Field.Store.NO));
-    parent.add(newStringField("isParent", "yes", Field.Store.NO));
-
-    List<Document> docs = new ArrayList<>();
-
-    Document child = new Document();
-    docs.add(child);
-    child.add(new StoredField("childID", "0"));
-    child.add(newTextField("childText", "text", Field.Store.NO));
-
-    // parent last:
-    docs.add(parent);
-    w.addDocuments(docs);
-
-    docs.clear();
-
-    parent = new Document();
-    parent.add(newTextField("parentText", "text", Field.Store.NO));
-    parent.add(newStringField("isParent", "yes", Field.Store.NO));
-    parent.add(new StoredField("parentID", "1"));
-
-    // parent last:
-    docs.add(parent);
-    w.addDocuments(docs);
-
-    IndexReader r = w.getReader();
-    w.close();
-
-    // illegally matches parent:
-    Query childQuery = new TermQuery(new Term("parentText", "text"));
-    BitSetProducer parentsFilter = new QueryBitSetProducer(new TermQuery(new Term("isParent", "yes")));
-    CheckJoinIndex.check(r, parentsFilter);
-    ToParentBlockJoinQuery childJoinQuery = new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Avg);
-    BooleanQuery.Builder parentQuery = new BooleanQuery.Builder();
-    parentQuery.add(childJoinQuery, Occur.SHOULD);
-    parentQuery.add(new TermQuery(new Term("parentText", "text")), Occur.SHOULD);
 
-    ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(new Sort(new SortField("parentID", SortField.Type.STRING)),
-                                                                  10, true, true);
+    Weight weight = searcher.createNormalizedWeight(childJoinQuery, random().nextBoolean());
+    Scorer scorer = weight.scorer(searcher.getIndexReader().leaves().get(0));
+    assertNull(scorer);
 
-    expectThrows(IllegalStateException.class, () -> {
-      newSearcher(r).search(parentQuery.build(), c);
-    });
+    // never matches and produces a null scorer
+    childQuery = new TermQuery(new Term("bogus", "bogus"));
+    childJoinQuery = new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Avg);
 
+    weight = searcher.createNormalizedWeight(childJoinQuery, random().nextBoolean());
+    scorer = weight.scorer(searcher.getIndexReader().leaves().get(0));
+    assertNull(scorer);
 
     r.close();
     d.close();
diff --git a/solr/core/src/java/org/apache/solr/search/join/BlockJoinFacetAccsHolder.java b/solr/core/src/java/org/apache/solr/search/join/BlockJoinFacetAccsHolder.java
index 00b3ac4..769a9c8 100644
--- a/solr/core/src/java/org/apache/solr/search/join/BlockJoinFacetAccsHolder.java
+++ b/solr/core/src/java/org/apache/solr/search/join/BlockJoinFacetAccsHolder.java
@@ -19,11 +19,9 @@ package org.apache.solr.search.join;
 import java.io.IOException;
 
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.join.ToParentBlockJoinQuery.ChildrenMatchesScorer;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.search.join.BlockJoinFieldFacetAccumulator.AggregatableDocIter;
-import org.apache.solr.search.join.BlockJoinFieldFacetAccumulator.SortedIntsAggDocIterator;
 
 /**
  * For each collected parent document creates matched block, which is a docSet with matched children and parent doc
@@ -32,8 +30,6 @@ import org.apache.solr.search.join.BlockJoinFieldFacetAccumulator.SortedIntsAggD
 class BlockJoinFacetAccsHolder {
   private BlockJoinFieldFacetAccumulator[] blockJoinFieldFacetAccumulators;
   private boolean firstSegment = true;
-  private ChildrenMatchesScorer blockJoinScorer;
-  private int[] childDocs = new int[0];
   
   BlockJoinFacetAccsHolder(SolrQueryRequest req) throws IOException {
     String[] facetFieldNames = BlockJoinFacetComponentSupport.getChildFacetFields(req);
@@ -61,16 +57,6 @@ class BlockJoinFacetAccsHolder {
     }
   }
 
-  protected void incrementFacets(int parent) throws IOException {
-    final int[] docNums = blockJoinScorer.swapChildDocs(childDocs);
-    // now we don't
-    //includeParentDoc(parent);
-    //final int childCountPlusParent = childTracking.getChildCount()+1;
-    final int childCountNoParent = blockJoinScorer.getChildCount();
-    final SortedIntsAggDocIterator iter = new SortedIntsAggDocIterator(docNums, childCountNoParent, parent);
-    countFacets(iter);
-  }
-
   /** is not used 
   protected int[] includeParentDoc(int parent) {
     final int[] docNums = ArrayUtil.grow(childTracking.getChildDocs(), childTracking.getChildCount()+1);
