You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@asterixdb.apache.org by mb...@apache.org on 2025/09/09 00:06:56 UTC

(asterixdb) branch master updated: [ASTERIXDB-3632]: Add Index Advisor

This is an automated email from the ASF dual-hosted git repository.

mblow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 3483159e6d [ASTERIXDB-3632]: Add Index Advisor
3483159e6d is described below

commit 3483159e6d5fbce8551eb0777b5c034ca6eeba7a
Author: preetham0202 <pr...@couchbase.com>
AuthorDate: Fri May 9 16:44:37 2025 +0530

    [ASTERIXDB-3632]: Add Index Advisor
    
    - user model changes: yes
    - storage format changes: no
    - interface changes: no
    
    Ext-ref: MB-66492
    
    Change-Id: Iac773ebb49bdde18cfd340ef15d483dc324bdced
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/19763
    Integration-Tests: Jenkins <je...@fulliautomatix.ics.uci.edu>
    Tested-by: Ali Alsuliman <al...@gmail.com>
    Reviewed-by: Ali Alsuliman <al...@gmail.com>
---
 .../compiler/provider/DefaultRuleSetFactory.java   |  17 ++
 .../asterix/compiler/provider/IRuleSetFactory.java |   9 +
 .../optimizer/base/AsterixOptimizationContext.java |   5 +-
 .../asterix/optimizer/base/RuleCollections.java    |   2 +
 .../am/AbstractIntroduceAccessMethodRule.java      |   9 +-
 .../rules/am/IntroduceJoinAccessMethodRule.java    |  16 +-
 .../rules/am/IntroduceSelectAccessMethodRule.java  |  17 +-
 .../array/AbstractOperatorFromSubplanRewrite.java  |   8 +-
 .../optimizer/rules/cbo/EnumerateJoinsRule.java    |  34 ++-
 .../asterix/optimizer/rules/cbo/JoinEnum.java      |  11 +-
 .../asterix/optimizer/rules/cbo/JoinNode.java      |  57 ++---
 .../apache/asterix/optimizer/rules/cbo/Stats.java  |   9 +-
 .../cbo/indexadvisor/AbstractAdvisorPlanNode.java  |  24 ++-
 .../rules/cbo/indexadvisor/AdviseIndexRule.java    | 158 ++++++++++++++
 .../cbo/indexadvisor/AdvisorConditionParser.java   | 229 +++++++++++++++++++++
 .../cbo/indexadvisor/AdvisorJoinPlanNode.java      |  61 ++++++
 .../rules/cbo/indexadvisor/AdvisorPlanParser.java  | 114 ++++++++++
 .../cbo/indexadvisor/AdvisorScanPlanNode.java      |  67 ++++++
 .../rules/cbo/indexadvisor/CBOPlanStateTree.java   |  50 +++++
 .../rules/cbo/indexadvisor/FakeIndex.java          |  45 ++++
 .../rules/cbo/indexadvisor/FakeIndexProvider.java  | 176 ++++++++++++++++
 .../rules/cbo/indexadvisor/IIndexEnumerator.java   |  80 +++++++
 .../rules/cbo/indexadvisor/JoinFilter.java         |  13 +-
 .../cbo/indexadvisor/JoinFilterCondition.java      |  57 +++++
 .../rules/cbo/indexadvisor/ScanFilter.java         |  11 +-
 .../cbo/indexadvisor/ScanFilterCondition.java      |  26 ++-
 .../apache/asterix/api/common/APIFramework.java    |  38 +++-
 .../result/fields/IndexAdviseResultsPrinter.java   |  57 +++++
 .../asterix/app/translator/QueryTranslator.java    |   2 +-
 .../org/apache/asterix/utils/FeedOperations.java   |   2 +-
 .../queries_sqlpp/cbo-join/CBOJoinQueries.xml      |  26 ++-
 .../join-advise/join-advise.1.ddl.sqlpp            |  14 +-
 .../join-advise/join-advise.2.update.sqlpp         |  19 +-
 .../join-advise/join-advise.3.update.sqlpp         |  12 +-
 .../join-advise/join-advise.4.query.sqlpp          |  16 +-
 .../nested-fields/nested-fields.1.ddl.sqlpp        |  17 +-
 .../nested-fields/nested-fields.2.update.sqlpp     |  19 +-
 .../nested-fields/nested-fields.3.update.sqlpp     |  12 +-
 .../nested-fields/nested-fields.4.query.sqlpp      |  17 +-
 .../simple-advise/simple-advise.1.ddl.sqlpp        |  16 +-
 .../simple-advise/simple-advise.2.update.sqlpp     |  19 +-
 .../simple-advise/simple-advise.3.update.sqlpp     |  12 +-
 .../simple-advise/simple-advise.4.query.sqlpp      |  16 +-
 .../simple-advise1/simple-advise1.1.ddl.sqlpp      |  14 +-
 .../simple-advise1/simple-advise1.2.update.sqlpp   |  19 +-
 .../simple-advise1/simple-advise1.3.update.sqlpp   |  13 +-
 .../simple-advise1/simple-advise1.4.query.sqlpp    |  14 +-
 .../simple-advise1/simple-advise1.5.query.sqlpp    |  14 +-
 .../simple-advise2/simple-advise2.1.ddl.sqlpp      |  14 +-
 .../simple-advise2/simple-advise2.2.update.sqlpp   |  19 +-
 .../simple-advise2/simple-advise2.3.update.sqlpp   |  12 +-
 .../simple-advise2/simple-advise2.4.query.sqlpp    |  19 +-
 .../index-advisor/join-advise/join-advise.4.adm    |   1 +
 .../nested-fields/nested-fields.4.adm              |   1 +
 .../simple-advise/simple-advise.4.adm              |   1 +
 .../simple-advise1/simple-advise1.4.adm            |   1 +
 .../simple-advise1/simple-advise1.5.adm            |   1 +
 .../simple-advise2/simple-advise2.4.adm            |   1 +
 .../asterix/lang/common/statement/Query.java       |  21 +-
 .../asterix/lang/common/util/ExpressionUtils.java  |   2 +-
 .../CloneAndSubstituteVariablesVisitor.java        |   2 +-
 .../visitor/SqlppCopyToRewriteVisitor.java         |   2 +-
 .../asterix/lang/sqlpp/util/SqlppRewriteUtil.java  |   2 +-
 .../lang/sqlpp/visitor/DeepCopyVisitor.java        |   4 +-
 .../sqlpp/visitor/SqlppDeleteRewriteVisitor.java   |   2 +-
 .../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj    |  22 +-
 .../asterix/metadata/declared/IIndexProvider.java  |  19 +-
 .../metadata/declared/MetadataProvider.java        |   4 +-
 .../api/AbstractCompilerFactoryBuilder.java        |   6 +
 .../api/HeuristicCompilerFactoryBuilder.java       |  21 +-
 .../algebricks/compiler/api/ICompilerFactory.java  |   4 +-
 .../core/algebra/base/IOptimizationContext.java    |   3 +
 .../algebricks/core/algebra/base/IndexAdvisor.java | 147 +++++++++++++
 .../base/AlgebricksOptimizationContext.java        |  12 +-
 .../rewriter/base/IOptimizationContextFactory.java |   4 +-
 75 files changed, 1711 insertions(+), 329 deletions(-)

diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/compiler/provider/DefaultRuleSetFactory.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/compiler/provider/DefaultRuleSetFactory.java
index fa1a174e3a..b74363d1c9 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/compiler/provider/DefaultRuleSetFactory.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/compiler/provider/DefaultRuleSetFactory.java
@@ -19,6 +19,7 @@
 package org.apache.asterix.compiler.provider;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.apache.asterix.common.dataflow.ICcApplicationContext;
@@ -46,6 +47,10 @@ public class DefaultRuleSetFactory implements IRuleSetFactory {
             ICcApplicationContext appCtx) {
         if (ruleSetKind == RuleSetKind.SAMPLING) {
             return buildLogicalSampling();
+        } else if (ruleSetKind == RuleSetKind.QUERY) {
+            return getLogicalRewrites(appCtx);
+        } else if (ruleSetKind == RuleSetKind.LOGICAL_ADVISOR) {
+            return getLogicalRewrites(appCtx);
         } else {
             throw new IllegalArgumentException(String.valueOf(ruleSetKind));
         }
@@ -57,6 +62,18 @@ public class DefaultRuleSetFactory implements IRuleSetFactory {
         return buildPhysical(appCtx, CostMethods::new);
     }
 
+    @Override
+    public List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> getPhysicalRewrites(IRuleSetKind ruleSetKind,
+            ICcApplicationContext appCtx) {
+        if (ruleSetKind == RuleSetKind.QUERY) {
+            return buildPhysical(appCtx, CostMethods::new);
+        } else if (ruleSetKind == RuleSetKind.SAMPLING) {
+            return buildPhysical(appCtx, CostMethods::new);
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
     public static List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> buildLogical(
             ICcApplicationContext appCtx) {
         List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> defaultLogicalRewrites = new ArrayList<>();
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/compiler/provider/IRuleSetFactory.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/compiler/provider/IRuleSetFactory.java
index c643d21450..977875924d 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/compiler/provider/IRuleSetFactory.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/compiler/provider/IRuleSetFactory.java
@@ -29,6 +29,8 @@ import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 public interface IRuleSetFactory {
 
     enum RuleSetKind implements IRuleSetKind {
+        QUERY,
+        LOGICAL_ADVISOR,
         SAMPLING
     }
 
@@ -47,4 +49,11 @@ public interface IRuleSetFactory {
      * @return the physical rewrites
      */
     List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> getPhysicalRewrites(ICcApplicationContext appCtx);
+
+    /**
+     * @return the physical rewrites of the specified kind
+     */
+    List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> getPhysicalRewrites(IRuleSetKind ruleSetKind,
+            ICcApplicationContext appCtx);
+
 }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/AsterixOptimizationContext.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/AsterixOptimizationContext.java
index 7c6b150ed5..047394b7d4 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/AsterixOptimizationContext.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/AsterixOptimizationContext.java
@@ -27,6 +27,7 @@ import org.apache.asterix.common.metadata.MetadataConstants;
 import org.apache.asterix.metadata.declared.DataSource;
 import org.apache.asterix.metadata.declared.FunctionDataSource;
 import org.apache.hyracks.algebricks.common.constraints.AlgebricksPartitionConstraint;
+import org.apache.hyracks.algebricks.core.algebra.base.IndexAdvisor;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IConflictingTypeResolver;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionEvalSizeComputer;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionTypeComputer;
@@ -51,10 +52,10 @@ public final class AsterixOptimizationContext extends AlgebricksOptimizationCont
             IExpressionTypeComputer expressionTypeComputer, IMissableTypeComputer nullableTypeComputer,
             IConflictingTypeResolver conflictingTypeResovler, PhysicalOptimizationConfig physicalOptimizationConfig,
             AlgebricksPartitionConstraint clusterLocations, IPlanPrettyPrinter prettyPrinter,
-            IWarningCollector warningCollector) {
+            IWarningCollector warningCollector, IndexAdvisor indexAdvisor) {
         super(optContextFactory, varCounter, expressionEvalSizeComputer, mergeAggregationExpressionFactory,
                 expressionTypeComputer, nullableTypeComputer, conflictingTypeResovler, physicalOptimizationConfig,
-                clusterLocations, prettyPrinter, warningCollector);
+                clusterLocations, prettyPrinter, warningCollector, indexAdvisor);
         dataSourceMap = new Int2ObjectOpenHashMap<>();
     }
 
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
index d786e9d371..e14cbf32b9 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java
@@ -106,6 +106,7 @@ import org.apache.asterix.optimizer.rules.am.IntroducePrimaryIndexForAggregation
 import org.apache.asterix.optimizer.rules.am.IntroduceSelectAccessMethodRule;
 import org.apache.asterix.optimizer.rules.cbo.EnumerateJoinsRule;
 import org.apache.asterix.optimizer.rules.cbo.JoinEnum;
+import org.apache.asterix.optimizer.rules.cbo.indexadvisor.AdviseIndexRule;
 import org.apache.asterix.optimizer.rules.subplan.AsterixMoveFreeVariableOperatorOutOfSubplanRule;
 import org.apache.asterix.optimizer.rules.subplan.InlineSubplanInputForNestedTupleSourceRule;
 import org.apache.asterix.optimizer.rules.temporal.TranslateIntervalExpressionRule;
@@ -336,6 +337,7 @@ public final class RuleCollections {
         accessMethod.add(new RemoveUnusedOneToOneEquiJoinRule());
         accessMethod.add(new PushSimilarityFunctionsBelowJoin());
         accessMethod.add(new RemoveUnusedAssignAndAggregateRule());
+        accessMethod.add(new AdviseIndexRule());
         return accessMethod;
     }
 
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
index 24ef2d7a55..2d97258d01 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
@@ -39,6 +39,7 @@ import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.common.metadata.MetadataUtil;
 import org.apache.asterix.dataflow.data.common.ExpressionTypeComputer;
 import org.apache.asterix.metadata.declared.DataSourceId;
+import org.apache.asterix.metadata.declared.IIndexProvider;
 import org.apache.asterix.metadata.declared.MetadataProvider;
 import org.apache.asterix.metadata.entities.Dataset;
 import org.apache.asterix.metadata.entities.Index;
@@ -87,6 +88,7 @@ import com.google.common.base.Strings;
  */
 public abstract class AbstractIntroduceAccessMethodRule implements IAlgebraicRewriteRule {
     protected MetadataProvider metadataProvider;
+    protected IIndexProvider indexProvider;
 
     public abstract Map<FunctionIdentifier, List<IAccessMethod>> getAccessMethods();
 
@@ -109,8 +111,9 @@ public abstract class AbstractIntroduceAccessMethodRule implements IAlgebraicRew
         return false;
     }
 
-    protected void setMetadataDeclarations(IOptimizationContext context) {
+    protected void setMetadataIndexDeclarations(IOptimizationContext context, IIndexProvider indexProvider) {
         metadataProvider = (MetadataProvider) context.getMetadataProvider();
+        this.indexProvider = indexProvider;
     }
 
     protected void fillSubTreeIndexExprs(OptimizableOperatorSubTree subTree,
@@ -882,7 +885,7 @@ public abstract class AbstractIntroduceAccessMethodRule implements IAlgebraicRew
         LogicalVariable datasetMetaVar = null;
         if (subTree.getDataSourceType() != DataSourceType.COLLECTION_SCAN
                 && subTree.getDataSourceType() != DataSourceType.INDEXONLY_PLAN_SECONDARY_INDEX_LOOKUP) {
-            datasetIndexes = metadataProvider.getDatasetIndexes(subTree.getDataset().getDatabaseName(),
+            datasetIndexes = indexProvider.getDatasetIndexes(subTree.getDataset().getDatabaseName(),
                     subTree.getDataset().getDataverseName(), subTree.getDataset().getDatasetName());
             List<LogicalVariable> datasetVars = subTree.getDataSourceVariables();
             if (subTree.getDataset().hasMetaPart()) {
@@ -1200,6 +1203,6 @@ public abstract class AbstractIntroduceAccessMethodRule implements IAlgebraicRew
         String database = srcId.getDatabaseName();
         DataverseName dataverseName = srcId.getDataverseName();
         String datasourceName = srcId.getDatasourceName();
-        return metadataProvider.getIndex(database, dataverseName, datasourceName, datasourceName);
+        return indexProvider.getIndex(database, dataverseName, datasourceName, datasourceName);
     }
 }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceJoinAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceJoinAccessMethodRule.java
index b7ae1b0edb..5bc1e90b5d 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceJoinAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceJoinAccessMethodRule.java
@@ -26,6 +26,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.TreeMap;
 
+import org.apache.asterix.metadata.declared.IIndexProvider;
 import org.apache.asterix.metadata.entities.Dataset;
 import org.apache.asterix.metadata.entities.Index;
 import org.apache.asterix.optimizer.rules.am.array.IIntroduceAccessMethodRuleLocalRewrite;
@@ -121,7 +122,12 @@ public class IntroduceJoinAccessMethodRule extends AbstractIntroduceAccessMethod
     public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
             throws AlgebricksException {
         clear();
-        setMetadataDeclarations(context);
+        boolean adviseIndex = context.getIndexAdvisor().getAdvise();
+        if (adviseIndex) {
+            setMetadataIndexDeclarations(context, (IIndexProvider) context.getIndexAdvisor().getFakeIndexProvider());
+        } else {
+            setMetadataIndexDeclarations(context, (IIndexProvider) context.getMetadataProvider());
+        }
 
         AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
 
@@ -160,10 +166,10 @@ public class IntroduceJoinAccessMethodRule extends AbstractIntroduceAccessMethod
     }
 
     public boolean checkApplicable(Mutable<ILogicalOperator> opRef, IOptimizationContext context,
-            List<Pair<IAccessMethod, Index>> chosenIndexes, Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs)
-            throws AlgebricksException {
+            List<Pair<IAccessMethod, Index>> chosenIndexes, Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs,
+            IIndexProvider indexProvider) throws AlgebricksException {
         clear();
-        setMetadataDeclarations(context);
+        setMetadataIndexDeclarations(context, indexProvider);
 
         AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
 
@@ -332,7 +338,7 @@ public class IntroduceJoinAccessMethodRule extends AbstractIntroduceAccessMethod
             }
 
             if (continueCheck && context.getPhysicalOptimizationConfig().isArrayIndexEnabled()
-                    && JoinFromSubplanRewrite.isApplicableForRewriteCursory(metadataProvider, joinOp)) {
+                    && JoinFromSubplanRewrite.isApplicableForRewriteCursory(indexProvider, joinOp)) {
                 // If there exists a SUBPLAN in our plan, and we are conditioning on a variable, attempt to rewrite
                 // this subplan to allow an array-index AM to be introduced. If successful, this rewrite will transform
                 // into an index-nested-loop-join. This rewrite is to be used for pushing the UNNESTs and ASSIGNs from
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
index dbb164a24f..8257ec5327 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
@@ -30,6 +30,7 @@ import org.apache.asterix.common.cluster.PartitioningProperties;
 import org.apache.asterix.common.config.DatasetConfig;
 import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.metadata.declared.IIndexProvider;
 import org.apache.asterix.metadata.declared.MetadataProvider;
 import org.apache.asterix.metadata.entities.Index;
 import org.apache.asterix.optimizer.rules.am.array.IIntroduceAccessMethodRuleLocalRewrite;
@@ -155,7 +156,13 @@ public class IntroduceSelectAccessMethodRule extends AbstractIntroduceAccessMeth
     public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
             throws AlgebricksException {
         clear();
-        setMetadataDeclarations(context);
+
+        boolean adviseIndex = context.getIndexAdvisor().getAdvise();
+        if (adviseIndex) {
+            setMetadataIndexDeclarations(context, (IIndexProvider) context.getIndexAdvisor().getFakeIndexProvider());
+        } else {
+            setMetadataIndexDeclarations(context, (IIndexProvider) context.getMetadataProvider());
+        }
 
         AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
 
@@ -200,10 +207,10 @@ public class IntroduceSelectAccessMethodRule extends AbstractIntroduceAccessMeth
     }
 
     public boolean checkApplicable(Mutable<ILogicalOperator> opRef, IOptimizationContext context,
-            List<Pair<IAccessMethod, Index>> chosenIndexes, Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs)
-            throws AlgebricksException {
+            List<Pair<IAccessMethod, Index>> chosenIndexes, Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs,
+            IIndexProvider indexProvider) throws AlgebricksException {
         clear();
-        setMetadataDeclarations(context);
+        setMetadataIndexDeclarations(context, indexProvider);
 
         AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
 
@@ -507,7 +514,7 @@ public class IntroduceSelectAccessMethodRule extends AbstractIntroduceAccessMeth
             }
 
             if (continueCheck && context.getPhysicalOptimizationConfig().isArrayIndexEnabled()
-                    && SelectFromSubplanRewrite.isApplicableForRewriteCursory(metadataProvider, selectOp)) {
+                    && SelectFromSubplanRewrite.isApplicableForRewriteCursory(indexProvider, selectOp)) {
                 // If there exists a composite atomic-array index, our conjuncts will be split across multiple
                 // SELECTs. This rewrite is to be used **solely** for the purpose of changing a DATA-SCAN into a
                 // non-index-only plan branch. No nodes introduced from this rewrite will be used beyond this point.
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/array/AbstractOperatorFromSubplanRewrite.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/array/AbstractOperatorFromSubplanRewrite.java
index 38d6982bb9..04d163ec32 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/array/AbstractOperatorFromSubplanRewrite.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/array/AbstractOperatorFromSubplanRewrite.java
@@ -28,7 +28,7 @@ import org.apache.asterix.common.exceptions.CompilationException;
 import org.apache.asterix.common.exceptions.ErrorCode;
 import org.apache.asterix.common.metadata.DataverseName;
 import org.apache.asterix.metadata.declared.DataSourceId;
-import org.apache.asterix.metadata.declared.MetadataProvider;
+import org.apache.asterix.metadata.declared.IIndexProvider;
 import org.apache.asterix.om.base.AInt16;
 import org.apache.asterix.om.base.AInt32;
 import org.apache.asterix.om.base.AInt64;
@@ -73,7 +73,7 @@ abstract public class AbstractOperatorFromSubplanRewrite<T> implements IIntroduc
     private IOptimizationContext context;
     private SourceLocation sourceLocation;
 
-    public static boolean isApplicableForRewriteCursory(MetadataProvider metadataProvider, ILogicalOperator workingOp)
+    public static boolean isApplicableForRewriteCursory(IIndexProvider indexProvider, ILogicalOperator workingOp)
             throws AlgebricksException {
         boolean isApplicableForRewrite = false;
         if (workingOp.getOperatorTag() == LogicalOperatorTag.DATASOURCESCAN) {
@@ -82,14 +82,14 @@ abstract public class AbstractOperatorFromSubplanRewrite<T> implements IIntroduc
             DataverseName dataverseName = srcId.getDataverseName();
             String database = srcId.getDatabaseName();
             String datasetName = srcId.getDatasourceName();
-            if (metadataProvider.getDatasetIndexes(database, dataverseName, datasetName).stream()
+            if (indexProvider.getDatasetIndexes(database, dataverseName, datasetName).stream()
                     .anyMatch(i -> i.getIndexType() == DatasetConfig.IndexType.ARRAY)) {
                 return true;
             }
         }
 
         for (Mutable<ILogicalOperator> inputOp : workingOp.getInputs()) {
-            isApplicableForRewrite |= isApplicableForRewriteCursory(metadataProvider, inputOp.getValue());
+            isApplicableForRewrite |= isApplicableForRewriteCursory(indexProvider, inputOp.getValue());
         }
         return isApplicableForRewrite;
     }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
index 8406520070..6e4208d14e 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/EnumerateJoinsRule.java
@@ -28,7 +28,11 @@ import java.util.Map;
 
 import org.apache.asterix.common.annotations.IndexedNLJoinExpressionAnnotation;
 import org.apache.asterix.common.annotations.SkipSecondaryIndexSearchExpressionAnnotation;
+import org.apache.asterix.metadata.declared.IIndexProvider;
 import org.apache.asterix.metadata.entities.Index;
+import org.apache.asterix.optimizer.rules.cbo.indexadvisor.AdvisorPlanParser;
+import org.apache.asterix.optimizer.rules.cbo.indexadvisor.CBOPlanStateTree;
+import org.apache.asterix.optimizer.rules.cbo.indexadvisor.FakeIndexProvider;
 import org.apache.asterix.translator.SqlppExpressionToPlanTranslator;
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.commons.lang3.mutable.MutableBoolean;
@@ -107,7 +111,9 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
 
     private List<Triple<ILogicalOperator, ILogicalOperator, List<ILogicalOperator>>> modifyUnnestInfo;
     private final Map<DataSourceScanOperator, Boolean> fakeLeafInputsMap = new HashMap();
-    ILogicalOperator newRootAfterUnnest = null;
+    private ILogicalOperator newRootAfterUnnest = null;
+
+    private IIndexProvider indexProvider;
 
     public EnumerateJoinsRule(JoinEnum joinEnum) {
         this.joinEnum = joinEnum;
@@ -116,6 +122,10 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
         rootOrderByOp = null;
     }
 
+    private void setIndexProvider(IIndexProvider indexProvider) {
+        this.indexProvider = indexProvider;
+    }
+
     @Override
     public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) {
         return false;
@@ -170,8 +180,17 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
             return false;
         }
 
-        IPlanPrettyPrinter pp = context.getPrettyPrinter();
+        boolean adviseIndex = context.getIndexAdvisor().getAdvise();
+        if (adviseIndex) {
+            CBOPlanStateTree cboPlanStateTree = AdvisorPlanParser.getCBOPlanState(opRef, context);
+            FakeIndexProvider fakeIndexProvider = new FakeIndexProvider(cboPlanStateTree);
+            context.getIndexAdvisor().setFakeIndexProvider(fakeIndexProvider);
+            setIndexProvider(fakeIndexProvider);
+        } else {
+            setIndexProvider((IIndexProvider) context.getMetadataProvider());
+        }
 
+        IPlanPrettyPrinter pp = context.getPrettyPrinter();
         String viewInPlan;
         if (LOGGER.isTraceEnabled()) {
             viewInPlan = new ALogicalPlanImpl(opRef).toString(); //useful when debugging
@@ -256,8 +275,7 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
         joinEnum.initEnum((AbstractLogicalOperator) op, cboMode, cboTestMode, numberOfFromTerms, leafInputs, allJoinOps,
                 assignOps, outerJoinsDependencyList, buildSets, varLeafInputIds, unnestOpsInfo,
                 dataScanAndGroupByDistinctOps, rootGroupByDistinctOp, rootOrderByOp, resultAndJoinVars,
-                fakeLeafInputsMap, context);
-
+                fakeLeafInputsMap, context, indexProvider);
         if (cboMode) {
             if (!doAllDataSourcesHaveSamples(leafInputs, context)) {
                 return cleanUp();
@@ -729,7 +747,7 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
         }
     }
 
-    private boolean joinClause(ILogicalOperator op) {
+    public static boolean joinClause(ILogicalOperator op) {
         if (op.getOperatorTag() == LogicalOperatorTag.INNERJOIN)
             return true;
         if (op.getOperatorTag() == LogicalOperatorTag.LEFTOUTERJOIN)
@@ -764,7 +782,7 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
      * Should not see any kind of joins here. store the emptyTupeSourceOp and DataSource operators.
      * Each leaf input will normally have both, but sometimes only emptyTupeSourceOp will be present (in lists)
      */
-    private Pair<EmptyTupleSourceOperator, DataSourceScanOperator> containsLeafInputOnly(ILogicalOperator op) {
+    public static Pair<EmptyTupleSourceOperator, DataSourceScanOperator> containsLeafInputOnly(ILogicalOperator op) {
         DataSourceScanOperator dataSourceOp = null;
         ILogicalOperator currentOp = op;
         while (currentOp.getInputs().size() == 1) {
@@ -797,7 +815,7 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
     }
 
     // An internal edge must contain only assigns followed by an inner join. Not sure if there will be other ops between joins
-    private int numVarRefExprs(AssignOperator aOp) {
+    public static int numVarRefExprs(AssignOperator aOp) {
         List<Mutable<ILogicalExpression>> exprs = aOp.getExpressions();
         int count = 0;
         for (Mutable<ILogicalExpression> exp : exprs) {
@@ -828,7 +846,7 @@ public class EnumerateJoinsRule implements IAlgebraicRewriteRule {
         return (joinClause(op));
     }
 
-    private ILogicalOperator skipPastAssigns(ILogicalOperator nextOp) {
+    public static ILogicalOperator skipPastAssigns(ILogicalOperator nextOp) {
         while (nextOp.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
             nextOp = nextOp.getInputs().get(0).getValue();
         }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
index 16518c2a55..30b513bd10 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinEnum.java
@@ -39,6 +39,7 @@ import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.metadata.declared.DataSource;
 import org.apache.asterix.metadata.declared.DataSourceId;
 import org.apache.asterix.metadata.declared.DatasetDataSource;
+import org.apache.asterix.metadata.declared.IIndexProvider;
 import org.apache.asterix.metadata.declared.MetadataProvider;
 import org.apache.asterix.metadata.declared.SampleDataSource;
 import org.apache.asterix.metadata.entities.Index;
@@ -144,6 +145,7 @@ public class JoinEnum {
     protected ICostMethods costMethods;
     List<LogicalVariable> resultAndJoinVars;
     Map<DataSourceScanOperator, Boolean> fakeLeafInputsMap;
+    IIndexProvider indexProvider;
 
     public JoinEnum() {
     }
@@ -155,8 +157,8 @@ public class JoinEnum {
             List<List<List<ILogicalOperator>>> unnestOpsInfo,
             HashMap<DataSourceScanOperator, ILogicalOperator> dataScanAndGroupByDistinctOps,
             ILogicalOperator grpByDistinctOp, ILogicalOperator orderByOp, List<LogicalVariable> resultAndJoinVars,
-            Map<DataSourceScanOperator, Boolean> fakeLeafInputsMap, IOptimizationContext context)
-            throws AsterixException {
+            Map<DataSourceScanOperator, Boolean> fakeLeafInputsMap, IOptimizationContext context,
+            IIndexProvider indexProvider) throws AsterixException {
         this.singleDatasetPreds = new ArrayList<>();
         this.joinConditions = new ArrayList<>();
         this.joinHints = new HashMap<>();
@@ -187,6 +189,7 @@ public class JoinEnum {
         initCostHandleAndJoinNodes(context);
         this.allTabsJnNum = 1; // keeps track of where the final join Node will be. In case of bushy plans, this may not always be the last join nod     e.
         this.maxBits = 1;
+        this.indexProvider = indexProvider;
     }
 
     protected void initCostHandleAndJoinNodes(IOptimizationContext context) {
@@ -992,7 +995,7 @@ public class JoinEnum {
                 JoinNode jnIJ = jnArray[addPlansToThisJn];
                 jnIJ.jnArrayIndex = addPlansToThisJn;
 
-                jnIJ.addMultiDatasetPlans(jnI, jnJ);
+                jnIJ.addMultiDatasetPlans(jnI, jnJ, indexProvider);
                 if (forceJoinOrderMode && level > cboFullEnumLevel) {
                     break;
                 }
@@ -1187,7 +1190,7 @@ public class JoinEnum {
                 return PlanNode.NO_PLAN;
             }
             // We may not add any index plans, so need to check for NO_PLAN
-            jn.addIndexAccessPlans(EnumerateJoinsRule.removeTrue(leafInput));
+            jn.addIndexAccessPlans(EnumerateJoinsRule.removeTrue(leafInput), indexProvider);
         }
         return this.numberOfTerms;
     }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java
index 5cbb6f80e7..2abad0618d 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/JoinNode.java
@@ -37,6 +37,7 @@ import org.apache.asterix.common.annotations.IndexedNLJoinExpressionAnnotation;
 import org.apache.asterix.common.annotations.SkipSecondaryIndexSearchExpressionAnnotation;
 import org.apache.asterix.common.config.DatasetConfig;
 import org.apache.asterix.metadata.declared.DatasetDataSource;
+import org.apache.asterix.metadata.declared.IIndexProvider;
 import org.apache.asterix.metadata.declared.SampleDataSource;
 import org.apache.asterix.metadata.entities.Index;
 import org.apache.asterix.om.base.IAObject;
@@ -916,7 +917,8 @@ public class JoinNode {
         return changes;
     }
 
-    protected void addIndexAccessPlans(ILogicalOperator leafInput) throws AlgebricksException {
+    protected void addIndexAccessPlans(ILogicalOperator leafInput, IIndexProvider indexProvider)
+            throws AlgebricksException {
         IntroduceSelectAccessMethodRule tmp = new IntroduceSelectAccessMethodRule();
         List<Pair<IAccessMethod, Index>> chosenIndexes = new ArrayList<>();
         Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs = new TreeMap<>();
@@ -927,8 +929,8 @@ public class JoinNode {
         SelectOperator firstSelop = copySelExprsAndSetTrue(selExprs, selOpers, leafInput);
         if (firstSelop != null) { // if there are no selects, then there is no question of index selections either.
             firstSelop.getCondition().setValue(andAlltheExprs(selExprs));
-            boolean index_access_possible =
-                    tmp.checkApplicable(new MutableObject<>(leafInput), joinEnum.optCtx, chosenIndexes, analyzedAMs);
+            boolean index_access_possible = tmp.checkApplicable(new MutableObject<>(leafInput), joinEnum.optCtx,
+                    chosenIndexes, analyzedAMs, indexProvider);
             restoreSelExprs(selExprs, selOpers);
             if (index_access_possible) {
                 costAndChooseIndexPlans(leafInput, analyzedAMs);
@@ -980,8 +982,8 @@ public class JoinNode {
     }
 
     private boolean nestedLoopsApplicable(ILogicalExpression joinExpr, boolean outerJoin,
-            List<Pair<IAccessMethod, Index>> chosenIndexes, Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs)
-            throws AlgebricksException {
+            List<Pair<IAccessMethod, Index>> chosenIndexes, Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs,
+            IIndexProvider indexProvider) throws AlgebricksException {
 
         if (joinExpr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
             return false;
@@ -1037,14 +1039,15 @@ public class JoinNode {
         // Now call the rewritePre code
         IntroduceJoinAccessMethodRule tmp = new IntroduceJoinAccessMethodRule();
         boolean retVal = tmp.checkApplicable(new MutableObject<>(joinEnum.localJoinOp), joinEnum.optCtx, chosenIndexes,
-                analyzedAMs);
+                analyzedAMs, indexProvider);
 
         return retVal;
     }
 
     private boolean NLJoinApplicable(JoinNode leftJn, JoinNode rightJn, boolean outerJoin,
             ILogicalExpression nestedLoopJoinExpr, List<Pair<IAccessMethod, Index>> chosenIndexes,
-            Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs) throws AlgebricksException {
+            Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs, IIndexProvider indexProvider)
+            throws AlgebricksException {
 
         if (leftJn.fake || rightJn.fake) {
             return false;
@@ -1058,8 +1061,8 @@ public class JoinNode {
             return false; // nested loop plan not possible.
         }
 
-        if (nestedLoopJoinExpr == null
-                || !rightJn.nestedLoopsApplicable(nestedLoopJoinExpr, outerJoin, chosenIndexes, analyzedAMs)) {
+        if (nestedLoopJoinExpr == null || !rightJn.nestedLoopsApplicable(nestedLoopJoinExpr, outerJoin, chosenIndexes,
+                analyzedAMs, indexProvider)) {
             return false;
         }
 
@@ -1167,7 +1170,8 @@ public class JoinNode {
     }
 
     private int buildNLJoinPlan(PlanNode leftPlan, PlanNode rightPlan, ILogicalExpression nestedLoopJoinExpr,
-            IndexedNLJoinExpressionAnnotation hintNLJoin, boolean outerJoin) throws AlgebricksException {
+            IndexedNLJoinExpressionAnnotation hintNLJoin, boolean outerJoin, IIndexProvider indexProvider)
+            throws AlgebricksException {
         JoinNode leftJn = leftPlan.getJoinNode();
         JoinNode rightJn = rightPlan.getJoinNode();
 
@@ -1186,7 +1190,8 @@ public class JoinNode {
 
         List<Pair<IAccessMethod, Index>> chosenIndexes = new ArrayList<>();
         Map<IAccessMethod, AccessMethodAnalysisContext> analyzedAMs = new TreeMap<>();
-        if (!NLJoinApplicable(leftJn, rightJn, outerJoin, nestedLoopJoinExpr, chosenIndexes, analyzedAMs)) {
+        if (!NLJoinApplicable(leftJn, rightJn, outerJoin, nestedLoopJoinExpr, chosenIndexes, analyzedAMs,
+                indexProvider)) {
             return PlanNode.NO_PLAN;
         }
         if (chosenIndexes.isEmpty()) {
@@ -1295,7 +1300,8 @@ public class JoinNode {
         return PlanNode.NO_PLAN;
     }
 
-    protected void addMultiDatasetPlans(JoinNode leftJn, JoinNode rightJn) throws AlgebricksException {
+    protected void addMultiDatasetPlans(JoinNode leftJn, JoinNode rightJn, IIndexProvider indexProvider)
+            throws AlgebricksException {
         PlanNode leftPlan, rightPlan;
 
         if (level > joinEnum.cboFullEnumLevel) {
@@ -1307,7 +1313,7 @@ public class JoinNode {
             leftPlan = joinEnum.allPlans.get(leftJn.cheapestPlanIndex);
             rightPlan = joinEnum.allPlans.get(rightJn.cheapestPlanIndex);
 
-            addMultiDatasetPlans(leftPlan, rightPlan);
+            addMultiDatasetPlans(leftPlan, rightPlan, indexProvider);
         } else {
             // FOR JOIN NODE LEVELS LESS THAN OR EQUAL TO THE LEVEL SPECIFIED FOR FULL ENUMERATION,
             // DO FULL ENUMERATION => DO NOT PRUNE
@@ -1315,13 +1321,14 @@ public class JoinNode {
                 leftPlan = joinEnum.allPlans.get(leftPlanIndex);
                 for (int rightPlanIndex : rightJn.planIndexesArray) {
                     rightPlan = joinEnum.allPlans.get(rightPlanIndex);
-                    addMultiDatasetPlans(leftPlan, rightPlan);
+                    addMultiDatasetPlans(leftPlan, rightPlan, indexProvider);
                 }
             }
         }
     }
 
-    protected void addMultiDatasetPlans(PlanNode leftPlan, PlanNode rightPlan) throws AlgebricksException {
+    protected void addMultiDatasetPlans(PlanNode leftPlan, PlanNode rightPlan, IIndexProvider indexProvider)
+            throws AlgebricksException {
         JoinNode leftJn = leftPlan.getJoinNode();
         JoinNode rightJn = rightPlan.getJoinNode();
 
@@ -1388,12 +1395,12 @@ public class JoinNode {
                     newJoinConditions);
         } else if (hintNLJoin != null) {
             validPlan = buildHintedNLJPlans(leftPlan, rightPlan, nestedLoopJoinExpr, hintNLJoin, outerJoin,
-                    newJoinConditions);
+                    newJoinConditions, indexProvider);
         }
 
         if (!validPlan) {
             // No join hints or inapplicable hinted plans, try all non hinted plans.
-            buildAllNonHintedPlans(leftPlan, rightPlan, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
+            buildAllNonHintedPlans(leftPlan, rightPlan, hashJoinExpr, nestedLoopJoinExpr, outerJoin, indexProvider);
         }
 
         //Reset as these might have changed when we tried the commutative joins.
@@ -1468,11 +1475,11 @@ public class JoinNode {
     }
 
     private boolean buildHintedNLJPlans(PlanNode leftPlan, PlanNode rightPlan, ILogicalExpression nestedLoopJoinExpr,
-            IndexedNLJoinExpressionAnnotation hintNLJoin, boolean outerJoin, List<Integer> newJoinConditions)
-            throws AlgebricksException {
+            IndexedNLJoinExpressionAnnotation hintNLJoin, boolean outerJoin, List<Integer> newJoinConditions,
+            IIndexProvider indexProvider) throws AlgebricksException {
         int nljPlan, commutativeNljPlan;
         nljPlan = commutativeNljPlan = PlanNode.NO_PLAN;
-        nljPlan = buildNLJoinPlan(leftPlan, rightPlan, nestedLoopJoinExpr, hintNLJoin, outerJoin);
+        nljPlan = buildNLJoinPlan(leftPlan, rightPlan, nestedLoopJoinExpr, hintNLJoin, outerJoin, indexProvider);
 
         // The indexnl hint may have been removed during applicability checking
         // and is no longer available for a hintedNL plan.
@@ -1480,7 +1487,8 @@ public class JoinNode {
             return false;
         }
         if (!joinEnum.forceJoinOrderMode || level <= joinEnum.cboFullEnumLevel) {
-            commutativeNljPlan = buildNLJoinPlan(rightPlan, leftPlan, nestedLoopJoinExpr, hintNLJoin, outerJoin);
+            commutativeNljPlan =
+                    buildNLJoinPlan(rightPlan, leftPlan, nestedLoopJoinExpr, hintNLJoin, outerJoin, indexProvider);
             // The indexnl hint may have been removed during applicability checking
             // and is no longer available for a hintedNL plan.
             if (joinEnum.findNLJoinHint(newJoinConditions) == null) {
@@ -1511,7 +1519,8 @@ public class JoinNode {
     }
 
     private void buildAllNonHintedPlans(PlanNode leftPlan, PlanNode rightPlan, ILogicalExpression hashJoinExpr,
-            ILogicalExpression nestedLoopJoinExpr, boolean outerJoin) throws AlgebricksException {
+            ILogicalExpression nestedLoopJoinExpr, boolean outerJoin, IIndexProvider indexProvider)
+            throws AlgebricksException {
 
         buildHashJoinPlan(leftPlan, rightPlan, hashJoinExpr, null, outerJoin);
         if (!joinEnum.forceJoinOrderMode || level <= joinEnum.cboFullEnumLevel) {
@@ -1521,9 +1530,9 @@ public class JoinNode {
         if (!joinEnum.forceJoinOrderMode || level <= joinEnum.cboFullEnumLevel) {
             buildBroadcastHashJoinPlan(rightPlan, leftPlan, hashJoinExpr, null, outerJoin);
         }
-        buildNLJoinPlan(leftPlan, rightPlan, nestedLoopJoinExpr, null, outerJoin);
+        buildNLJoinPlan(leftPlan, rightPlan, nestedLoopJoinExpr, null, outerJoin, indexProvider);
         if (!joinEnum.forceJoinOrderMode || level <= joinEnum.cboFullEnumLevel) {
-            buildNLJoinPlan(rightPlan, leftPlan, nestedLoopJoinExpr, null, outerJoin);
+            buildNLJoinPlan(rightPlan, leftPlan, nestedLoopJoinExpr, null, outerJoin, indexProvider);
         }
         buildCPJoinPlan(leftPlan, rightPlan, hashJoinExpr, nestedLoopJoinExpr, outerJoin);
         if (!joinEnum.forceJoinOrderMode || level <= joinEnum.cboFullEnumLevel) {
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/Stats.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/Stats.java
index 47e5c6d277..deac2feff8 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/Stats.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/Stats.java
@@ -223,7 +223,7 @@ public class Stats {
         LogicalVariable var;
 
         if (unnestOp1) {// we cannot choose teh side with an array as we need the unnesting scaling factor also.
-                            // have to see if there are other alternatives later
+            // have to see if there are other alternatives later
             leafInput = joinEnum.leafInputs.get(idx2 - 1);
             var = exprUsedVars.get(1);
         } else if (unnestOp2) {
@@ -277,6 +277,9 @@ public class Stats {
             AbstractFunctionCallExpression joinExpr, JoinOperator join) throws AlgebricksException {
         AbstractBinaryJoinOperator abjoin = join.getAbstractJoinOp();
         Pair<ILogicalOperator, Double> leftOutput = replaceDataSourceWithSample(left, index1, joinExpr);
+        ILogicalOperator originalLeft, originalRight;
+        originalLeft = abjoin.getInputs().get(0).getValue();
+        originalRight = abjoin.getInputs().get(1).getValue();
         abjoin.getInputs().get(0).setValue(leftOutput.getFirst());
         Pair<ILogicalOperator, Double> rightOutput = replaceDataSourceWithSample(right, index2, joinExpr);
         abjoin.getInputs().get(1).setValue(rightOutput.getFirst());
@@ -284,6 +287,10 @@ public class Stats {
         List<List<IAObject>> result = runSamplingQuery(optCtx, abjoin);
         double estCardSample = findPredicateCardinality(result, false);
         double sel = estCardSample / leftOutput.getSecond() / rightOutput.getSecond();
+
+        abjoin.getInputs().get(0).setValue(originalLeft);
+        abjoin.getInputs().get(1).setValue(originalRight);
+
         return sel;
     }
 
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AbstractAdvisorPlanNode.java
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AbstractAdvisorPlanNode.java
index 07a80349a7..23165e1d45 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AbstractAdvisorPlanNode.java
@@ -16,15 +16,23 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
+package org.apache.asterix.optimizer.rules.cbo.indexadvisor;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+import java.util.List;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+public abstract class AbstractAdvisorPlanNode {
+    protected AbstractAdvisorPlanNode(PlanNodeType planNodeType) {
+        this.planNodeType = planNodeType;
+    }
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
+    enum PlanNodeType {
+        SCAN,
+        JOIN
+    }
+
+    private final PlanNodeType planNodeType;
+
+    public abstract List<AdvisorScanPlanNode> getLeafs();
+
+    public abstract List<AdvisorJoinPlanNode> getJoins();
 }
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdviseIndexRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdviseIndexRule.java
new file mode 100644
index 0000000000..9a7fbdd53e
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdviseIndexRule.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.asterix.common.config.DatasetConfig;
+import org.apache.asterix.common.metadata.DataverseName;
+import org.apache.asterix.metadata.declared.IIndexProvider;
+import org.apache.asterix.metadata.entities.Index;
+import org.apache.asterix.optimizer.rules.am.AccessMethodJobGenParams;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.IndexAdvisor;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import org.apache.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractUnnestMapOperator;
+import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
+
+// This rule executes after the CBO rule collections have run.
+// By this point, the CBO should have generated a plan that includes fake indexes.
+// This rule runs just once, is responsible for collecting and parsing the plan,
+// inspecting each operator, identifying index scan operators,
+// parses each operator to identify index scans, and adds the index details into the index advisor result.
+
+//distribute result [$$16]
+//  project ([$$16])
+//      assign [$$16] <- [{"A": $$A}]
+//          select (eq($$18, 1))
+//              assign [$$18] <- [$$A.getField("b")]
+//                  unnest-map [$$17, $$A] <- index-search("A", 0, "Default", "test", "A", false, false, 1, $$22, 1, $$22, true, true, true)
+//                      order (ASC, $$22) [cardinality: 0.0, doc-size: 0.0, op-cost: 0.0, total-cost: 0.0]
+//                          select (eq($$21, 1))
+//                              unnest-map [$$21, $$22] <- index-search("fake_index_4b4f5c05-f666-4ba2-912e-1a580de50542", 0, "Default", "test", "A", false, false, 1, $$19, 1, $$20, true, true, true)
+//                                  assign [$$19, $$20] <- [1, 1]
+//                                      empty-tuple-source
+
+// This rule looks at unnest-map [$$21, $$22] <- index-search("fake_index_4b4f5c05-f666-4ba2-912e-1a580de50542", 0, "Default", "test", "A", false, false, 1, $$19, 1, $$20, true, true, true)
+// and extracts the index name field names from the fake index provider.
+
+public class AdviseIndexRule implements IAlgebraicRewriteRule {
+    boolean applied = false;
+
+    public AdviseIndexRule() {
+
+    }
+
+    @Override
+    public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) {
+        return false;
+    }
+
+    @Override
+    public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
+            throws AlgebricksException {
+        if (applied) {
+            return false;
+        }
+        applied = true;
+
+        if (!context.getIndexAdvisor().getAdvise()) {
+            return false;
+        }
+
+        FakeIndexProvider fakeIndexProvider = (FakeIndexProvider) context.getIndexAdvisor().getFakeIndexProvider();
+        IIndexProvider actualIndexProvider = (IIndexProvider) context.getMetadataProvider();
+        visit(opRef, fakeIndexProvider, actualIndexProvider, context.getIndexAdvisor());
+
+        return true;
+
+    }
+
+    void visit(Mutable<ILogicalOperator> opRef, IIndexProvider fakeIndexProvider, IIndexProvider actualIndexProvider,
+            IndexAdvisor indexAdvisor) throws AlgebricksException {
+
+        ILogicalOperator op = opRef.getValue();
+
+        for (Mutable<ILogicalOperator> input : op.getInputs()) {
+            visit(input, fakeIndexProvider, actualIndexProvider, indexAdvisor);
+        }
+
+        if (op.getOperatorTag() == LogicalOperatorTag.UNNEST_MAP
+                || op.getOperatorTag() == LogicalOperatorTag.LEFT_OUTER_UNNEST_MAP) {
+            AbstractUnnestMapOperator unnestMapOp = (AbstractUnnestMapOperator) op;
+            ILogicalExpression expr = unnestMapOp.getExpressionRef().getValue();
+            if (!(expr instanceof UnnestingFunctionCallExpression unnestExpr)) {
+                return;
+            }
+
+            AccessMethodJobGenParams jobGenParams = new AccessMethodJobGenParams();
+            jobGenParams.readFromFuncArgs(unnestExpr.getArguments());
+            if (jobGenParams.isPrimaryIndex()) {
+                return;
+            }
+            if (jobGenParams.getIndexType() != DatasetConfig.IndexType.BTREE) {
+                return;
+            }
+
+            String indexName = jobGenParams.getIndexName();
+            String databaseName = jobGenParams.getDatabaseName();
+            DataverseName dataverse = jobGenParams.getDataverseName();
+            String datasetName = jobGenParams.getDatasetName();
+
+            Index fakeIndex = fakeIndexProvider.getIndex(databaseName, dataverse, datasetName, indexName);
+            Index actualIndex = lookupIndex(databaseName, dataverse, datasetName,
+                    ((Index.ValueIndexDetails) fakeIndex.getIndexDetails()).getKeyFieldNames(), actualIndexProvider);
+
+            if (actualIndex != null
+                    && actualIndex.getIndexDetails() instanceof Index.ValueIndexDetails valueIndexDetails) {
+                indexAdvisor.addPresentAdvise(actualIndex.getIndexName(), valueIndexDetails.getKeyFieldNames(),
+                        databaseName, dataverse.getCanonicalForm(), datasetName);
+                return;
+            }
+
+            indexAdvisor.addRecommendedAdvise(
+                    getIndexNameClause(((Index.ValueIndexDetails) fakeIndex.getIndexDetails()).getKeyFieldNames()),
+                    ((Index.ValueIndexDetails) fakeIndex.getIndexDetails()).getKeyFieldNames(), databaseName,
+                    dataverse.getCanonicalForm(), datasetName);
+
+        }
+
+    }
+
+    private static Index lookupIndex(String databaseName, DataverseName dataverseName, String datasetName,
+            List<List<String>> fieldsNames, IIndexProvider indexProvider) throws AlgebricksException {
+        return indexProvider.getDatasetIndexes(databaseName, dataverseName, datasetName).stream()
+                .filter(index -> index.getIndexDetails() instanceof Index.ValueIndexDetails)
+                .filter(index -> (((Index.ValueIndexDetails) index.getIndexDetails()).getKeyFieldNames()
+                        .equals(fieldsNames)))
+                .filter(index -> !index.isEnforced()).findFirst().orElse(null);
+    }
+
+    public static String getIndexNameClause(List<List<String>> fields) {
+        return "idx_" + fields.stream().map(field -> String.join("_", field)).collect(Collectors.joining("_"))
+                .replaceAll(" ", "");
+    }
+
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorConditionParser.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorConditionParser.java
new file mode 100644
index 0000000000..a86b1b5df2
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorConditionParser.java
@@ -0,0 +1,229 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.asterix.metadata.utils.PushdownUtil;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.optimizer.rules.am.BTreeAccessMethod;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
+import org.apache.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
+
+public class AdvisorConditionParser {
+
+    public static ScanFilter parseScanNode(ILogicalOperator op, IOptimizationContext context)
+            throws AlgebricksException {
+
+        List<Mutable<ILogicalExpression>> filterExprs = new ArrayList<>();
+        IVariableTypeEnvironment typeEnv = PushdownUtil.getTypeEnv(op, context);
+        ILogicalOperator tempOp = op;
+        do {
+            if (tempOp.getOperatorTag() == LogicalOperatorTag.SELECT) {
+                SelectOperator selectOp = (SelectOperator) tempOp;
+                ILogicalExpression condition = selectOp.getCondition().getValue();
+                List<Mutable<ILogicalExpression>> conjs = new ArrayList<>();
+                if (condition.splitIntoConjuncts(conjs)) {
+                    filterExprs.addAll(conjs);
+                } else {
+                    filterExprs.add(selectOp.getCondition());
+                }
+            }
+            tempOp = tempOp.getInputs().getFirst().getValue();
+        } while (tempOp.hasInputs());
+
+        filterExprs = OperatorManipulationUtil.cloneExpressions(filterExprs);
+
+        filterExprs.removeIf(expr -> !(expr.getValue() instanceof AbstractFunctionCallExpression));
+
+        tempOp = op;
+        do {
+            if (tempOp.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
+                replaceExprsWithAssign((AssignOperator) tempOp, filterExprs);
+            }
+            tempOp = tempOp.getInputs().getFirst().getValue();
+        } while (tempOp.hasInputs());
+
+        List<ScanFilterCondition> filterConditions = new ArrayList<>();
+
+        for (Mutable<ILogicalExpression> filterExpr : filterExprs) {
+            ScanFilterCondition filterCondition = parseCondition(filterExpr.getValue(), typeEnv);
+            if (filterCondition != null) {
+                filterConditions.add(filterCondition);
+            }
+        }
+
+        return new ScanFilter(filterConditions);
+    }
+
+    private static ScanFilterCondition parseCondition(ILogicalExpression logicalExpression,
+            IVariableTypeEnvironment typeEnv) throws AlgebricksException {
+        if (!(logicalExpression instanceof AbstractFunctionCallExpression expr)) {
+            return null;
+        }
+
+        FunctionIdentifier fi = expr.getFunctionIdentifier();
+
+        if (!BTreeAccessMethod.INSTANCE.getOptimizableFunctions().contains(new Pair<>(fi, false))) {
+            return null;
+        }
+
+        ConstantExpression constantExpression = null;
+        Pair<LogicalVariable, List<String>> accessPath = null;
+
+        if (expr.getArguments().get(1).getValue().getExpressionTag() == LogicalExpressionTag.CONSTANT) {
+            constantExpression = (ConstantExpression) expr.getArguments().get(1).getValue();
+            accessPath = parseAccessPath(expr.getArguments().get(0).getValue(), typeEnv);
+        } else if (expr.getArguments().get(0).getValue().getExpressionTag() == LogicalExpressionTag.CONSTANT) {
+            constantExpression = (ConstantExpression) expr.getArguments().get(0).getValue();
+            accessPath = parseAccessPath(expr.getArguments().get(1).getValue(), typeEnv);
+        }
+        if (accessPath == null) {
+            return null;
+        }
+
+        return new ScanFilterCondition(fi, accessPath.getSecond(), constantExpression);
+    }
+
+    private static Pair<LogicalVariable, List<String>> parseAccessPath(ILogicalExpression expr,
+            IVariableTypeEnvironment typeEnv) throws AlgebricksException {
+        List<String> fieldNames = new LinkedList<>();
+        while (expr.getExpressionTag() != LogicalExpressionTag.VARIABLE) {
+            if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) {
+                return null;
+            }
+            AbstractFunctionCallExpression functionCallExpr = (AbstractFunctionCallExpression) expr;
+            FunctionIdentifier fi = functionCallExpr.getFunctionIdentifier();
+            if (fi != BuiltinFunctions.FIELD_ACCESS_BY_NAME && fi != BuiltinFunctions.FIELD_ACCESS_BY_INDEX) {
+                return null;
+            }
+            String fieldName = PushdownUtil.getFieldName(functionCallExpr, typeEnv);
+            fieldNames.addFirst(fieldName);
+
+            expr = functionCallExpr.getArguments().getFirst().getValue();
+        }
+
+        VariableReferenceExpression varRef = (VariableReferenceExpression) expr;
+        LogicalVariable var = varRef.getVariableReference();
+        return new Pair<>(var, fieldNames);
+    }
+
+    public static JoinFilter parseJoinNode(AbstractBinaryJoinOperator joinOp, IOptimizationContext context)
+            throws AlgebricksException {
+        List<Mutable<ILogicalExpression>> joinExprs = new ArrayList<>();
+        IVariableTypeEnvironment typeEnv = PushdownUtil.getTypeEnv(joinOp, context);
+
+        ILogicalExpression joinExpression = joinOp.getCondition().getValue();
+        List<Mutable<ILogicalExpression>> conjs = new ArrayList<>();
+        if (joinExpression.splitIntoConjuncts(conjs)) {
+            joinExprs.addAll(conjs);
+        } else {
+            joinExprs.add(joinOp.getCondition());
+        }
+        joinExprs = OperatorManipulationUtil.cloneExpressions(joinExprs);
+        traverseAndReplace(joinOp, joinExprs);
+
+        List<JoinFilterCondition> joinConditions = new ArrayList<>();
+
+        for (Mutable<ILogicalExpression> joinExpr : joinExprs) {
+            JoinFilterCondition joinCondition = parseJoinCondition(joinExpr.getValue(), typeEnv);
+            if (joinCondition != null) {
+                joinConditions.add(joinCondition);
+            }
+        }
+
+        return new JoinFilter(joinConditions);
+    }
+
+    private static void traverseAndReplace(AbstractLogicalOperator op, List<Mutable<ILogicalExpression>> exprs) {
+
+        if (op.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
+            replaceExprsWithAssign((AssignOperator) op, exprs);
+        }
+
+        for (Mutable<ILogicalOperator> input : op.getInputs()) {
+            traverseAndReplace((AbstractLogicalOperator) input.getValue(), exprs);
+        }
+
+    }
+
+    public static void replaceExprsWithAssign(AssignOperator assignOp, List<Mutable<ILogicalExpression>> exprs) {
+        for (Mutable<ILogicalExpression> filterExpr : exprs) {
+            if (filterExpr.getValue().getExpressionTag() == LogicalExpressionTag.CONSTANT) {
+                continue;
+            }
+            for (int i = 0; i < assignOp.getVariables().size(); i++) {
+                OperatorManipulationUtil.replaceVarWithExpr((AbstractFunctionCallExpression) filterExpr.getValue(),
+                        assignOp.getVariables().get(i), assignOp.getExpressions().get(i).getValue());
+            }
+        }
+    }
+
+    public static JoinFilterCondition parseJoinCondition(ILogicalExpression logicalExpression,
+            IVariableTypeEnvironment typeEnv) throws AlgebricksException {
+
+        if (!(logicalExpression instanceof AbstractFunctionCallExpression expr)) {
+            return null;
+        }
+
+        FunctionIdentifier fi = expr.getFunctionIdentifier();
+
+        if (!BTreeAccessMethod.INSTANCE.getOptimizableFunctions().contains(new Pair<>(fi, false))) {
+            return null;
+        }
+
+        if (expr.getArguments().size() != 2) {
+            return null;
+        }
+
+        ILogicalExpression lhs = expr.getArguments().get(0).getValue();
+        ILogicalExpression rhs = expr.getArguments().get(1).getValue();
+
+        Pair<LogicalVariable, List<String>> lhsAccessPath = parseAccessPath(lhs, typeEnv);
+        if (lhsAccessPath == null) {
+            return null;
+        }
+        Pair<LogicalVariable, List<String>> rhsAccessPath = parseAccessPath(rhs, typeEnv);
+        if (rhsAccessPath == null) {
+            return null;
+        }
+        return new JoinFilterCondition(fi, lhsAccessPath.getFirst(), lhsAccessPath.getSecond(),
+                rhsAccessPath.getFirst(), rhsAccessPath.getSecond());
+    }
+
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorJoinPlanNode.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorJoinPlanNode.java
new file mode 100644
index 0000000000..44f7855aa1
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorJoinPlanNode.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
+
+public class AdvisorJoinPlanNode extends AbstractAdvisorPlanNode {
+    private final AbstractAdvisorPlanNode left;
+    private final AbstractAdvisorPlanNode right;
+    private final JoinFilter joinCondition;
+
+    public AdvisorJoinPlanNode(AbstractAdvisorPlanNode left, AbstractAdvisorPlanNode right,
+            AbstractBinaryJoinOperator joinOp, IOptimizationContext context) throws AlgebricksException {
+        super(PlanNodeType.JOIN);
+        this.left = left;
+        this.right = right;
+        this.joinCondition = AdvisorConditionParser.parseJoinNode(joinOp, context);
+    }
+
+    @Override
+    public List<AdvisorScanPlanNode> getLeafs() {
+        List<AdvisorScanPlanNode> leafs = new ArrayList<>();
+        leafs.addAll(left.getLeafs());
+        leafs.addAll(right.getLeafs());
+        return leafs;
+    }
+
+    @Override
+    public List<AdvisorJoinPlanNode> getJoins() {
+        List<AdvisorJoinPlanNode> joins = new ArrayList<>();
+        joins.add(this);
+        joins.addAll(left.getJoins());
+        joins.addAll(right.getJoins());
+        return joins;
+    }
+
+    public JoinFilter getJoinCondition() {
+        return joinCondition;
+    }
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorPlanParser.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorPlanParser.java
new file mode 100644
index 0000000000..8b341bbecd
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorPlanParser.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import static org.apache.asterix.optimizer.rules.cbo.EnumerateJoinsRule.containsLeafInputOnly;
+import static org.apache.asterix.optimizer.rules.cbo.EnumerateJoinsRule.joinClause;
+import static org.apache.asterix.optimizer.rules.cbo.EnumerateJoinsRule.numVarRefExprs;
+import static org.apache.asterix.optimizer.rules.cbo.EnumerateJoinsRule.skipPastAssigns;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractBinaryJoinOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator;
+
+public class AdvisorPlanParser {
+    public static CBOPlanStateTree getCBOPlanState(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
+            throws AlgebricksException {
+        Map<Mutable<ILogicalOperator>, Boolean> visited = new HashMap<>();
+        return visit(opRef, visited, context);
+    }
+
+    private static CBOPlanStateTree visit(Mutable<ILogicalOperator> opRef,
+            Map<Mutable<ILogicalOperator>, Boolean> visited, IOptimizationContext context) throws AlgebricksException {
+        CBOPlanStateTree planStateTree = runGetOps(opRef, visited, context);
+        if (planStateTree != null) {
+            return planStateTree;
+        }
+        for (Mutable<ILogicalOperator> childOpRef : opRef.getValue().getInputs()) {
+            planStateTree = visit(childOpRef, visited, context);
+
+            if (planStateTree != null) {
+                return planStateTree;
+            }
+        }
+        return null;
+    }
+
+    private static CBOPlanStateTree runGetOps(Mutable<ILogicalOperator> opRef,
+            Map<Mutable<ILogicalOperator>, Boolean> visited, IOptimizationContext context) throws AlgebricksException {
+        if (visited.containsKey(opRef)) {
+            return null;
+        }
+        if (!joinClause(opRef.getValue())
+                && opRef.getValue().getOperatorTag() != LogicalOperatorTag.DISTRIBUTE_RESULT) {
+            return null;
+        }
+        AbstractAdvisorPlanNode planNode = getJoinOpsAndLeafInputs(opRef.getValue(), context);
+        if (planNode == null) {
+            return null;
+        }
+        return new CBOPlanStateTree(planNode);
+    }
+
+    private static AbstractAdvisorPlanNode getJoinOpsAndLeafInputs(ILogicalOperator op, IOptimizationContext context)
+            throws AlgebricksException {
+        if (onlyAssigns(op)) {
+            AbstractBinaryJoinOperator skipAssignOp = (AbstractBinaryJoinOperator) skipPastAssigns(op);
+            AbstractAdvisorPlanNode leftPlanNode =
+                    getJoinOpsAndLeafInputs(skipAssignOp.getInputs().get(0).getValue(), context);
+            AbstractAdvisorPlanNode rightPlanNode =
+                    getJoinOpsAndLeafInputs(skipAssignOp.getInputs().get(1).getValue(), context);
+            if (leftPlanNode == null || rightPlanNode == null) {
+                return null;
+            }
+            return new AdvisorJoinPlanNode(leftPlanNode, rightPlanNode, skipAssignOp, context);
+        } else {
+            if (op.getOperatorTag() == LogicalOperatorTag.GROUP) {
+                return null;
+            }
+            Pair<EmptyTupleSourceOperator, DataSourceScanOperator> etsDataSource = containsLeafInputOnly(op);
+            if (etsDataSource == null || etsDataSource.second == null) {
+                return null;
+            }
+            return new AdvisorScanPlanNode(op, context);
+        }
+    }
+
+    private static boolean onlyAssigns(ILogicalOperator op) {
+        while (op.getOperatorTag() == LogicalOperatorTag.ASSIGN) {
+            AssignOperator aOp = (AssignOperator) op;
+            int count = numVarRefExprs(aOp);
+            if (count > 1) {
+                return false;
+            }
+            op = op.getInputs().get(0).getValue();
+        }
+        return (joinClause(op));
+    }
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorScanPlanNode.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorScanPlanNode.java
new file mode 100644
index 0000000000..f4a15d8b99
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/AdvisorScanPlanNode.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
+
+public class AdvisorScanPlanNode extends AbstractAdvisorPlanNode {
+    private ILogicalOperator op;
+    private final DataSourceScanOperator scanOperator;
+    private final ScanFilter filter;
+
+    public AdvisorScanPlanNode(ILogicalOperator op, IOptimizationContext context) throws AlgebricksException {
+        super(PlanNodeType.SCAN);
+        this.op = op;
+        this.scanOperator = findDatascanOperator(op);
+        filter = AdvisorConditionParser.parseScanNode(op, context);
+    }
+
+    private DataSourceScanOperator findDatascanOperator(ILogicalOperator op) {
+        while (op.getOperatorTag() != LogicalOperatorTag.DATASOURCESCAN) {
+            op = op.getInputs().get(0).getValue();
+        }
+        return (DataSourceScanOperator) op;
+    }
+
+    public DataSourceScanOperator getScanOperator() {
+        return scanOperator;
+    }
+
+    @Override
+    public List<AdvisorScanPlanNode> getLeafs() {
+        return Collections.singletonList(this);
+    }
+
+    @Override
+    public List<AdvisorJoinPlanNode> getJoins() {
+        return Collections.emptyList();
+    }
+
+    public ScanFilter getFilter() {
+        return filter;
+    }
+
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/CBOPlanStateTree.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/CBOPlanStateTree.java
new file mode 100644
index 0000000000..62449167cd
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/CBOPlanStateTree.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
+
+public class CBOPlanStateTree {
+    private final AbstractAdvisorPlanNode cboPlanNode;
+    private final Map<LogicalVariable, DataSourceScanOperator> dataSourceScanVariableMap;
+
+    public CBOPlanStateTree(AbstractAdvisorPlanNode cboPlanNode) {
+        this.cboPlanNode = cboPlanNode;
+        dataSourceScanVariableMap = new HashMap<>();
+        for (AdvisorScanPlanNode scanNode : cboPlanNode.getLeafs()) {
+            DataSourceScanOperator scanOperator = scanNode.getScanOperator();
+            for (LogicalVariable variable : scanOperator.getVariables()) {
+                dataSourceScanVariableMap.put(variable, scanOperator);
+            }
+        }
+    }
+
+    public AbstractAdvisorPlanNode getCboPlanNode() {
+        return cboPlanNode;
+    }
+
+    public Map<LogicalVariable, DataSourceScanOperator> getDataSourceScanVariableMap() {
+        return dataSourceScanVariableMap;
+    }
+
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndex.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndex.java
new file mode 100644
index 0000000000..b7fd1a7d5c
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndex.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.asterix.common.config.DatasetConfig;
+import org.apache.asterix.common.metadata.DataverseName;
+import org.apache.asterix.common.metadata.MetadataUtil;
+import org.apache.asterix.metadata.entities.Index;
+import org.apache.asterix.metadata.utils.Creator;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.hyracks.util.OptionalBoolean;
+
+public class FakeIndex extends Index {
+    private static final long serialVersionUID = 8100544760363679761L;
+
+    public FakeIndex(String databaseName, DataverseName dataverseName, String datasetName, String indexName,
+            List<List<String>> fieldNames, boolean isPrimary) {
+        super(databaseName, dataverseName, datasetName, indexName, DatasetConfig.IndexType.BTREE,
+                new ValueIndexDetails(fieldNames, Collections.nCopies(fieldNames.size(), 0),
+                        Collections.nCopies(fieldNames.size(), BuiltinType.ANY), false, OptionalBoolean.FALSE(),
+                        OptionalBoolean.empty(), null, null, null)
+
+                , false, isPrimary, MetadataUtil.PENDING_NO_OP, Creator.DEFAULT_CREATOR);
+    }
+
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndexProvider.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndexProvider.java
new file mode 100644
index 0000000000..ee487dd146
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/FakeIndexProvider.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import static java.util.UUID.randomUUID;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.asterix.common.config.DatasetConfig;
+import org.apache.asterix.common.metadata.DatasetFullyQualifiedName;
+import org.apache.asterix.common.metadata.DataverseName;
+import org.apache.asterix.metadata.declared.DataSourceId;
+import org.apache.asterix.metadata.declared.DatasetDataSource;
+import org.apache.asterix.metadata.declared.IIndexProvider;
+import org.apache.asterix.metadata.entities.Index;
+import org.apache.asterix.metadata.entities.InternalDatasetDetails;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.common.utils.Pair;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import org.apache.hyracks.algebricks.core.algebra.operators.logical.DataSourceScanOperator;
+
+public class FakeIndexProvider implements IIndexProvider {
+
+    private final Map<DatasetFullyQualifiedName, Map<String, Index>> filterIndexesMap;
+    private final IIndexEnumerator singleDataSourceIndexEnumerator = IIndexEnumerator.alphabeticEnumerator();
+    private final IIndexEnumerator joinIndexEnumerator = IIndexEnumerator.alphabeticEnumerator();
+
+    public FakeIndexProvider(CBOPlanStateTree planStateTree) {
+        filterIndexesMap = new HashMap<>();
+        if (planStateTree == null || planStateTree.getCboPlanNode() == null) {
+            return;
+        }
+
+        addSingleDataSourceIndexes(planStateTree.getCboPlanNode().getLeafs());
+        addJoinIndexes(planStateTree.getCboPlanNode().getJoins(), planStateTree.getDataSourceScanVariableMap());
+    }
+
+    private void addSingleDataSourceIndexes(List<AdvisorScanPlanNode> scanPlanNodes) {
+        Map<DatasetFullyQualifiedName, Pair<DataSourceScanOperator, Set<List<String>>>> singleDataSourceFieldNamesMap =
+                new HashMap<>();
+        for (AdvisorScanPlanNode scanPlanNode : scanPlanNodes) {
+            DataSourceScanOperator scanOperator = scanPlanNode.getScanOperator();
+
+            if (!(scanOperator.getDataSource() instanceof DatasetDataSource dataSource)) {
+                continue;
+            }
+            DatasetFullyQualifiedName fullyQualifiedName = dataSource.getDataset().getDatasetFullyQualifiedName();
+
+            ScanFilter filter = scanPlanNode.getFilter();
+            List<ScanFilterCondition> filterConditions = filter.filterConditions();
+            HashSet<List<String>> datasetFieldNames = new HashSet<>();
+            for (ScanFilterCondition filterCondition : filterConditions) {
+                datasetFieldNames.add(filterCondition.getLhsFieldAccessPath());
+            }
+            singleDataSourceFieldNamesMap.put(fullyQualifiedName, new Pair<>(scanOperator, datasetFieldNames));
+
+        }
+        for (Map.Entry<DatasetFullyQualifiedName, Pair<DataSourceScanOperator, Set<List<String>>>> entry : singleDataSourceFieldNamesMap
+                .entrySet()) {
+            DatasetFullyQualifiedName qualifiedName = entry.getKey();
+            Set<List<String>> fieldNames = entry.getValue().getSecond();
+            DataSourceScanOperator scanOperator = entry.getValue().getFirst();
+
+            if (((DatasetDataSource) scanOperator.getDataSource()).getDataset().getDatasetDetails()
+                    .getDatasetType() != DatasetConfig.DatasetType.INTERNAL) {
+                continue;
+            }
+
+            List<List<String>> primaryKeys =
+                    ((InternalDatasetDetails) ((DatasetDataSource) scanOperator.getDataSource()).getDataset()
+                            .getDatasetDetails()).getPrimaryKey();
+
+            Map<String, Index> datasetIndexes = new HashMap<>();
+            filterIndexesMap.put(qualifiedName, datasetIndexes);
+
+            String primaryKeyName = ((DataSourceId) scanOperator.getDataSource().getId()).getDatasourceName();
+
+            FakeIndex primIndex = new FakeIndex(qualifiedName.getDatabaseName(), qualifiedName.getDataverseName(),
+                    qualifiedName.getDatasetName(), primaryKeyName, primaryKeys, true);
+            datasetIndexes.put(primaryKeyName, primIndex);
+
+            if (fieldNames.isEmpty()) {
+                continue;
+            }
+
+            singleDataSourceIndexEnumerator.init(fieldNames);
+            Iterator<List<List<String>>> itr = singleDataSourceIndexEnumerator.getIterator();
+
+            while (itr.hasNext()) {
+                String indexName = "fake_index_" + randomUUID();
+                FakeIndex fakeIndex = new FakeIndex(qualifiedName.getDatabaseName(), qualifiedName.getDataverseName(),
+                        qualifiedName.getDatasetName(), indexName, itr.next(), false);
+                datasetIndexes.put(indexName, fakeIndex);
+            }
+        }
+    }
+
+    private void addJoinIndexes(List<AdvisorJoinPlanNode> joinPlanNodes,
+            Map<LogicalVariable, DataSourceScanOperator> dataSourceScanVariableMap) {
+        Map<DatasetFullyQualifiedName, Set<List<String>>> joinDataSourceFieldNamesMap = new HashMap<>();
+        for (AdvisorJoinPlanNode joinPlanNode : joinPlanNodes) {
+            for (JoinFilterCondition filterCondition : joinPlanNode.getJoinCondition().joinFilterConditions()) {
+                addOperandToMap(filterCondition.getLhsVar(), filterCondition.getLhsFields(),
+                        joinDataSourceFieldNamesMap, dataSourceScanVariableMap);
+                addOperandToMap(filterCondition.getRhsVar(), filterCondition.getRhsFields(),
+                        joinDataSourceFieldNamesMap, dataSourceScanVariableMap);
+            }
+
+        }
+        for (Map.Entry<DatasetFullyQualifiedName, Set<List<String>>> entry : joinDataSourceFieldNamesMap.entrySet()) {
+            DatasetFullyQualifiedName qualifiedName = entry.getKey();
+            Set<List<String>> fieldNames = entry.getValue();
+
+            joinIndexEnumerator.init(fieldNames);
+            Iterator<List<List<String>>> itr = joinIndexEnumerator.getIterator();
+            Map<String, Index> indexes = filterIndexesMap.get(qualifiedName);
+
+            while (itr.hasNext()) {
+                String indexName = "fake_join_index_" + randomUUID();
+                FakeIndex fakeIndex = new FakeIndex(qualifiedName.getDatabaseName(), qualifiedName.getDataverseName(),
+                        qualifiedName.getDatasetName(), indexName, itr.next(), false);
+                indexes.put(indexName, fakeIndex);
+            }
+
+        }
+    }
+
+    private void addOperandToMap(LogicalVariable var, List<String> fields,
+            Map<DatasetFullyQualifiedName, Set<List<String>>> joinDataSourceFieldNamesMap,
+            Map<LogicalVariable, DataSourceScanOperator> dataSourceScanVariableMap) {
+        DataSourceScanOperator op = dataSourceScanVariableMap.get(var);
+        if (op == null) {
+            return;
+        }
+        if (!(op.getDataSource() instanceof DatasetDataSource dataSource)) {
+            return;
+        }
+        DatasetFullyQualifiedName qualifiedName = dataSource.getDataset().getDatasetFullyQualifiedName();
+        joinDataSourceFieldNamesMap.computeIfAbsent(qualifiedName, k -> new HashSet<>()).add(fields);
+    }
+
+    @Override
+    public Index getIndex(String database, DataverseName dataverseName, String datasetName, String indexName)
+            throws AlgebricksException {
+        return filterIndexesMap.get(new DatasetFullyQualifiedName(database, dataverseName, datasetName)).get(indexName);
+    }
+
+    @Override
+    public List<Index> getDatasetIndexes(String database, DataverseName dataverseName, String datasetName)
+            throws AlgebricksException {
+        return filterIndexesMap.get(new DatasetFullyQualifiedName(database, dataverseName, datasetName)).values()
+                .stream().toList();
+    }
+
+}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/IIndexEnumerator.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/IIndexEnumerator.java
new file mode 100644
index 0000000000..d554e838e7
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/IIndexEnumerator.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.collections4.iterators.PermutationIterator;
+
+public interface IIndexEnumerator {
+
+    Iterator<List<List<String>>> getIterator();
+
+    void init(Collection<List<String>> fieldNames);
+
+    static IIndexEnumerator allFieldsEnumerator() {
+
+        return new IIndexEnumerator() {
+            private PermutationIterator<List<String>> permutationIterator;
+
+            @Override
+            public Iterator<List<List<String>>> getIterator() {
+                return permutationIterator;
+            }
+
+            @Override
+            public void init(Collection<List<String>> fieldNames) {
+                this.permutationIterator = new PermutationIterator<>(new ArrayList<>(fieldNames));
+            }
+        };
+    }
+
+    static IIndexEnumerator alphabeticEnumerator() {
+        return new IIndexEnumerator() {
+            private List<List<String>> listOfAllFields;
+
+            @Override
+            public Iterator<List<List<String>>> getIterator() {
+                ArrayList<List<List<String>>> list = new ArrayList<>();
+                list.add(listOfAllFields);
+                return list.iterator();
+            }
+
+            @Override
+            public void init(Collection<List<String>> fieldNames) {
+                this.listOfAllFields = new ArrayList<>(fieldNames);
+                listOfAllFields.sort((list1, list2) -> {
+                    int minSize = Math.min(list1.size(), list2.size());
+                    for (int i = 0; i < minSize; i++) {
+                        int comparison = list1.get(i).compareTo(list2.get(i));
+                        if (comparison != 0) {
+                            return comparison;
+                        }
+                    }
+                    return Integer.compare(list1.size(), list2.size());
+                });
+
+            }
+        };
+    }
+
+}
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/JoinFilter.java
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/JoinFilter.java
index 07a80349a7..e3d4af6f24 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/JoinFilter.java
@@ -16,15 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
+package org.apache.asterix.optimizer.rules.cbo.indexadvisor;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+import java.util.List;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
-
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+public record JoinFilter(List<JoinFilterCondition>joinFilterConditions){}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/JoinFilterCondition.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/JoinFilterCondition.java
new file mode 100644
index 0000000000..bca51ee54b
--- /dev/null
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/JoinFilterCondition.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.optimizer.rules.cbo.indexadvisor;
+
+import java.util.List;
+
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class JoinFilterCondition {
+    private final FunctionIdentifier fi;
+    private final List<String> lhsFields;
+    private final List<String> rhsFields;
+    private final LogicalVariable lhsVar;
+    private final LogicalVariable rhsVar;
+
+    public JoinFilterCondition(FunctionIdentifier fi, LogicalVariable lhsVar, List<String> lhsFields,
+            LogicalVariable rhsVar, List<String> rhsFields) {
+        this.fi = fi;
+        this.lhsFields = lhsFields;
+        this.rhsFields = rhsFields;
+        this.lhsVar = lhsVar;
+        this.rhsVar = rhsVar;
+    }
+
+    public List<String> getLhsFields() {
+        return lhsFields;
+    }
+
+    public List<String> getRhsFields() {
+        return rhsFields;
+    }
+
+    public LogicalVariable getLhsVar() {
+        return lhsVar;
+    }
+
+    public LogicalVariable getRhsVar() {
+        return rhsVar;
+    }
+}
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/ScanFilter.java
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/ScanFilter.java
index 07a80349a7..45535168a9 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/ScanFilter.java
@@ -16,15 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
+package org.apache.asterix.optimizer.rules.cbo.indexadvisor;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+import java.util.List;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+public record ScanFilter(List<ScanFilterCondition>filterConditions){
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/ScanFilterCondition.java
similarity index 52%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/ScanFilterCondition.java
index 07a80349a7..1036e9cd2f 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/cbo/indexadvisor/ScanFilterCondition.java
@@ -16,15 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
+package org.apache.asterix.optimizer.rules.cbo.indexadvisor;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+import java.util.List;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
+public class ScanFilterCondition {
+    private final FunctionIdentifier fi;
+    private final List<String> lhsFieldAccessPath;
+    private final ConstantExpression rhs;
+
+    public ScanFilterCondition(FunctionIdentifier fi, List<String> lhsFieldAccessPath, ConstantExpression rhs) {
+        this.fi = fi;
+        this.lhsFieldAccessPath = lhsFieldAccessPath;
+        this.rhs = rhs;
+    }
+
+    public List<String> getLhsFieldAccessPath() {
+        return lhsFieldAccessPath;
+    }
 }
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index 511861849c..59e6dc2d09 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -18,6 +18,9 @@
  */
 package org.apache.asterix.api.common;
 
+import static org.apache.asterix.compiler.provider.IRuleSetFactory.RuleSetKind.LOGICAL_ADVISOR;
+import static org.apache.asterix.compiler.provider.IRuleSetFactory.RuleSetKind.QUERY;
+
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -36,6 +39,7 @@ import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslator;
 import org.apache.asterix.algebra.base.ILangExpressionToPlanTranslatorFactory;
 import org.apache.asterix.api.http.server.ResultUtil;
 import org.apache.asterix.app.result.fields.ExplainOnlyResultsPrinter;
+import org.apache.asterix.app.result.fields.IndexAdviseResultsPrinter;
 import org.apache.asterix.app.result.fields.SignaturePrinter;
 import org.apache.asterix.common.api.INodeJobTracker;
 import org.apache.asterix.common.api.IResponsePrinter;
@@ -87,6 +91,7 @@ import org.apache.hyracks.algebricks.compiler.api.ICompiler;
 import org.apache.hyracks.algebricks.compiler.api.ICompilerFactory;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.IndexAdvisor;
 import org.apache.hyracks.algebricks.core.algebra.expressions.ExpressionRuntimeProvider;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IConflictingTypeResolver;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionEvalSizeComputer;
@@ -97,6 +102,7 @@ import org.apache.hyracks.algebricks.core.algebra.prettyprint.AlgebricksStringBu
 import org.apache.hyracks.algebricks.core.algebra.prettyprint.IPlanPrettyPrinter;
 import org.apache.hyracks.algebricks.core.algebra.prettyprint.PlanPrettyPrinter;
 import org.apache.hyracks.algebricks.core.rewriter.base.IOptimizationContextFactory;
+import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 import org.apache.hyracks.algebricks.core.rewriter.base.PhysicalOptimizationConfig;
 import org.apache.hyracks.algebricks.core.utils.DotFormatGenerator;
 import org.apache.hyracks.algebricks.data.IPrinterFactoryProvider;
@@ -180,12 +186,13 @@ public class APIFramework {
                 IMergeAggregationExpressionFactory mergeAggregationExpressionFactory,
                 IExpressionTypeComputer expressionTypeComputer, IMissableTypeComputer missableTypeComputer,
                 IConflictingTypeResolver conflictingTypeResolver, PhysicalOptimizationConfig physicalOptimizationConfig,
-                AlgebricksPartitionConstraint clusterLocations, IWarningCollector warningCollector) {
+                AlgebricksPartitionConstraint clusterLocations, IWarningCollector warningCollector,
+                IndexAdvisor indexAdvisor) {
             IPlanPrettyPrinter prettyPrinter = PlanPrettyPrinter.createStringPlanPrettyPrinter();
             return new AsterixOptimizationContext(this, varCounter, expressionEvalSizeComputer,
                     mergeAggregationExpressionFactory, expressionTypeComputer, missableTypeComputer,
                     conflictingTypeResolver, physicalOptimizationConfig, clusterLocations, prettyPrinter,
-                    warningCollector);
+                    warningCollector, indexAdvisor);
         }
 
         @Override
@@ -228,6 +235,7 @@ public class APIFramework {
             final SourceLocation sourceLoc = query != null ? query.getSourceLocation()
                     : statement != null ? statement.getSourceLocation() : null;
             final boolean isExplainOnly = isQuery && query.isExplain();
+            final boolean isAdviceOnly = isQuery && query.isAdvise();
 
             SessionConfig conf = output.config();
             if (isQuery && !conf.is(SessionConfig.FORMAT_ONLY_PHYSICAL_OPS)
@@ -261,6 +269,7 @@ public class APIFramework {
             builder.setLogicalRewrites(() -> ruleSetFactory.getLogicalRewrites(ccAppContext));
             builder.setLogicalRewritesByKind(kind -> ruleSetFactory.getLogicalRewrites(kind, ccAppContext));
             builder.setPhysicalRewrites(() -> ruleSetFactory.getPhysicalRewrites(ccAppContext));
+            builder.setPhysicalRewritesByKind(kind -> ruleSetFactory.getPhysicalRewrites(kind, ccAppContext));
             IDataFormat format = metadataProvider.getDataFormat();
             ICompilerFactory compilerFactory = builder.create();
             builder.setExpressionEvalSizeComputer(format.getExpressionEvalSizeComputer());
@@ -302,7 +311,10 @@ public class APIFramework {
             builder.setTypeTraitProvider(format.getTypeTraitProvider());
             builder.setNormalizedKeyComputerFactoryProvider(format.getNormalizedKeyComputerFactoryProvider());
 
-            ICompiler compiler = compilerFactory.createCompiler(plan, metadataProvider, t.getVarCounter());
+            IndexAdvisor indexAdvisor = new IndexAdvisor(isAdviceOnly);
+            IRuleSetKind ruleSetKind = isAdviceOnly ? LOGICAL_ADVISOR : QUERY;
+            ICompiler compiler = compilerFactory.createCompiler(plan, metadataProvider, t.getVarCounter(), ruleSetKind,
+                    indexAdvisor);
             if (conf.isOptimize()) {
                 compiler.optimize();
                 if (conf.is(SessionConfig.OOB_OPTIMIZED_LOGICAL_PLAN) || isExplainOnly) {
@@ -313,7 +325,7 @@ public class APIFramework {
                         PlanPrettyPrinter.printPhysicalOps(plan, buf, 0, true);
                         output.out().write(buf.toString());
                     } else {
-                        if (isQuery || isLoad || isCopy) {
+                        if ((isQuery || isLoad || isCopy) && !isAdviceOnly) {
                             generateOptimizedLogicalPlan(plan, output.config().getPlanFormat(), cboMode);
                         }
                     }
@@ -350,6 +362,12 @@ public class APIFramework {
 
             JobEventListenerFactory jobEventListenerFactory =
                     new JobEventListenerFactory(txnId, metadataProvider.isWriteTransaction());
+
+            if (isAdviceOnly) {
+                printAdviseAsResult(metadataProvider, output, printer, indexAdvisor);
+                return null;
+            }
+
             JobSpecification spec = compiler.createJob(ccAppContext, jobEventListenerFactory, runtimeFlags);
 
             if (isQuery || isCopy) {
@@ -399,6 +417,18 @@ public class APIFramework {
         }
     }
 
+    private void printAdviseAsResult(MetadataProvider metadataProvider, SessionOutput output, IResponsePrinter printer,
+            IndexAdvisor advisor) throws AlgebricksException {
+
+        try {
+            printer.addResultPrinter(
+                    new IndexAdviseResultsPrinter(metadataProvider.getApplicationContext(), advisor, output));
+            printer.printResults();
+        } catch (HyracksDataException e) {
+            throw new AlgebricksException(e);
+        }
+    }
+
     private void printPlanAsResult(MetadataProvider metadataProvider, SessionOutput output, IResponsePrinter printer,
             boolean printSignature) throws AlgebricksException {
         try {
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/IndexAdviseResultsPrinter.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/IndexAdviseResultsPrinter.java
new file mode 100644
index 0000000000..9e636ff332
--- /dev/null
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/result/fields/IndexAdviseResultsPrinter.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.asterix.app.result.fields;
+
+import java.io.PrintWriter;
+
+import org.apache.asterix.api.http.server.ResultUtil;
+import org.apache.asterix.common.api.IApplicationContext;
+import org.apache.asterix.common.api.IResponseFieldPrinter;
+import org.apache.asterix.translator.IStatementExecutor;
+import org.apache.asterix.translator.SessionConfig;
+import org.apache.asterix.translator.SessionOutput;
+import org.apache.hyracks.algebricks.core.algebra.base.IndexAdvisor;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+public class IndexAdviseResultsPrinter implements IResponseFieldPrinter {
+
+    private final IApplicationContext appCtx;
+    private final IndexAdvisor advisor;
+    private final SessionOutput sessionOutput;
+
+    public IndexAdviseResultsPrinter(IApplicationContext appCtx, IndexAdvisor advisor, SessionOutput sessionOutput) {
+        this.appCtx = appCtx;
+        this.advisor = advisor;
+        this.sessionOutput = sessionOutput;
+    }
+
+    @Override
+    public void print(PrintWriter pw) throws HyracksDataException {
+        boolean quoteResult = sessionOutput.config().getPlanFormat() == SessionConfig.PlanFormat.STRING;
+        sessionOutput.config().set(SessionConfig.FORMAT_QUOTE_RECORD, quoteResult);
+        ResultUtil.printResults(appCtx, advisor.getResultJson().toString(), sessionOutput,
+                new IStatementExecutor.Stats(), null);
+    }
+
+    @Override
+    public String getName() {
+        return ResultsPrinter.FIELD_NAME;
+    }
+
+}
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
index bdbcbfb39a..888bef14a3 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/translator/QueryTranslator.java
@@ -5503,7 +5503,7 @@ public class QueryTranslator extends AbstractLangTranslator implements IStatemen
                 MetadataManager.INSTANCE.commitTransaction(mdTxnCtx);
                 stats.setCompileTime(System.nanoTime() - compileStart);
                 bActiveTxn = false;
-                return query.isExplain() || isCompileOnly() ? null : jobSpec;
+                return query.isExplain() || query.isAdvise() || isCompileOnly() ? null : jobSpec;
             } catch (Exception e) {
                 LOGGER.log(Level.INFO, e.getMessage(), e);
                 if (bActiveTxn) {
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/utils/FeedOperations.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/utils/FeedOperations.java
index 93233497dd..267a8c8a6a 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/utils/FeedOperations.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/utils/FeedOperations.java
@@ -213,7 +213,7 @@ public class FeedOperations {
                 whereClause != null ? Collections.singletonList(whereClause) : null, null, null);
         SelectSetOperation selectSetOperation = new SelectSetOperation(new SetOperationInput(selectBlock, null), null);
         SelectExpression body = new SelectExpression(null, selectSetOperation, null, null, true);
-        Query query = new Query(false, true, body, 0);
+        Query query = new Query(false, false, true, body, 0);
         return query;
     }
 
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/CBOJoinQueries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/CBOJoinQueries.xml
index 26eb5ca4e0..67691a3eaa 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/CBOJoinQueries.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/CBOJoinQueries.xml
@@ -21,5 +21,29 @@
             <output-dir compare="Text">ch2</output-dir>
         </compilation-unit>
     </test-case>
- ! Unless required by applicable la
+    <test-case FilePath="cbo-join/index-advisor">
+        <compilation-unit name="simple-advise">
+            <output-dir compare="Text">simple-advise</output-dir>
+        </compilation-unit>
+    </test-case>
+    <test-case FilePath="cbo-join/index-advisor">
+        <compilation-unit name="simple-advise1">
+            <output-dir compare="Text">simple-advise1</output-dir>
+        </compilation-unit>
+    </test-case>
+    <test-case FilePath="cbo-join/index-advisor">
+        <compilation-unit name="simple-advise2">
+            <output-dir compare="Text">simple-advise2</output-dir>
+        </compilation-unit>
+    </test-case>
+    <test-case FilePath="cbo-join/index-advisor">
+        <compilation-unit name="nested-fields">
+            <output-dir compare="Text">nested-fields</output-dir>
+        </compilation-unit>
+    </test-case>
+    <test-case FilePath="cbo-join/index-advisor">
+        <compilation-unit name="join-advise">
+            <output-dir compare="Text">join-advise</output-dir>
+        </compilation-unit>
+    </test-case>
 </test-group>
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.1.ddl.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.1.ddl.sqlpp
index 07a80349a7..7068064fff 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.1.ddl.sqlpp
@@ -16,15 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+drop dataverse test if exists;
+create dataverse test;
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+CREATE DATASET A PRIMARY KEY ( a_0 : integer);
+CREATE DATASET B PRIMARY KEY ( b_0 : integer);
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.2.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.2.update.sqlpp
index 07a80349a7..c0addba0f9 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.2.update.sqlpp
@@ -16,15 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+INSERT INTO A(
+    SELECT VALUE    {"a_0": i, "a_1": 1 , "a_2" : i%5 }
+    FROM range(1, 1000) i
+);
+
+INSERT INTO B(
+    SELECT VALUE    {"b_0": i, "b_1" : i  ,"b_2": i}
+    FROM range(1, 1000) i
+);
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.3.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.3.update.sqlpp
index 07a80349a7..686e092211 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.3.update.sqlpp
@@ -16,15 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+use test;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ANALYZE DATASET A with {"sample": "high", "sample-seed": 2};
+ANALYZE DATASET B with {"sample": "high", "sample-seed": 2};
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.4.query.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.4.query.sqlpp
index 07a80349a7..b74f810bc1 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/join-advise/join-advise.4.query.sqlpp
@@ -1,3 +1,4 @@
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -16,15 +17,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
-
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ADVISE SELECT A.a_0, A.a_1, A.a_2, B.b_0, B.b_1, B.b_2
+       FROM  A JOIN B   ON (
+           A.a_1  = B.b_1)
+       WHERE A.a_2 = 1;
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.1.ddl.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.1.ddl.sqlpp
index 07a80349a7..cce6346f97 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.1.ddl.sqlpp
@@ -16,15 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+drop dataverse test if exists;
+create dataverse test;
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+CREATE DATASET A PRIMARY KEY ( a_0 : integer);
+CREATE DATASET B PRIMARY KEY ( b_0 : integer);
+
+CREATE INDEX idx_2 ON A(a_1.n_1.`n_2 field name`.`n_3`: time) CAST (DEFAULT NULL time 'hh:mm:ss.nnna');
+CREATE INDEX i3 ON B(UNNEST b.c UNNEST d.e UNNEST t SELECT x.y : string, q.w: int, u: int) EXCLUDE UNKNOWN KEY;
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.2.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.2.update.sqlpp
index 07a80349a7..67f4df518f 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.2.update.sqlpp
@@ -16,15 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+INSERT INTO A(
+    SELECT VALUE    {"a_0" : i , "a_1": { "n_1 field" :{ "n_2 name" : i%20 }}, "a_2" : { "n_1 field" :{ "n_2 name" : i }} }
+    FROM range(1, 1000) i
+);
+
+INSERT INTO B(
+    SELECT VALUE    {"b_0": i, "b_1" : { "n_1 field" :{ "n_2 name" : i }}  ,"b_2": { "n_1 field" :{ "n_2 name" : i }}}
+    FROM range(1, 500) i
+);
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.3.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.3.update.sqlpp
index 07a80349a7..dadb7f1f76 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.3.update.sqlpp
@@ -16,15 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+use test;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ANALYZE DATASET A with {"sample": "high", "sample-seed": 2};
+ANALYZE DATASET B with {"sample": "high", "sample-seed": 2};
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.4.query.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.4.query.sqlpp
index 07a80349a7..3732f411c7 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/nested-fields/nested-fields.4.query.sqlpp
@@ -16,15 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+use test;
+
+
+ADVISE
+SELECT *
+FROM  A
+WHERE A.a_2.`n_1 field`.`n_2 name` = 1 AND repeat(A.a_1.`n_1 field`.`n_2 name`, 2) = "aa";
+
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.1.ddl.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.1.ddl.sqlpp
index 07a80349a7..18cf0e6e64 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.1.ddl.sqlpp
@@ -16,15 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+drop dataverse test if exists;
+create dataverse test;
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+CREATE DATASET A PRIMARY KEY ( a_0 : integer);
+CREATE DATASET B PRIMARY KEY ( b_0 : integer);
+
+CREATE INDEX idx_a_1 ON A(a_1);
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.2.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.2.update.sqlpp
index 07a80349a7..36c0078035 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.2.update.sqlpp
@@ -16,15 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+INSERT INTO A(
+    SELECT VALUE    {"a_0": i, "a_1": i%20 , "a_2" : i }
+    FROM range(1, 1000) i
+);
+
+INSERT INTO B(
+    SELECT VALUE    {"b_0": i, "b_1" : i  ,"b_2": i}
+    FROM range(1, 500) i
+);
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.3.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.3.update.sqlpp
index 07a80349a7..dadb7f1f76 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.3.update.sqlpp
@@ -16,15 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+use test;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ANALYZE DATASET A with {"sample": "high", "sample-seed": 2};
+ANALYZE DATASET B with {"sample": "high", "sample-seed": 2};
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.4.query.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.4.query.sqlpp
index 07a80349a7..37c4659b4e 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise/simple-advise.4.query.sqlpp
@@ -1,3 +1,4 @@
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -16,15 +17,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
-
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ADVISE SELECT A.a_0, A.a_1, A.a_2, B.b_0, B.b_1, B.b_2
+FROM  A JOIN B   ON (
+    A.a_2  = B.b_2)
+WHERE A.a_1 = 1;
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.1.ddl.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.1.ddl.sqlpp
index 07a80349a7..e3f9e43baf 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.1.ddl.sqlpp
@@ -16,15 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+drop dataverse test if exists;
+create dataverse test;
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+CREATE DATASET A PRIMARY KEY ( id : integer);
+CREATE DATASET B PRIMARY KEY ( id : integer);
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.2.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.2.update.sqlpp
index 07a80349a7..e3fbe85c48 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.2.update.sqlpp
@@ -16,15 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+INSERT INTO A(
+    SELECT VALUE {"id": intVal, "a": intVal%2, "b": intVal%25 , "d": intVal%25}
+    FROM range(1, 1000) intVal
+);
+
+INSERT INTO B(
+    SELECT VALUE {"id": intVal, "a": intVal%5, "c": intVal%25}
+    FROM range(1, 1000) intVal
+);
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.3.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.3.update.sqlpp
index 07a80349a7..4a1c864aaf 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.3.update.sqlpp
@@ -16,15 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+use test;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
-
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ANALYZE DATASET A with {"sample": "high", "sample-seed": 2};
+ANALYZE DATASET B with {"sample": "high", "sample-seed": 2};
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.4.query.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.4.query.sqlpp
index 07a80349a7..2357984936 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.4.query.sqlpp
@@ -1,3 +1,4 @@
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -16,15 +17,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
-
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ADVISE select * from A
+       where A.b=1 ;
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.5.query.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.5.query.sqlpp
index 07a80349a7..a9517626d7 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise1/simple-advise1.5.query.sqlpp
@@ -1,3 +1,4 @@
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -16,15 +17,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
-
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ADVISE select * from A join B on A.a=B.a
+       where A.b=1 AND B.c=2;
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.1.ddl.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.1.ddl.sqlpp
index 07a80349a7..432f897d6b 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.1.ddl.sqlpp
@@ -16,15 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+drop dataverse test if exists;
+create dataverse test;
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+CREATE DATASET A PRIMARY KEY ( pa : integer);
+CREATE DATASET B PRIMARY KEY ( pb : integer);
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.2.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.2.update.sqlpp
index 07a80349a7..0fd47f4a50 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.2.update.sqlpp
@@ -16,15 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+INSERT INTO A(
+    SELECT VALUE    { "pa" : i  , "a0": i%2, "a1": i%40 , "a2" : i%30 , "a3" : i%5, "a4" : i }
+    FROM range(1, 500) i
+);
+
+INSERT INTO B(
+    SELECT VALUE    { "pb" : i  , "b3" : 5, "b4" : i }
+    FROM range(1, 500) i
+);
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.3.update.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.3.update.sqlpp
index 07a80349a7..686e092211 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.3.update.sqlpp
@@ -16,15 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+use test;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ANALYZE DATASET A with {"sample": "high", "sample-seed": 2};
+ANALYZE DATASET B with {"sample": "high", "sample-seed": 2};
\ No newline at end of file
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.4.query.sqlpp
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.4.query.sqlpp
index 07a80349a7..6a9bb9964f 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/cbo-join/index-advisor/simple-advise2/simple-advise2.4.query.sqlpp
@@ -1,3 +1,4 @@
+
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -16,15 +17,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
-
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+use test;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
-}
+ADVISE
+SELECT *
+FROM A
+JOIN B ON A.a3 = B.b3 AND A.a4 = B.b4
+WHERE A.a0 = 0 AND A.a1 = 0 AND A.a2 = 0
+and sleep(A.a5,10) = 0
+;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/join-advise/join-advise.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/join-advise/join-advise.4.adm
new file mode 100644
index 0000000000..42da673db7
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/join-advise/join-advise.4.adm
@@ -0,0 +1 @@
+[{"#operator":"Advise","advice":{"#operator":"IndexAdvice","adviseinfo":{"current_indexes":[],"recommended_indexes":{"indexes":[{"index_statement":"CREATE INDEX idx_b_1 ON `Default`.`test`.`B`(`b_1`);"}]}}}}]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/nested-fields/nested-fields.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/nested-fields/nested-fields.4.adm
new file mode 100644
index 0000000000..db990cb9bb
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/nested-fields/nested-fields.4.adm
@@ -0,0 +1 @@
+[{"#operator":"Advise","advice":{"#operator":"IndexAdvice","adviseinfo":{"current_indexes":[],"recommended_indexes":{"indexes":[{"index_statement":"CREATE INDEX idx_a_2_n_1field_n_2name ON `Default`.`test`.`A`(`a_2`.`n_1 field`.`n_2 name`);"}]}}}}]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise/simple-advise.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise/simple-advise.4.adm
new file mode 100644
index 0000000000..55150dedd9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise/simple-advise.4.adm
@@ -0,0 +1 @@
+[{"#operator":"Advise","advice":{"#operator":"IndexAdvice","adviseinfo":{"current_indexes":[{"index_statement":"CREATE INDEX idx_a_1 ON `Default`.`test`.`A`(`a_1`);"}],"recommended_indexes":{"indexes":[{"index_statement":"CREATE INDEX idx_b_2 ON `Default`.`test`.`B`(`b_2`);"}]}}}}]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise1/simple-advise1.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise1/simple-advise1.4.adm
new file mode 100644
index 0000000000..e2b4c29dde
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise1/simple-advise1.4.adm
@@ -0,0 +1 @@
+[{"#operator":"Advise","advice":{"#operator":"IndexAdvice","adviseinfo":{"current_indexes":[],"recommended_indexes":{"indexes":[{"index_statement":"CREATE INDEX idx_b ON `Default`.`test`.`A`(`b`);"}]}}}}]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise1/simple-advise1.5.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise1/simple-advise1.5.adm
new file mode 100644
index 0000000000..e2b4c29dde
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise1/simple-advise1.5.adm
@@ -0,0 +1 @@
+[{"#operator":"Advise","advice":{"#operator":"IndexAdvice","adviseinfo":{"current_indexes":[],"recommended_indexes":{"indexes":[{"index_statement":"CREATE INDEX idx_b ON `Default`.`test`.`A`(`b`);"}]}}}}]
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise2/simple-advise2.4.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise2/simple-advise2.4.adm
new file mode 100644
index 0000000000..f519cd1246
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/cbo-join/index-advisor/simple-advise2/simple-advise2.4.adm
@@ -0,0 +1 @@
+[{"#operator":"Advise","advice":{"#operator":"IndexAdvice","adviseinfo":{"current_indexes":[],"recommended_indexes":{"indexes":[{"index_statement":"CREATE INDEX idx_a0_a1_a2 ON `Default`.`test`.`A`(`a0`,`a1`,`a2`);"}]}}}}]
\ No newline at end of file
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/Query.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/Query.java
index 7332c7d226..2e3974bab4 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/Query.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/statement/Query.java
@@ -31,20 +31,23 @@ import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
 
 public class Query extends AbstractStatement implements IReturningStatement {
     private boolean explain;
+    private boolean advise;
     private boolean topLevel = true;
     private Expression body;
     private int varCounter;
 
     public Query() {
-        this(false);
+        this(false, false);
     }
 
-    public Query(boolean explain) {
+    public Query(boolean explain, boolean advise) {
         this.explain = explain;
+        this.advise = advise;
     }
 
-    public Query(boolean explain, boolean topLevel, Expression body, int varCounter) {
+    public Query(boolean explain, boolean advise, boolean topLevel, Expression body, int varCounter) {
         this.explain = explain;
+        this.advise = advise;
         this.topLevel = topLevel;
         this.body = body;
         this.varCounter = varCounter;
@@ -88,10 +91,18 @@ public class Query extends AbstractStatement implements IReturningStatement {
         this.explain = explain;
     }
 
+    public void setAdvise(boolean advise) {
+        this.advise = advise;
+    }
+
     public boolean isExplain() {
         return explain;
     }
 
+    public boolean isAdvise() {
+        return advise;
+    }
+
     @Override
     public Kind getKind() {
         return Statement.Kind.QUERY;
@@ -104,7 +115,7 @@ public class Query extends AbstractStatement implements IReturningStatement {
 
     @Override
     public int hashCode() {
-        return Objects.hash(body, topLevel, explain, varCounter);
+        return Objects.hash(body, topLevel, explain, advise, varCounter);
     }
 
     @Override
@@ -117,7 +128,7 @@ public class Query extends AbstractStatement implements IReturningStatement {
         }
         Query target = (Query) object;
         return explain == target.explain && Objects.equals(body, target.body) && topLevel == target.topLevel
-                && varCounter == target.varCounter;
+                && varCounter == target.varCounter && advise == target.advise;
     }
 
     @Override
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/ExpressionUtils.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/ExpressionUtils.java
index a7de54124c..afa2effa65 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/ExpressionUtils.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/util/ExpressionUtils.java
@@ -369,7 +369,7 @@ public class ExpressionUtils {
     }
 
     public static Query createWrappedQuery(Expression expr, SourceLocation sourceLoc) {
-        Query wrappedQuery = new Query(false);
+        Query wrappedQuery = new Query(false, false);
         wrappedQuery.setSourceLocation(sourceLoc);
         wrappedQuery.setBody(expr);
         wrappedQuery.setTopLevel(false);
diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
index 4f09722921..c515cdd52d 100644
--- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
+++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
@@ -264,7 +264,7 @@ public class CloneAndSubstituteVariablesVisitor extends
     @Override
     public Pair<ILangExpression, VariableSubstitutionEnvironment> visit(Query q, VariableSubstitutionEnvironment env)
             throws CompilationException {
-        Query newQ = new Query(q.isExplain());
+        Query newQ = new Query(q.isExplain(), q.isAdvise());
         Pair<ILangExpression, VariableSubstitutionEnvironment> p1 = q.getBody().accept(this, env);
         newQ.setBody((Expression) p1.first);
         newQ.setSourceLocation(q.getSourceLocation());
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppCopyToRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppCopyToRewriteVisitor.java
index d15eb2ddf0..ef38f58cab 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppCopyToRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/SqlppCopyToRewriteVisitor.java
@@ -82,7 +82,7 @@ public class SqlppCopyToRewriteVisitor extends AbstractSqlppAstVisitor<Void, Met
         selectSetOperation.setSourceLocation(var.getSourceLocation());
         SelectExpression selectExpression = new SelectExpression(null, selectSetOperation, null, null, false);
         selectExpression.setSourceLocation(var.getSourceLocation());
-        Query query = new Query(false, false, selectExpression, 0);
+        Query query = new Query(false, false, false, selectExpression, 0);
         query.setBody(selectExpression);
         query.setSourceLocation(stmtCopy.getSourceLocation());
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
index 62acfebe0a..f48cd99291 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/util/SqlppRewriteUtil.java
@@ -89,7 +89,7 @@ public class SqlppRewriteUtil {
         }
         // Creates a wrapper query for the expression so that if the expression itself
         // is the key, it can also be replaced.
-        Query wrapper = new Query(false);
+        Query wrapper = new Query(false, false);
         wrapper.setSourceLocation(expression.getSourceLocation());
         wrapper.setBody(expression);
         // Creates a substitution visitor.
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
index 9941dded4b..018ba69a94 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -258,8 +258,8 @@ public class DeepCopyVisitor extends AbstractSqlppQueryExpressionVisitor<ILangEx
 
     @Override
     public Query visit(Query q, Void arg) throws CompilationException {
-        Query copy =
-                new Query(q.isExplain(), q.isTopLevel(), (Expression) q.getBody().accept(this, arg), q.getVarCounter());
+        Query copy = new Query(q.isExplain(), q.isAdvise(), q.isTopLevel(), (Expression) q.getBody().accept(this, arg),
+                q.getVarCounter());
         copy.setSourceLocation(q.getSourceLocation());
         return copy;
     }
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppDeleteRewriteVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppDeleteRewriteVisitor.java
index 62e4d23719..72e9b450b2 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppDeleteRewriteVisitor.java
+++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/SqlppDeleteRewriteVisitor.java
@@ -96,7 +96,7 @@ public class SqlppDeleteRewriteVisitor extends AbstractSqlppAstVisitor<Void, Met
         selectSetOperation.setSourceLocation(var.getSourceLocation());
         SelectExpression selectExpression = new SelectExpression(null, selectSetOperation, null, null, false);
         selectExpression.setSourceLocation(var.getSourceLocation());
-        Query query = new Query(false, false, selectExpression, 0);
+        Query query = new Query(false, false, false, selectExpression, 0);
         query.setBody(selectExpression);
         query.setSourceLocation(deleteStmt.getSourceLocation());
 
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 34f5ba0af0..cbd1f490ae 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -276,6 +276,7 @@ class SQLPPParser extends ScopeChecker implements IParser {
     private static final String RETURNS = "RETURNS";
     private static final String CONFIG = "CONFIG";
     private static final String STATISTICS = "STATISTICS";
+    private static final String ADVISE = "ADVISE";
 
 
     private static final String INT_TYPE_NAME = "int";
@@ -973,7 +974,7 @@ List<Statement> Statement() throws ParseException:
 }
 {
   (
-    (stmt = ExplainStatement()
+    (stmt = ExplainOrAdviseStatement()
       {
         decls.add(stmt);
       }
@@ -986,16 +987,21 @@ List<Statement> Statement() throws ParseException:
   }
 }
 
-Statement ExplainStatement() throws ParseException:
+Statement ExplainOrAdviseStatement() throws ParseException:
 {
   Statement stmt = null;
   Token explainToken = null;
+  boolean adviseToken = false;
 }
 {
-   ( <EXPLAIN> { explainToken = token; } )?
+( <EXPLAIN> { explainToken = token; } | LOOKAHEAD({ laIdentifier(ADVISE) }) <IDENTIFIER> { adviseToken = true; } )?
    stmt = SingleStatement()
    {
-      if (explainToken != null) {
+        if (explainToken != null && adviseToken ) {
+            throw new SqlppParseException(getSourceLocation(explainToken),
+            "EXPLAIN and ADVISE cannot be used together");
+        }
+      if (explainToken != null ) {
         if (stmt.getKind() == Statement.Kind.QUERY) {
           ((Query)stmt).setExplain(true);
         } else {
@@ -1003,6 +1009,14 @@ Statement ExplainStatement() throws ParseException:
             "EXPLAIN is not supported for this kind of statement");
         }
       }
+      if (adviseToken ) {
+        if (stmt.getKind() == Statement.Kind.QUERY) {
+          ((Query)stmt).setAdvise(true);
+        } else {
+          throw new SqlppParseException(getSourceLocation(token),
+            "ADVISE is not supported for statement kind: "+ stmt.getKind());
+        }
+      }
       return stmt;
    }
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/IIndexProvider.java
similarity index 57%
copy from hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
copy to asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/IIndexProvider.java
index 07a80349a7..5e4593244b 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/IIndexProvider.java
@@ -16,15 +16,18 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.hyracks.algebricks.compiler.api;
+package org.apache.asterix.metadata.declared;
 
-import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
-import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
-import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
-import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
+import java.util.List;
 
-public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+import org.apache.asterix.common.metadata.DataverseName;
+import org.apache.asterix.metadata.entities.Index;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 
-    ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
+public interface IIndexProvider {
+    Index getIndex(String database, DataverseName dataverseName, String datasetName, String indexName)
+            throws AlgebricksException;
+
+    List<Index> getDatasetIndexes(String database, DataverseName dataverseName, String datasetName)
+            throws AlgebricksException;
 }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
index b0291375b2..464f6d8319 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
@@ -177,7 +177,7 @@ import org.apache.hyracks.storage.am.rtree.dataflow.RTreeSearchOperatorDescripto
 import org.apache.hyracks.storage.common.IStorageManager;
 import org.apache.hyracks.storage.common.projection.ITupleProjectorFactory;
 
-public class MetadataProvider implements IMetadataProvider<DataSourceId, String> {
+public class MetadataProvider implements IMetadataProvider<DataSourceId, String>, IIndexProvider {
 
     private final ICcApplicationContext appCtx;
     private final IStorageComponentProvider storageComponentProvider;
@@ -463,11 +463,13 @@ public class MetadataProvider implements IMetadataProvider<DataSourceId, String>
         return index != null ? new DataSourceIndex(index, database, dataverseName, datasetName, this) : null;
     }
 
+    @Override
     public Index getIndex(String database, DataverseName dataverseName, String datasetName, String indexName)
             throws AlgebricksException {
         return MetadataManager.INSTANCE.getIndex(mdTxnCtx, database, dataverseName, datasetName, indexName);
     }
 
+    @Override
     public List<Index> getDatasetIndexes(String database, DataverseName dataverseName, String datasetName)
             throws AlgebricksException {
         return MetadataManagerUtil.getDatasetIndexes(mdTxnCtx, database, dataverseName, datasetName);
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/AbstractCompilerFactoryBuilder.java b/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/AbstractCompilerFactoryBuilder.java
index 3a2fe3bbbd..b2b223e95e 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/AbstractCompilerFactoryBuilder.java
+++ b/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/AbstractCompilerFactoryBuilder.java
@@ -57,6 +57,7 @@ public abstract class AbstractCompilerFactoryBuilder {
     protected Supplier<List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>>> logicalRewrites;
     protected Function<IRuleSetKind, List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>>> logicalRewritesByKind;
     protected Supplier<List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>>> physicalRewrites;
+    protected Function<IRuleSetKind, List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>>> physicalRewritesByKind;
     protected ITypeTraitProvider typeTraitProvider;
     protected ISerializerDeserializerProvider serializerDeserializerProvider;
     protected IBinaryHashFunctionFactoryProvider hashFunctionFactoryProvider;
@@ -101,6 +102,11 @@ public abstract class AbstractCompilerFactoryBuilder {
         this.physicalRewrites = physicalRewrites;
     }
 
+    public void setPhysicalRewritesByKind(
+            Function<IRuleSetKind, List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>>> physicalRewritesByKind) {
+        this.physicalRewritesByKind = physicalRewritesByKind;
+    }
+
     public void setTypeTraitProvider(ITypeTraitProvider typeTraitProvider) {
         this.typeTraitProvider = typeTraitProvider;
     }
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/HeuristicCompilerFactoryBuilder.java b/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/HeuristicCompilerFactoryBuilder.java
index a44e1be35e..a994fa3e31 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/HeuristicCompilerFactoryBuilder.java
+++ b/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/HeuristicCompilerFactoryBuilder.java
@@ -26,6 +26,7 @@ import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.common.utils.Pair;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.IndexAdvisor;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IConflictingTypeResolver;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionEvalSizeComputer;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionTypeComputer;
@@ -66,12 +67,13 @@ public class HeuristicCompilerFactoryBuilder extends AbstractCompilerFactoryBuil
                 IMergeAggregationExpressionFactory mergeAggregationExpressionFactory,
                 IExpressionTypeComputer expressionTypeComputer, IMissableTypeComputer missableTypeComputer,
                 IConflictingTypeResolver conflictingTypeResolver, PhysicalOptimizationConfig physicalOptimizationConfig,
-                AlgebricksPartitionConstraint clusterLocations, IWarningCollector warningCollector) {
+                AlgebricksPartitionConstraint clusterLocations, IWarningCollector warningCollector,
+                IndexAdvisor indexAdvisor) {
             IPlanPrettyPrinter prettyPrinter = PlanPrettyPrinter.createStringPlanPrettyPrinter();
             return new AlgebricksOptimizationContext(this, varCounter, expressionEvalSizeComputer,
                     mergeAggregationExpressionFactory, expressionTypeComputer, missableTypeComputer,
                     conflictingTypeResolver, physicalOptimizationConfig, clusterLocations, prettyPrinter,
-                    warningCollector);
+                    warningCollector, indexAdvisor);
         }
 
         @Override
@@ -97,15 +99,16 @@ public class HeuristicCompilerFactoryBuilder extends AbstractCompilerFactoryBuil
 
     private class CompilerFactoryImpl implements ICompilerFactory {
         @Override
-        public ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter) {
-            IOptimizationContext optContext =
-                    optCtxFactory.createOptimizationContext(varCounter, expressionEvalSizeComputer,
-                            mergeAggregationExpressionFactory, expressionTypeComputer, missableTypeComputer,
-                            conflictingTypeResolver, physicalOptimizationConfig, clusterLocations, warningCollector);
+        public ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter,
+                IRuleSetKind ruleSetKind, IndexAdvisor indexAdvisor) {
+            IOptimizationContext optContext = optCtxFactory.createOptimizationContext(varCounter,
+                    expressionEvalSizeComputer, mergeAggregationExpressionFactory, expressionTypeComputer,
+                    missableTypeComputer, conflictingTypeResolver, physicalOptimizationConfig, clusterLocations,
+                    warningCollector, indexAdvisor);
             optContext.setMetadataDeclarations(metadata);
             optContext.setCompilerFactory(this);
-            return new CompilerImpl(this, plan, optContext, logicalRewrites.get(), physicalRewrites.get(),
-                    writerFactory);
+            return new CompilerImpl(this, plan, optContext, logicalRewritesByKind.apply(ruleSetKind),
+                    physicalRewritesByKind.apply(ruleSetKind), writerFactory);
         }
 
         @Override
diff --git a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java b/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
index 07a80349a7..dbf45cc609 100644
--- a/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
+++ b/hyracks-fullstack/algebricks/algebricks-compiler/src/main/java/org/apache/hyracks/algebricks/compiler/api/ICompilerFactory.java
@@ -20,11 +20,13 @@ package org.apache.hyracks.algebricks.compiler.api;
 
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalPlan;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.IndexAdvisor;
 import org.apache.hyracks.algebricks.core.algebra.metadata.IMetadataProvider;
 import org.apache.hyracks.algebricks.core.rewriter.base.IRuleSetKind;
 
 public interface ICompilerFactory {
-    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter);
+    ICompiler createCompiler(ILogicalPlan plan, IMetadataProvider<?, ?> metadata, int varCounter,
+            IRuleSetKind ruleSetKind, IndexAdvisor indexAdvisor);
 
     ICompiler createCompiler(ILogicalPlan plan, IOptimizationContext newOptContext, IRuleSetKind ruleSetKind);
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IOptimizationContext.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IOptimizationContext.java
index 10b04516f1..78cbf7f0c5 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IOptimizationContext.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IOptimizationContext.java
@@ -104,4 +104,7 @@ public interface IOptimizationContext extends ITypingContext, IVariableContext {
     default boolean skipJobCapacityAssignment() {
         return false;
     }
+
+    IndexAdvisor getIndexAdvisor();
+
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IndexAdvisor.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IndexAdvisor.java
new file mode 100644
index 0000000000..d381afab43
--- /dev/null
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/base/IndexAdvisor.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.hyracks.algebricks.core.algebra.base;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class IndexAdvisor {
+    private final boolean isAdvise;
+    private Object fakeIndexProvider;
+    private final List<Advise> recommendedAdvise;
+    private final List<Advise> presentAdvise;
+    private static final JsonNode template;
+
+    static {
+        try {
+            template = new ObjectMapper().readTree("""
+                      [
+                                      {
+                                          "#operator": "Advise",
+                                          "advice": {
+                                              "#operator": "IndexAdvice",
+                                              "adviseinfo": {
+                                                  "current_indexes": [ ],
+                                                  "recommended_indexes": {
+                                                      "indexes": [ ]
+                                                  }
+                                              }
+                                          }
+                                      }
+                     ]
+                    """);
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public IndexAdvisor(boolean isAdvise) {
+        this.isAdvise = isAdvise;
+        recommendedAdvise = new ArrayList<>();
+        presentAdvise = new ArrayList<>();
+    }
+
+    public boolean getAdvise() {
+        return isAdvise;
+    }
+
+    public void addRecommendedAdvise(String indexName, List<List<String>> keyFieldNames, String databaseName,
+            String dataverseName, String datasetName) {
+        recommendedAdvise.add(new Advise(indexName, keyFieldNames, databaseName, dataverseName, datasetName));
+    }
+
+    public void addPresentAdvise(String indexName, List<List<String>> keyFieldNames, String databaseName,
+            String dataverseName, String datasetName) {
+        presentAdvise.add(new Advise(indexName, keyFieldNames, databaseName, dataverseName, datasetName));
+    }
+
+    public void setFakeIndexProvider(Object fakeIndexProvider) {
+        this.fakeIndexProvider = fakeIndexProvider;
+    }
+
+    public Object getFakeIndexProvider() {
+        return fakeIndexProvider;
+    }
+
+    public JsonNode getResultJson() {
+
+        JsonNode resultJson = template.deepCopy();
+        ArrayNode currentIndexNode =
+                (ArrayNode) resultJson.path(0).path("advice").path("adviseinfo").path("current_indexes");
+        for (Advise advise : presentAdvise) {
+            ObjectNode indexNode = JsonNodeFactory.instance.objectNode();
+            indexNode.put("index_statement", advise.getCreateIndexClause());
+            currentIndexNode.add(indexNode);
+        }
+
+        ArrayNode recommendedIndexNode = (ArrayNode) resultJson.path(0).path("advice").path("adviseinfo")
+                .path("recommended_indexes").path("indexes");
+
+        for (Advise advise : recommendedAdvise) {
+            ObjectNode indexNode = JsonNodeFactory.instance.objectNode();
+            indexNode.put("index_statement", advise.getCreateIndexClause());
+            recommendedIndexNode.add(indexNode);
+        }
+        return resultJson; // Placeholder for actual implementation
+    }
+
+    static public class Advise {
+        private final String indexName;
+        private final List<List<String>> keyFieldNames;
+        private final String databaseName;
+        private final String dataverseName;
+        private final String datasetName;
+
+        public Advise(String indexName, List<List<String>> keyFieldNames, String databaseName, String dataverseName,
+                String datasetName) {
+            this.indexName = indexName;
+            this.keyFieldNames = keyFieldNames;
+            this.databaseName = databaseName;
+            this.dataverseName = dataverseName;
+            this.datasetName = datasetName;
+        }
+
+        public String getIndexName() {
+            return indexName;
+        }
+
+        public String getCreateIndexClause() {
+
+            return "CREATE INDEX " + indexName + " ON `" + databaseName + "`.`" + dataverseName + "`.`" + datasetName
+                    + "`" + getKeyFieldNamesClause() + ";";
+        }
+
+        public String getKeyFieldNamesClause() {
+            return keyFieldNames.stream()
+                    .map(fields -> fields.stream().map(s -> "`" + s + "`").collect(Collectors.joining(".")))
+                    .collect(Collectors.joining(",", "(", ")"));
+
+        }
+
+    }
+
+}
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/AlgebricksOptimizationContext.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/AlgebricksOptimizationContext.java
index bee842ce70..ae9767867b 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/AlgebricksOptimizationContext.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/AlgebricksOptimizationContext.java
@@ -29,6 +29,7 @@ import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.base.EquivalenceClass;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.IndexAdvisor;
 import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IConflictingTypeResolver;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionEvalSizeComputer;
@@ -81,6 +82,7 @@ public class AlgebricksOptimizationContext implements IOptimizationContext {
     private int varCounter;
     private IMetadataProvider metadataProvider;
     private Object compilerFactory;
+    private IndexAdvisor indexAdvisor;
 
     public AlgebricksOptimizationContext(IOptimizationContextFactory optContextFactory, int varCounter,
             IExpressionEvalSizeComputer expressionEvalSizeComputer,
@@ -88,7 +90,7 @@ public class AlgebricksOptimizationContext implements IOptimizationContext {
             IExpressionTypeComputer expressionTypeComputer, IMissableTypeComputer nullableTypeComputer,
             IConflictingTypeResolver conflictingTypeResovler, PhysicalOptimizationConfig physicalOptimizationConfig,
             AlgebricksPartitionConstraint clusterLocations, IPlanPrettyPrinter prettyPrinter,
-            IWarningCollector warningCollector) {
+            IWarningCollector warningCollector, IndexAdvisor indexAdvisor) {
         this.optContextFactory = optContextFactory;
         this.varCounter = varCounter;
         this.expressionEvalSizeComputer = expressionEvalSizeComputer;
@@ -103,6 +105,7 @@ public class AlgebricksOptimizationContext implements IOptimizationContext {
         boolean isSanityCheckEnabled = physicalOptimizationConfig.isSanityCheckEnabled();
         this.planStructureVerifier = isSanityCheckEnabled ? new PlanStructureVerifier(prettyPrinter, this) : null;
         this.planStabilityVerifier = isSanityCheckEnabled ? new PlanStabilityVerifier(prettyPrinter) : null;
+        this.indexAdvisor = indexAdvisor;
     }
 
     public AlgebricksOptimizationContext(AlgebricksOptimizationContext from) {
@@ -122,6 +125,7 @@ public class AlgebricksOptimizationContext implements IOptimizationContext {
         planStabilityVerifier = isSanityCheckEnabled ? new PlanStabilityVerifier(from.prettyPrinter) : null;
         metadataProvider = from.metadataProvider;
         compilerFactory = from.compilerFactory;
+        indexAdvisor = from.indexAdvisor;
 
         varEvalSizeEnv.varSizeMap.putAll(from.varEvalSizeEnv.varSizeMap);
         typeEnvMap.putAll(from.typeEnvMap);
@@ -403,4 +407,10 @@ public class AlgebricksOptimizationContext implements IOptimizationContext {
             return varSizeMap.get(var);
         }
     }
+
+    @Override
+    public IndexAdvisor getIndexAdvisor() {
+        return indexAdvisor;
+    }
+
 }
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/IOptimizationContextFactory.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/IOptimizationContextFactory.java
index c81025d82e..770aad59f8 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/IOptimizationContextFactory.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/rewriter/base/IOptimizationContextFactory.java
@@ -20,6 +20,7 @@ package org.apache.hyracks.algebricks.core.rewriter.base;
 
 import org.apache.hyracks.algebricks.common.constraints.AlgebricksPartitionConstraint;
 import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.IndexAdvisor;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IConflictingTypeResolver;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionEvalSizeComputer;
 import org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionTypeComputer;
@@ -33,7 +34,8 @@ public interface IOptimizationContextFactory {
             IMergeAggregationExpressionFactory mergeAggregationExpressionFactory,
             IExpressionTypeComputer expressionTypeComputer, IMissableTypeComputer missableTypeComputer,
             IConflictingTypeResolver conflictintTypeResolver, PhysicalOptimizationConfig physicalOptimizationConfig,
-            AlgebricksPartitionConstraint clusterLocations, IWarningCollector warningCollector);
+            AlgebricksPartitionConstraint clusterLocations, IWarningCollector warningCollector,
+            IndexAdvisor indexAdvisor);
 
     IOptimizationContext cloneOptimizationContext(IOptimizationContext oc);
 }