public static JoinNode deserialize(ByteBuffer byteBuffer) {
    JoinType joinType = JoinType.values()[ReadWriteIOUtils.readInt(byteBuffer)];
    int size = ReadWriteIOUtils.readInt(byteBuffer);
    List<EquiJoinClause> criteria = new ArrayList<>(size);
    while (size-- > 0) {
      criteria.add(
          new EquiJoinClause(Symbol.deserialize(byteBuffer), Symbol.deserialize(byteBuffer)));
    }

    Optional<AsofJoinClause> asofJoinClause = Optional.empty();
    if (ReadWriteIOUtils.readBool(byteBuffer)) {
      asofJoinClause =
          Optional.of(
              new AsofJoinClause(
                  ComparisonExpression.Operator.values()[ReadWriteIOUtils.readInt(byteBuffer)],
                  Symbol.deserialize(byteBuffer),
                  Symbol.deserialize(byteBuffer)));
    }

    size = ReadWriteIOUtils.readInt(byteBuffer);
    List<Symbol> leftOutputSymbols = new ArrayList<>(size);
    while (size-- > 0) {
      leftOutputSymbols.add(Symbol.deserialize(byteBuffer));
    }

    size = ReadWriteIOUtils.readInt(byteBuffer);
    List<Symbol> rightOutputSymbols = new ArrayList<>(size);
    while (size-- > 0) {
      rightOutputSymbols.add(Symbol.deserialize(byteBuffer));
    }

    PlanNodeId planNodeId = PlanNodeId.deserialize(byteBuffer);
    return new JoinNode(
        planNodeId, joinType, criteria, asofJoinClause, leftOutputSymbols, rightOutputSymbols);
  }
@return a new JoinNode with the flipped attributes
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/JoinNode.java