class LuceneBatchIterator implements BatchIterator<Row> {

    private final IndexSearcher indexSearcher;
    private final Query query;
    private final CollectorContext collectorContext;
    private final boolean doScores;
    private final LuceneCollectorExpression[] expressions;
    private final List<LeafReaderContext> leaves;
    private final InputRow row;
    private Weight weight;
    private final Float minScore;

    private Iterator<LeafReaderContext> leavesIt;
    private LeafReaderContext currentLeaf;
    private Scorer currentScorer;
    private DocIdSetIterator currentDocIdSetIt;
    private volatile Throwable killed;

    public LuceneBatchIterator(IndexSearcher indexSearcher,
                               Query query,
                               @Nullable Float minScore,
                               boolean doScores,
                               CollectorContext collectorContext,
                               List<? extends Input<?>> inputs,
                               Collection<? extends LuceneCollectorExpression<?>> expressions) {
        this.indexSearcher = indexSearcher;
        this.query = query;
        this.doScores = doScores || minScore != null;
        this.minScore = minScore;
        this.collectorContext = collectorContext;
        this.row = new InputRow(inputs);
        this.expressions = expressions.toArray(new LuceneCollectorExpression[0]);
        leaves = indexSearcher.getTopReaderContext().leaves();
        leavesIt = leaves.iterator();
    }

    @Override
    public Row currentElement() {
        return row;
    }

    @Override
    public void moveToStart() {
        raiseIfKilled();
        leavesIt = leaves.iterator();
    }

    @Override
    public boolean moveNext() {<FILL_FUNCTION_BODY>}

    private boolean innerMoveNext() throws IOException {
        while (tryAdvanceDocIdSetIterator()) {
            LeafReader reader = currentLeaf.reader();
            Bits liveDocs = reader.getLiveDocs();
            int doc;
            while ((doc = currentDocIdSetIt.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
                if (docDeleted(liveDocs, doc) || belowMinScore(currentScorer)) {
                    continue;
                }
                onDoc(doc);
                return true;
            }
            currentDocIdSetIt = null;
        }
        clearState();
        return false;
    }

    private boolean belowMinScore(Scorer currentScorer) throws IOException {
        return minScore != null && currentScorer.score() < minScore;
    }

    private boolean tryAdvanceDocIdSetIterator() throws IOException {
        if (currentDocIdSetIt != null) {
            return true;
        }
        while (leavesIt.hasNext()) {
            LeafReaderContext leaf = leavesIt.next();
            Scorer scorer = weight.scorer(leaf);
            if (scorer == null) {
                continue;
            }
            currentScorer = scorer;
            currentLeaf = leaf;
            currentDocIdSetIt = scorer.iterator();
            var readerContext = new ReaderContext(currentLeaf);
            for (LuceneCollectorExpression<?> expression : expressions) {
                expression.setScorer(currentScorer);
                expression.setNextReader(readerContext);
            }
            return true;
        }
        return false;
    }

    private void clearState() {
        currentDocIdSetIt = null;
        currentScorer = null;
        currentLeaf = null;
    }

    @Override
    public void close() {
        clearState();
        killed = BatchIterator.CLOSED;
    }

    @Override
    public CompletionStage<?> loadNextBatch() throws Exception {
        throw new IllegalStateException("BatchIterator already fully loaded");
    }

    private Weight createWeight() throws IOException {
        for (LuceneCollectorExpression<?> expression : expressions) {
            expression.startCollect(collectorContext);
        }
        ScoreMode scoreMode = doScores ? ScoreMode.COMPLETE : ScoreMode.COMPLETE_NO_SCORES;
        return indexSearcher.createWeight(indexSearcher.rewrite(query), scoreMode, 1f);
    }

    @Override
    public boolean allLoaded() {
        return true;
    }

    @Override
    public boolean hasLazyResultSet() {
        return true;
    }

    private static boolean docDeleted(@Nullable Bits liveDocs, int doc) {
        if (liveDocs == null) {
            return false;
        }
        return liveDocs.get(doc) == false;
    }

    private void onDoc(int doc) throws IOException {
        for (LuceneCollectorExpression<?> expression : expressions) {
            expression.setNextDocId(doc);
        }
    }

    private void raiseIfKilled() {
        if (killed != null) {
            Exceptions.rethrowUnchecked(killed);
        }
    }

    @Override
    public void kill(@NotNull Throwable throwable) {
        killed = throwable;
    }
}

        raiseIfKilled();
        if (weight == null) {
            try {
                weight = createWeight();
            } catch (IOException e) {
                Exceptions.rethrowUnchecked(e);
            }
        }

        try {
            return innerMoveNext();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    
<no_super_class>