// 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);
    }
    if (og.counts.length < numSubScorers) {
      og.counts = ArrayUtil.grow(og.counts);
    }
    if (trackScores && og.scores.length < numSubScorers) {
      og.scores = ArrayUtil.grow(og.scores);
    }

    // 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;
      }
    }
  }
  @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
      for (int i = 0; ; i++) {
        final int c = reverseMul[i] * comparators[i].compareBottom(parentDoc);
        if (c < 0) {
          // Definitely not competitive.
          // System.out.println("    skip");
          return;
        } else if (c > 0) {
          // Definitely competitive.
          break;
        } else if (i == compEnd) {
          // Here c=0. If we're at the last comparator, this doc is not
          // competitive, since docs are visited in doc Id order, which means
          // this doc cannot compete with any other document in the queue.
          // System.out.println("    skip");
          return;
        }
      }

      // System.out.println("    competes!  doc=" + (docBase + parentDoc));

      // This hit is competitive - replace bottom element in queue & adjustTop
      for (int i = 0; i < comparators.length; i++) {
        comparators[i].copy(bottom.slot, parentDoc);
      }
      if (!trackMaxScore && trackScores) {
        score = scorer.score();
      }
      bottom.doc = docBase + parentDoc;
      bottom.readerContext = currentReaderContext;
      bottom.score = score;
      copyGroups(bottom);
      bottom = queue.updateTop();

      for (int i = 0; i < comparators.length; i++) {
        comparators[i].setBottom(bottom.slot);
      }
    } else {
      // Startup transient: queue is not yet full:
      final int comparatorSlot = totalHitCount - 1;

      // Copy hit into queue
      for (int i = 0; i < comparators.length; i++) {
        comparators[i].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 = currentReaderContext;
      copyGroups(og);
      bottom = queue.add(og);
      queueFull = totalHitCount == numParentHits;
      if (queueFull) {
        // End of startup transient: queue just filled up:
        for (int i = 0; i < comparators.length; i++) {
          comparators[i].setBottom(bottom.slot);
        }
      }
    }
  }