@Override
  public Operator visitLastQueryScan(
      final LastQueryScanNode node, final LocalExecutionPlanContext context) {
    final MeasurementPath seriesPath = node.getSeriesPath();
    TimeValuePair timeValuePair = null;
    context.dataNodeQueryContext.lock();
    try {
      if (!context.dataNodeQueryContext.unCached(seriesPath)) {
        timeValuePair = DATA_NODE_SCHEMA_CACHE.getLastCache(seriesPath);
        if (timeValuePair == null) {
          context.dataNodeQueryContext.addUnCachePath(seriesPath, node.getDataNodeSeriesScanNum());
        }
      }
    } finally {
      context.dataNodeQueryContext.unLock();
    }

    if (timeValuePair == null) { // last value is not cached
      return createUpdateLastCacheOperator(node, context, node.getSeriesPath());
    } else if (timeValuePair.getValue()
        == TableDeviceLastCache.EMPTY_PRIMITIVE_TYPE) { // there is no data for this time series
      return null;
    } else if (!LastQueryUtil.satisfyFilter(
        updateFilterUsingTTL(
            context.getGlobalTimeFilter(),
            DataNodeTTLCache.getInstance().getTTLForTree(seriesPath.getDevicePath().getNodes())),
        timeValuePair)) { // cached last value is not satisfied

      if (!isFilterGtOrGe(context.getGlobalTimeFilter())) {
        // time filter is not > or >=, we still need to read from disk
        return createUpdateLastCacheOperator(node, context, node.getSeriesPath());
      } else { // otherwise, we just ignore it and return null
        return null;
      }
    } else { //  cached last value is satisfied, put it into LastCacheScanOperator
      context.addCachedLastValue(timeValuePair, node.outputPathSymbol());
      return null;
    }
  }
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