content: /*
   * Copyright 2020 Alibaba Group Holding Limited.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.alibaba.graphscope.common.ir.planner.rbo;
  
  import com.alibaba.graphscope.common.config.Configs;
  import com.alibaba.graphscope.common.ir.Utils;
  import com.alibaba.graphscope.common.ir.meta.IrMeta;
  import com.alibaba.graphscope.common.ir.planner.GraphIOProcessor;
  import com.alibaba.graphscope.common.ir.planner.GraphRelOptimizer;
  import com.alibaba.graphscope.common.ir.tools.GraphBuilder;
  import com.alibaba.graphscope.common.ir.tools.LogicalPlan;
  import com.google.common.collect.ImmutableMap;
  
  import org.apache.calcite.rel.RelNode;
  import org.junit.AfterClass;
  import org.junit.Assert;
  import org.junit.BeforeClass;
  import org.junit.Test;
  
  public class ScanEarlyStopTest {
      private static Configs configs;
      private static IrMeta irMeta;
      private static GraphRelOptimizer optimizer;
  
      @BeforeClass
      public static void beforeClass() {
          configs =
                  new Configs(
                          ImmutableMap.of(
                                  "graph.planner.is.on",
                                  "true",
                                  "graph.planner.opt",
                                  "CBO",
                                  "graph.planner.rules",
                                  "FilterIntoJoinRule, FilterMatchRule,  ExtendIntersectRule,"
                                          + " ScanExpandFusionRule, ExpandGetVFusionRule,"
                                          + " TopKPushDownRule, ScanEarlyStopRule"));
          optimizer = new GraphRelOptimizer(configs);
          irMeta =
                  Utils.mockIrMeta(
                          "schema/ldbc_schema_exp_hierarchy.json",
                          "statistics/ldbc30_hierarchy_statistics.json",
                          optimizer);
      }
  
      @AfterClass
      public static void afterClass() {
          if (optimizer != null) {
              optimizer.close();
          }
      }
  
      // the rules are applied by 2 steps:
      // 1. TopKPushDownRule: push down the limit operation to the project node
      // 2. ScanEarlyStopRule: fuse the limit operation to the source node
      @Test
      public void scan_early_stop_0_test() {
          GraphBuilder builder = Utils.mockGraphBuilder(optimizer, irMeta);
          RelNode before =
                  com.alibaba.graphscope.cypher.antlr4.Utils.eval(
                                  "Match (n:PERSON) Where n.firstName = 'Li' Return n Limit 10",
                                  builder)
                          .build();
          RelNode after = optimizer.optimize(before, new GraphIOProcessor(builder, irMeta));
          Assert.assertEquals(
                  "GraphLogicalProject(n=[n], isAppend=[false])\n"
                          + "  GraphLogicalSource(tableConfig=[{isAll=false, tables=[PERSON]}],"
                          + " alias=[n], fusedFilter=[[=(_.firstName, _UTF-8'Li')]], opt=[VERTEX],"
                          + " params=[{range=<0, 10>}])",
                  after.explain().trim());
      }
  
      // the rules are applied by 3 steps:
      // 1. ScanFusionRule: transform the edge expansion to edge scan, i.e. (:PERSON)-[n:KNOWS]->(b)
      // => ScanE(KNOWS)
      // 2. TopKPushDownRule: push down the limit operation to the project node
      // 2. ScanEarlyStopRule: fuse the limit operation to the source node
      @Test
      public void scan_early_stop_1_test() {
          GraphBuilder builder = Utils.mockGraphBuilder(optimizer, irMeta);
          RelNode before =
                  com.alibaba.graphscope.cypher.antlr4.Utils.eval(
                                  "Match (:PERSON)-[n:KNOWS]->(b) Where n.creationDate > $date Return"
                                          + " n Limit 10",
                                  builder)
                          .build();
          RelNode after = optimizer.optimize(before, new GraphIOProcessor(builder, irMeta));
          Assert.assertEquals(
                  "GraphLogicalProject(n=[n], isAppend=[false])\n"
                      + "  GraphLogicalSource(tableConfig=[{isAll=false, tables=[KNOWS]}], alias=[n],"
                      + " fusedFilter=[[>(_.creationDate, ?0)]], opt=[EDGE], params=[{range=<0,"
                      + " 10>}])",
                  after.explain().trim());
      }
  
      @Test
      public void scan_early_stop_2_test() {
          GraphBuilder builder = Utils.mockGraphBuilder(optimizer, irMeta);
          RelNode before =
                  com.alibaba.graphscope.cypher.antlr4.Utils.eval(
                                  "Match (:PERSON)-[n:KNOWS]->(b) Where n.creationDate > $date Return"
                                          + " n Limit 0",
                                  builder)
                          .build();
          RelNode after = optimizer.optimize(before, new GraphIOProcessor(builder, irMeta));
          LogicalPlan plan = new LogicalPlan(after);
          Assert.assertTrue(plan.isReturnEmpty());
      }
  }
  