private PartialPath transformPathByLevels(boolean isCountStar, PartialPath rawPath) {
    String[] nodes = rawPath.getNodes();
    Set<Integer> levelSet = new HashSet<>();
    for (int level : levels) {
      levelSet.add(level);
    }

    List<String> transformedNodes = new ArrayList<>(nodes.length);

    transformedNodes.add(nodes[0]);
    for (int k = 1; k < nodes.length - 1; k++) {
      if (levelSet.contains(k)) {
        transformedNodes.add(nodes[k]);
      } else {
        transformedNodes.add(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD);
      }
    }
    if (isCountStar) {
      transformedNodes.add(IoTDBConstant.ONE_LEVEL_PATH_WILDCARD);
    } else {
      transformedNodes.add(nodes[nodes.length - 1]);
    }

    // GroupByLevelNode can only accept intermediate input, so it doesn't matter what the origin
    // series datatype is for aggregation like COUNT, SUM. But for MAX_VALUE, it must be consistent
    // across different time series. And we will take one as the final type of grouped series.
    MeasurementPath groupedPath =
        new MeasurementPath(
            new PartialPath(transformedNodes.toArray(new String[0])),
            ((MeasurementPath) rawPath).getMeasurementSchema());
    if (rawPath.isMeasurementAliasExists()) {
      groupedPath.setMeasurementAlias(rawPath.getMeasurementAlias());
    }
    return groupedPath;
  }
Transform an originalPath to a partial path that satisfies given level. Path nodes don't
satisfy the given level will be replaced by "*" except the sensor level, e.g.
transformPathByLevels("root.sg.dh.d1.s1", 2) will return "root.*.dh.*.s1".

<p>Especially, if count(*), then the sensor level will be replaced by "*" too.

@return result partial path
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/GroupByLevelHelper.java