private static int countJoinNode(OptExpression root, boolean[] hasOuterOrSemi) {
        int count = 0;
        Operator operator = root.getOp();
        for (OptExpression child : root.getInputs()) {
            if (operator instanceof LogicalJoinOperator && ((LogicalJoinOperator) operator).getJoinHint().isEmpty()) {
                count += countJoinNode(child, hasOuterOrSemi);
            } else {
                count = Math.max(count, countJoinNode(child, hasOuterOrSemi));
            }
        }

        if (operator instanceof LogicalJoinOperator && ((LogicalJoinOperator) operator).getJoinHint().isEmpty()) {
            count += 1;
            if (!hasOuterOrSemi[0]) {
                LogicalJoinOperator joinOperator = (LogicalJoinOperator) operator;
                if (joinOperator.getJoinType().isOuterJoin() || joinOperator.getJoinType().isSemiAntiJoin()) {
                    hasOuterOrSemi[0] = true;
                }
            }
        }
        return count;
    }
@Test
    public void testCountJoinNode() {
        OptExpression root = OptExpression.create(
                new LogicalJoinOperator(JoinOperator.LEFT_OUTER_JOIN, null),
                OptExpression.create(new LogicalJoinOperator(JoinOperator.LEFT_OUTER_JOIN, null),
                        OptExpression.create(new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null)),
                        OptExpression.create(new LogicalValuesOperator(Lists.newArrayList(), Lists.newArrayList()))),
                OptExpression.create(new LogicalValuesOperator(Lists.newArrayList(), Lists.newArrayList())));

        assertEquals(1, Utils.countJoinNodeSize(root, JoinOperator.semiAntiJoinSet()));


        //      outer join (left child semi join node = 1, right child semi join node = 3) => result is 3
        //      /         \
        //  semi join      semi join (left child semi join node = 1, right child semi join node = 1)
        //                   /    \            => result is 1 + 1 + 1 = 3
        //           outer join   semi join
        //             /      \
        //         semi join   node
        root = OptExpression.create(
                new LogicalJoinOperator(JoinOperator.LEFT_OUTER_JOIN, null),
                OptExpression.create(new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null)),
                OptExpression.create(new LogicalProjectOperator(Maps.newHashMap()),
                        OptExpression.create(new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null),
                                OptExpression.create(new LogicalJoinOperator(JoinOperator.LEFT_OUTER_JOIN, null),
                                        OptExpression.create(
                                                new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null)),
                                        OptExpression.create(
                                                new LogicalValuesOperator(Lists.newArrayList(), Lists.newArrayList()))),
                                OptExpression.create(
                                        new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null)))));

        assertEquals(3, Utils.countJoinNodeSize(root, JoinOperator.semiAntiJoinSet()));

        //      semi join (left child semi join node = 0, right child semi join node = 3) => result is 0 + 3 + 1 = 4
        //      /         \
        //  inner join   semi join (left child semi join node = 2, right child semi join node = 0)
        //                /    \            => result is 2 + 0 + 1 = 3
        //        semi join    node
        //         /      \
        //   semi join  node
        root = OptExpression.create(
                new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null),
                OptExpression.create(new LogicalJoinOperator(JoinOperator.INNER_JOIN, null)),
                OptExpression.create(new LogicalProjectOperator(Maps.newHashMap()),
                        OptExpression.create(new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null),
                                OptExpression.create(new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null),
                                        OptExpression.create(
                                                new LogicalJoinOperator(JoinOperator.LEFT_SEMI_JOIN, null)),
                                        OptExpression.create(
                                                new LogicalValuesOperator(Lists.newArrayList(), Lists.newArrayList()))),
                                OptExpression.create(
                                        new LogicalValuesOperator(Lists.newArrayList(), Lists.newArrayList())))));

        assertEquals(4, Utils.countJoinNodeSize(root, JoinOperator.semiAntiJoinSet()));
    }