private RawDataAggregationOperator createRawDataAggregationOperator(
      RawDataAggregationNode node,
      LocalExecutionPlanContext context,
      Map<String, List<InputLocation>> layout) {
    checkArgument(
        !node.getAggregationDescriptorList().isEmpty(),
        "Aggregation descriptorList cannot be empty");
    Operator child = node.getChild().accept(this, context);
    boolean ascending = node.getScanOrder() == ASC;
    List<TreeAggregator> aggregators = new ArrayList<>();
    List<AggregationDescriptor> aggregationDescriptors = node.getAggregationDescriptorList();
    for (AggregationDescriptor descriptor : node.getAggregationDescriptorList()) {
      List<InputLocation[]> inputLocationList = calcInputLocationList(descriptor, layout);
      aggregators.add(
          new TreeAggregator(
              AccumulatorFactory.createAccumulator(
                  descriptor.getAggregationFuncName(),
                  descriptor.getAggregationType(),
                  descriptor.getInputExpressions().stream()
                      .map(x -> context.getTypeProvider().getTreeModelType(x.getExpressionString()))
                      .collect(Collectors.toList()),
                  descriptor.getInputExpressions(),
                  descriptor.getInputAttributes(),
                  ascending,
                  descriptor.getStep().isInputRaw()),
              descriptor.getStep(),
              inputLocationList));
    }

    GroupByTimeParameter groupByTimeParameter = node.getGroupByTimeParameter();
    GroupByParameter groupByParameter = node.getGroupByParameter();

    OperatorContext operatorContext =
        context
            .getDriverContext()
            .addOperatorContext(
                context.getNextOperatorId(),
                node.getPlanNodeId(),
                RawDataAggregationOperator.class.getSimpleName());

    ITimeRangeIterator timeRangeIterator =
        initTimeRangeIterator(groupByTimeParameter, ascending, true, context.getZoneId());
    long maxReturnSize =
        calculateMaxAggregationResultSize(
            aggregationDescriptors, timeRangeIterator, context.getTypeProvider());

    // groupByParameter and groupByTimeParameter
    if (groupByParameter != null) {
      WindowType windowType = groupByParameter.getWindowType();

      WindowParameter windowParameter;
      switch (windowType) {
        case VARIATION_WINDOW:
          Expression groupByVariationExpression = node.getGroupByExpression();
          if (groupByVariationExpression == null) {
            throw new IllegalArgumentException("groupByVariationExpression can't be null");
          }
          String controlColumn = groupByVariationExpression.getExpressionString();
          TSDataType controlColumnType = context.getTypeProvider().getTreeModelType(controlColumn);
          windowParameter =
              new VariationWindowParameter(
                  controlColumnType,
                  layout.get(controlColumn).get(0).getValueColumnIndex(),
                  node.isOutputEndTime(),
                  ((GroupByVariationParameter) groupByParameter).isIgnoringNull(),
                  ((GroupByVariationParameter) groupByParameter).getDelta());
          break;
        case CONDITION_WINDOW:
          Expression groupByConditionExpression = node.getGroupByExpression();
          if (groupByConditionExpression == null) {
            throw new IllegalArgumentException("groupByConditionExpression can't be null");
          }
          windowParameter =
              new ConditionWindowParameter(
                  node.isOutputEndTime(),
                  ((GroupByConditionParameter) groupByParameter).isIgnoringNull(),
                  layout
                      .get(groupByConditionExpression.getExpressionString())
                      .get(0)
                      .getValueColumnIndex(),
                  ((GroupByConditionParameter) groupByParameter).getKeepExpression());
          break;
        case SESSION_WINDOW:
          windowParameter =
              new SessionWindowParameter(
                  ((GroupBySessionParameter) groupByParameter).getTimeInterval(),
                  node.isOutputEndTime());
          break;
        case COUNT_WINDOW:
          Expression groupByCountExpression = node.getGroupByExpression();
          if (groupByCountExpression == null) {
            throw new IllegalArgumentException("groupByCountExpression can't be null");
          }
          windowParameter =
              new CountWindowParameter(
                  ((GroupByCountParameter) groupByParameter).getCountNumber(),
                  layout
                      .get(groupByCountExpression.getExpressionString())
                      .get(0)
                      .getValueColumnIndex(),
                  node.isOutputEndTime(),
                  ((GroupByCountParameter) groupByParameter).isIgnoreNull());
          break;
        default:
          throw new IllegalArgumentException("Unsupported window type");
      }
      return new RawDataAggregationOperator(
          operatorContext,
          aggregators,
          timeRangeIterator,
          child,
          ascending,
          maxReturnSize,
          windowParameter);
    }

    WindowParameter windowParameter = new TimeWindowParameter(node.isOutputEndTime());
    return new RawDataAggregationOperator(
        operatorContext,
        aggregators,
        timeRangeIterator,
        child,
        ascending,
        maxReturnSize,
        windowParameter);
  }
This Visitor is responsible for transferring PlanNode Tree to Operator Tree.
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java