class ReorderNestedLoopJoin implements Rule<NestedLoopJoin> {

    private final Pattern<NestedLoopJoin> pattern = typeOf(NestedLoopJoin.class)
        .with(j -> j.orderByWasPushedDown() == false &&
                   j.joinType().supportsInversion() == true);

    @Override
    public Pattern<NestedLoopJoin> pattern() {
        return pattern;
    }

    @Override
    public LogicalPlan apply(NestedLoopJoin nestedLoop,
                             Captures captures,
                             PlanStats planStats,
                             TransactionContext txnCtx,
                             NodeContext nodeCtx,
                             UnaryOperator<LogicalPlan> resolvePlan) {<FILL_FUNCTION_BODY>}
}

        // We move the smaller table to the right side since benchmarking
        // revealed that this improves performance in most cases.
        var lhStats = planStats.get(nestedLoop.lhs());
        var rhStats = planStats.get(nestedLoop.rhs());
        boolean expectedRowsAvailable = lhStats.numDocs() != -1 && rhStats.numDocs() != -1;
        if (expectedRowsAvailable) {
            if (lhStats.numDocs() < rhStats.numDocs()) {
                // We need to keep the same order of the output symbols when lhs/rhs are swapped
                // therefore we add an Eval on top with original output order
                return Eval.create(
                    new NestedLoopJoin(
                        nestedLoop.rhs(),
                        nestedLoop.lhs(),
                        nestedLoop.joinType().invert(),
                        nestedLoop.joinCondition(),
                        nestedLoop.isFiltered(),
                        nestedLoop.orderByWasPushedDown(),
                        nestedLoop.isRewriteNestedLoopJoinToHashJoinDone()
                    ),
                    nestedLoop.outputs());
            }
        }
        return null;
    
<no_super_class>