Taewoo Kim has uploaded a new change for review.

  https://asterix-gerrit.ics.uci.edu/637

Change subject: Added LeftOuterUnnestMap operator.
......................................................................

Added LeftOuterUnnestMap operator.

 - Added LeftOuterUnnestMap operator to represent the left-outer-join semantics 
properly.

Change-Id: I6760319c2d3ff90c8b7d8ddeea3d9dd8f743366b
---
M 
asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/BTreeSearchPOperator.java
M 
asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/InvertedIndexPOperator.java
M 
asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/RTreeSearchPOperator.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixPhysicalOperatorsRule.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/OptimizableOperatorSubTree.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineLeftNtsInSubplanJoinFlatteningVisitor.java
M 
asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanSpecialFlatteningCheckVisitor.java
13 files changed, 312 insertions(+), 60 deletions(-)


  git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb 
refs/changes/37/637/1

diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/BTreeSearchPOperator.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/BTreeSearchPOperator.java
index da7d9c6..bd515d9 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/BTreeSearchPOperator.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/BTreeSearchPOperator.java
@@ -34,6 +34,7 @@
 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.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.base.PhysicalOperatorTag;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
@@ -41,6 +42,7 @@
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.core.algebra.metadata.IDataSourceIndex;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IOperatorSchema;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder.OrderKind;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
@@ -94,8 +96,30 @@
     public void contributeRuntimeOperator(IHyracksJobBuilder builder, 
JobGenContext context, ILogicalOperator op,
             IOperatorSchema opSchema, IOperatorSchema[] inputSchemas, 
IOperatorSchema outerPlanSchema)
             throws AlgebricksException {
-        UnnestMapOperator unnestMap = (UnnestMapOperator) op;
-        ILogicalExpression unnestExpr = 
unnestMap.getExpressionRef().getValue();
+               // We have two types of unnest-map operator - UNNEST_MAP and
+               // LEFT_OUTER_UNNEST_MAP
+               UnnestMapOperator unnestMapOp = null;
+               LeftOuterUnnestMapOperator leftOuterUnnestMapOp = null;
+               ILogicalOperator unnestMap = null;
+               ILogicalExpression unnestExpr = null;
+               List<LogicalVariable> minFilterVarList = null;
+               List<LogicalVariable> maxFilterVarList = null;
+               List<LogicalVariable> outputVars = null;
+               if (op.getOperatorTag() == LogicalOperatorTag.UNNEST_MAP) {
+                       unnestMapOp = (UnnestMapOperator) op;
+                       unnestExpr = unnestMapOp.getExpressionRef().getValue();
+                       minFilterVarList = unnestMapOp.getMinFilterVars();
+                       maxFilterVarList = unnestMapOp.getMaxFilterVars();
+                       outputVars = unnestMapOp.getVariables();
+                       unnestMap = unnestMapOp;
+               } else if (op.getOperatorTag() == 
LogicalOperatorTag.LEFT_OUTER_UNNEST_MAP) {
+                       leftOuterUnnestMapOp = (LeftOuterUnnestMapOperator) op;
+                       unnestExpr = 
leftOuterUnnestMapOp.getExpressionRef().getValue();
+                       minFilterVarList = 
leftOuterUnnestMapOp.getMinFilterVars();
+                       maxFilterVarList = 
leftOuterUnnestMapOp.getMaxFilterVars();
+                       outputVars = leftOuterUnnestMapOp.getVariables();
+                       unnestMap = leftOuterUnnestMapOp;
+               }
         if (unnestExpr.getExpressionTag() != 
LogicalExpressionTag.FUNCTION_CALL) {
             throw new IllegalStateException();
         }
@@ -109,13 +133,14 @@
         int[] lowKeyIndexes = getKeyIndexes(jobGenParams.getLowKeyVarList(), 
inputSchemas);
         int[] highKeyIndexes = getKeyIndexes(jobGenParams.getHighKeyVarList(), 
inputSchemas);
 
-        int[] minFilterFieldIndexes = 
getKeyIndexes(unnestMap.getMinFilterVars(), inputSchemas);
-        int[] maxFilterFieldIndexes = 
getKeyIndexes(unnestMap.getMaxFilterVars(), inputSchemas);
+               int[] minFilterFieldIndexes = getKeyIndexes(minFilterVarList,
+                               inputSchemas);
+               int[] maxFilterFieldIndexes = getKeyIndexes(maxFilterVarList,
+                               inputSchemas);
 
         AqlMetadataProvider metadataProvider = (AqlMetadataProvider) 
context.getMetadataProvider();
         Dataset dataset = 
metadataProvider.findDataset(jobGenParams.getDataverseName(), 
jobGenParams.getDatasetName());
         IVariableTypeEnvironment typeEnv = context.getTypeEnvironment(op);
-        List<LogicalVariable> outputVars = unnestMap.getVariables();
         if (jobGenParams.getRetainInput()) {
             outputVars = new ArrayList<LogicalVariable>();
             VariableUtilities.getLiveVariables(unnestMap, outputVars);
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/InvertedIndexPOperator.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/InvertedIndexPOperator.java
index 0e5850b..7373caf 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/InvertedIndexPOperator.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/InvertedIndexPOperator.java
@@ -53,6 +53,7 @@
 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.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.base.PhysicalOperatorTag;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
@@ -60,6 +61,7 @@
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
 import org.apache.hyracks.algebricks.core.algebra.metadata.IDataSourceIndex;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IOperatorSchema;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import org.apache.hyracks.algebricks.core.jobgen.impl.JobGenContext;
@@ -107,8 +109,30 @@
     public void contributeRuntimeOperator(IHyracksJobBuilder builder, 
JobGenContext context, ILogicalOperator op,
             IOperatorSchema opSchema, IOperatorSchema[] inputSchemas, 
IOperatorSchema outerPlanSchema)
                     throws AlgebricksException {
-        UnnestMapOperator unnestMapOp = (UnnestMapOperator) op;
-        ILogicalExpression unnestExpr = 
unnestMapOp.getExpressionRef().getValue();
+               // We have two types of unnest-map operator - UNNEST_MAP and
+               // LEFT_OUTER_UNNEST_MAP
+               UnnestMapOperator unnestMapOp = null;
+               LeftOuterUnnestMapOperator leftOuterUnnestMapOp = null;
+               ILogicalOperator unnestMap = null;
+               ILogicalExpression unnestExpr = null;
+               List<LogicalVariable> minFilterVarList = null;
+               List<LogicalVariable> maxFilterVarList = null;
+               List<LogicalVariable> outputVars = null;
+               if (op.getOperatorTag() == LogicalOperatorTag.UNNEST_MAP) {
+                       unnestMapOp = (UnnestMapOperator) op;
+                       unnestExpr = unnestMapOp.getExpressionRef().getValue();
+                       minFilterVarList = unnestMapOp.getMinFilterVars();
+                       maxFilterVarList = unnestMapOp.getMaxFilterVars();
+                       outputVars = unnestMapOp.getVariables();
+                       unnestMap = unnestMapOp;
+               } else if (op.getOperatorTag() == 
LogicalOperatorTag.LEFT_OUTER_UNNEST_MAP) {
+                       leftOuterUnnestMapOp = (LeftOuterUnnestMapOperator) op;
+                       unnestExpr = 
leftOuterUnnestMapOp.getExpressionRef().getValue();
+                       minFilterVarList = 
leftOuterUnnestMapOp.getMinFilterVars();
+                       maxFilterVarList = 
leftOuterUnnestMapOp.getMaxFilterVars();
+                       outputVars = leftOuterUnnestMapOp.getVariables();
+                       unnestMap = leftOuterUnnestMapOp;
+               }
         if (unnestExpr.getExpressionTag() != 
LogicalExpressionTag.FUNCTION_CALL) {
             throw new IllegalStateException();
         }
@@ -129,25 +153,29 @@
         }
         int[] keyIndexes = getKeyIndexes(jobGenParams.getKeyVarList(), 
inputSchemas);
 
-        int[] minFilterFieldIndexes = 
getKeyIndexes(unnestMapOp.getMinFilterVars(), inputSchemas);
-        int[] maxFilterFieldIndexes = 
getKeyIndexes(unnestMapOp.getMaxFilterVars(), inputSchemas);
+               int[] minFilterFieldIndexes = getKeyIndexes(minFilterVarList,
+                               inputSchemas);
+               int[] maxFilterFieldIndexes = getKeyIndexes(maxFilterVarList,
+                               inputSchemas);
         // Build runtime.
         Pair<IOperatorDescriptor, AlgebricksPartitionConstraint> 
invIndexSearch = buildInvertedIndexRuntime(
-                metadataProvider, context, builder.getJobSpec(), unnestMapOp, 
opSchema, jobGenParams.getRetainInput(),
+                               metadataProvider, context, 
builder.getJobSpec(), unnestMap,
+                               opSchema, jobGenParams.getRetainInput(),
                 jobGenParams.getRetainNull(), jobGenParams.getDatasetName(), 
dataset, jobGenParams.getIndexName(),
                 jobGenParams.getSearchKeyType(), keyIndexes, 
jobGenParams.getSearchModifierType(),
                 jobGenParams.getSimilarityThreshold(), minFilterFieldIndexes, 
maxFilterFieldIndexes);
 
         // Contribute operator in hyracks job.
-        builder.contributeHyracksOperator(unnestMapOp, invIndexSearch.first);
+               builder.contributeHyracksOperator(unnestMap, 
invIndexSearch.first);
         builder.contributeAlgebricksPartitionConstraint(invIndexSearch.first, 
invIndexSearch.second);
-        ILogicalOperator srcExchange = 
unnestMapOp.getInputs().get(0).getValue();
-        builder.contributeGraphEdge(srcExchange, 0, unnestMapOp, 0);
+               ILogicalOperator srcExchange = 
unnestMap.getInputs().get(0).getValue();
+               builder.contributeGraphEdge(srcExchange, 0, unnestMap, 0);
     }
 
     public Pair<IOperatorDescriptor, AlgebricksPartitionConstraint> 
buildInvertedIndexRuntime(
             AqlMetadataProvider metadataProvider, JobGenContext context, 
JobSpecification jobSpec,
-            UnnestMapOperator unnestMap, IOperatorSchema opSchema, boolean 
retainInput, boolean retainNull,
+ ILogicalOperator unnestMap,
+                       IOperatorSchema opSchema, boolean retainInput, boolean 
retainNull,
             String datasetName, Dataset dataset, String indexName, ATypeTag 
searchKeyType, int[] keyFields,
             SearchModifierType searchModifierType, IAlgebricksConstantValue 
similarityThreshold,
             int[] minFilterFieldIndexes, int[] maxFilterFieldIndexes) throws 
AlgebricksException {
@@ -201,7 +229,15 @@
             }
 
             IVariableTypeEnvironment typeEnv = 
context.getTypeEnvironment(unnestMap);
-            List<LogicalVariable> outputVars = unnestMap.getVariables();
+                       List<LogicalVariable> outputVars = null;
+
+                       if (unnestMap.getOperatorTag() == 
LogicalOperatorTag.LEFT_OUTER_UNNEST_MAP) {
+                               outputVars = ((LeftOuterUnnestMapOperator) 
unnestMap)
+                                               .getVariables();
+                       } else if (unnestMap.getOperatorTag() == 
LogicalOperatorTag.UNNEST_MAP) {
+                               outputVars = ((UnnestMapOperator) 
unnestMap).getVariables();
+                       }
+
             if (retainInput) {
                 outputVars = new ArrayList<LogicalVariable>();
                 VariableUtilities.getLiveVariables(unnestMap, outputVars);
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/RTreeSearchPOperator.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/RTreeSearchPOperator.java
index d54d7f4..042c49b 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/RTreeSearchPOperator.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/algebra/operators/physical/RTreeSearchPOperator.java
@@ -33,6 +33,7 @@
 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.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.base.PhysicalOperatorTag;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
@@ -40,6 +41,7 @@
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import org.apache.hyracks.algebricks.core.algebra.metadata.IDataSourceIndex;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IOperatorSchema;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import org.apache.hyracks.algebricks.core.jobgen.impl.JobGenContext;
@@ -64,8 +66,30 @@
     public void contributeRuntimeOperator(IHyracksJobBuilder builder, 
JobGenContext context, ILogicalOperator op,
             IOperatorSchema opSchema, IOperatorSchema[] inputSchemas, 
IOperatorSchema outerPlanSchema)
             throws AlgebricksException {
-        UnnestMapOperator unnestMap = (UnnestMapOperator) op;
-        ILogicalExpression unnestExpr = 
unnestMap.getExpressionRef().getValue();
+               // We have two types of unnest-map operator - UNNEST_MAP and
+               // LEFT_OUTER_UNNEST_MAP
+               UnnestMapOperator unnestMapOp = null;
+               LeftOuterUnnestMapOperator leftOuterUnnestMapOp = null;
+               ILogicalOperator unnestMap = null;
+               ILogicalExpression unnestExpr = null;
+               List<LogicalVariable> minFilterVarList = null;
+               List<LogicalVariable> maxFilterVarList = null;
+               List<LogicalVariable> outputVars = null;
+               if (op.getOperatorTag() == LogicalOperatorTag.UNNEST_MAP) {
+                       unnestMapOp = (UnnestMapOperator) op;
+                       unnestExpr = unnestMapOp.getExpressionRef().getValue();
+                       minFilterVarList = unnestMapOp.getMinFilterVars();
+                       maxFilterVarList = unnestMapOp.getMaxFilterVars();
+                       outputVars = unnestMapOp.getVariables();
+                       unnestMap = unnestMapOp;
+               } else if (op.getOperatorTag() == 
LogicalOperatorTag.LEFT_OUTER_UNNEST_MAP) {
+                       leftOuterUnnestMapOp = (LeftOuterUnnestMapOperator) op;
+                       unnestExpr = 
leftOuterUnnestMapOp.getExpressionRef().getValue();
+                       minFilterVarList = 
leftOuterUnnestMapOp.getMinFilterVars();
+                       maxFilterVarList = 
leftOuterUnnestMapOp.getMaxFilterVars();
+                       outputVars = leftOuterUnnestMapOp.getVariables();
+                       unnestMap = leftOuterUnnestMapOp;
+               }
         if (unnestExpr.getExpressionTag() != 
LogicalExpressionTag.FUNCTION_CALL) {
             throw new IllegalStateException();
         }
@@ -78,13 +102,14 @@
         jobGenParams.readFromFuncArgs(unnestFuncExpr.getArguments());
         int[] keyIndexes = getKeyIndexes(jobGenParams.getKeyVarList(), 
inputSchemas);
 
-        int[] minFilterFieldIndexes = 
getKeyIndexes(unnestMap.getMinFilterVars(), inputSchemas);
-        int[] maxFilterFieldIndexes = 
getKeyIndexes(unnestMap.getMaxFilterVars(), inputSchemas);
+               int[] minFilterFieldIndexes = getKeyIndexes(minFilterVarList,
+                               inputSchemas);
+               int[] maxFilterFieldIndexes = getKeyIndexes(maxFilterVarList,
+                               inputSchemas);
 
         AqlMetadataProvider mp = (AqlMetadataProvider) 
context.getMetadataProvider();
         Dataset dataset = mp.findDataset(jobGenParams.getDataverseName(), 
jobGenParams.getDatasetName());
         IVariableTypeEnvironment typeEnv = 
context.getTypeEnvironment(unnestMap);
-        List<LogicalVariable> outputVars = unnestMap.getVariables();
         if (jobGenParams.getRetainInput()) {
             outputVars = new ArrayList<LogicalVariable>();
             VariableUtilities.getLiveVariables(unnestMap, outputVars);
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixPhysicalOperatorsRule.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixPhysicalOperatorsRule.java
index 353c9be..b4cb57e 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixPhysicalOperatorsRule.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SetAsterixPhysicalOperatorsRule.java
@@ -21,9 +21,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.commons.lang3.mutable.Mutable;
-import org.apache.commons.lang3.mutable.MutableObject;
-
 import org.apache.asterix.algebra.operators.physical.BTreeSearchPOperator;
 import org.apache.asterix.algebra.operators.physical.InvertedIndexPOperator;
 import org.apache.asterix.algebra.operators.physical.RTreeSearchPOperator;
@@ -33,6 +30,8 @@
 import org.apache.asterix.om.functions.AsterixBuiltinFunctions;
 import org.apache.asterix.optimizer.rules.am.AccessMethodJobGenParams;
 import org.apache.asterix.optimizer.rules.am.BTreeJobGenParams;
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.commons.lang3.mutable.MutableObject;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.common.exceptions.NotImplementedException;
 import org.apache.hyracks.algebricks.common.utils.Pair;
@@ -56,6 +55,7 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.physical.ExternalGroupByPOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.physical.PreclusteredGroupByPOperator;
@@ -198,9 +198,16 @@
                     
JoinUtils.setJoinAlgorithmAndExchangeAlgo((LeftOuterJoinOperator) op, context);
                     break;
                 }
-                case UNNEST_MAP: {
-                    UnnestMapOperator unnestMap = (UnnestMapOperator) op;
-                    ILogicalExpression unnestExpr = 
unnestMap.getExpressionRef().getValue();
+                       case UNNEST_MAP:
+                       case LEFT_OUTER_UNNEST_MAP: {
+                               ILogicalExpression unnestExpr = null;
+                               if (op.getOperatorTag() == 
LogicalOperatorTag.UNNEST_MAP) {
+                                       unnestExpr = ((UnnestMapOperator) 
op).getExpressionRef()
+                                                       .getValue();
+                               } else if (op.getOperatorTag() == 
LogicalOperatorTag.LEFT_OUTER_UNNEST_MAP) {
+                                       unnestExpr = 
((LeftOuterUnnestMapOperator) op)
+                                                       
.getExpressionRef().getValue();
+                               }
                     if (unnestExpr.getExpressionTag() == 
LogicalExpressionTag.FUNCTION_CALL) {
                         AbstractFunctionCallExpression f = 
(AbstractFunctionCallExpression) unnestExpr;
                         FunctionIdentifier fid = f.getFunctionIdentifier();
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java
index 238eaa3..23c5dca 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/SweepIllegalNonfunctionalFunctions.java
@@ -44,6 +44,7 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InsertDeleteUpsertOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IntersectOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.MaterializeOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
@@ -253,6 +254,11 @@
         public Void visitUnnestMapOperator(UnnestMapOperator op, Void arg) 
throws AlgebricksException {
             return null;
         }
+        
+        @Override
+        public Void visitLeftOuterUnnestMapOperator(LeftOuterUnnestMapOperator 
op, Void arg) throws AlgebricksException {
+            return null;
+        }
 
         @Override
         public Void visitDataScanOperator(DataSourceScanOperator op, Void arg) 
throws AlgebricksException {
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
index 9165506..f43f8ce 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AccessMethodUtils.java
@@ -71,6 +71,7 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator.ExecutionMode;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ExternalDataLookupOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.GroupByOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.OrderOperator.IOrder;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
@@ -307,7 +308,17 @@
             numPrimaryKeys = DatasetUtils.getPartitioningKeys(dataset).size();
         }
         List<LogicalVariable> primaryKeyVars = new 
ArrayList<LogicalVariable>();
-        List<LogicalVariable> sourceVars = ((UnnestMapOperator) 
unnestMapOp).getVariables();
+               List<LogicalVariable> sourceVars = null;
+
+               // For a left outer join case, LEFT_OUTER_UNNEST_MAP operator 
is placed
+               // instead of UNNEST_MAP operator.
+               if (unnestMapOp.getOperatorTag() == 
LogicalOperatorTag.UNNEST_MAP) {
+                       sourceVars = ((UnnestMapOperator) 
unnestMapOp).getVariables();
+               } else if (unnestMapOp.getOperatorTag() == 
LogicalOperatorTag.LEFT_OUTER_UNNEST_MAP) {
+                       sourceVars = ((LeftOuterUnnestMapOperator) unnestMapOp)
+                                       .getVariables();
+               }
+
         // Assumes the primary keys are located at the end.
         int start = sourceVars.size() - numPrimaryKeys;
         int stop = sourceVars.size();
@@ -321,7 +332,16 @@
             ILogicalOperator unnestMapOp) {
         int numPrimaryKeys = DatasetUtils.getPartitioningKeys(dataset).size();
         List<LogicalVariable> primaryKeyVars = new 
ArrayList<LogicalVariable>();
-        List<LogicalVariable> sourceVars = ((UnnestMapOperator) 
unnestMapOp).getVariables();
+               List<LogicalVariable> sourceVars = null;
+
+               // For a left outer join case, LEFT_OUTER_UNNEST_MAP operator 
is placed
+               // instead of UNNEST_MAP operator.
+               if (unnestMapOp.getOperatorTag() == 
LogicalOperatorTag.UNNEST_MAP) {
+                       sourceVars = ((UnnestMapOperator) 
unnestMapOp).getVariables();
+               } else if (unnestMapOp.getOperatorTag() == 
LogicalOperatorTag.LEFT_OUTER_UNNEST_MAP) {
+                       sourceVars = ((LeftOuterUnnestMapOperator) unnestMapOp)
+                                       .getVariables();
+               }
         // Assumes the primary keys are located at the beginning.
         for (int i = 0; i < numPrimaryKeys; i++) {
             primaryKeyVars.add(sourceVars.get(i));
@@ -423,7 +443,7 @@
         return indexExprs.get(0).second;
     }
 
-    public static UnnestMapOperator createSecondaryIndexUnnestMap(Dataset 
dataset, ARecordType recordType, Index index,
+    public static ILogicalOperator createSecondaryIndexUnnestMap(Dataset 
dataset, ARecordType recordType, Index index,
             ILogicalOperator inputOp, AccessMethodJobGenParams jobGenParams, 
IOptimizationContext context,
             boolean outputPrimaryKeysOnly, boolean retainInput) throws 
AlgebricksException {
         // The job gen parameters are transferred to the actual job gen via 
the UnnestMapOperator's function arguments.
@@ -443,14 +463,33 @@
         secondaryIndexSearchFunc.setReturnsUniqueValues(true);
         // This is the operator that jobgen will be looking for. It contains 
an unnest function that has all necessary arguments to determine
         // which index to use, which variables contain the index-search keys, 
what is the original dataset, etc.
-        UnnestMapOperator secondaryIndexUnnestOp = new 
UnnestMapOperator(secondaryIndexUnnestVars,
-                new 
MutableObject<ILogicalExpression>(secondaryIndexSearchFunc), 
secondaryIndexOutputTypes,
-                retainInput);
-        secondaryIndexUnnestOp.getInputs().add(new 
MutableObject<ILogicalOperator>(inputOp));
-        
context.computeAndSetTypeEnvironmentForOperator(secondaryIndexUnnestOp);
-        secondaryIndexUnnestOp.setExecutionMode(ExecutionMode.PARTITIONED);
-        return secondaryIndexUnnestOp;
-    }
+
+        // Left-outer-join (retainInput and retainNull) case?
+        // Then, we use the LEFT-OUTER-UNNEST-MAP operator instead of 
unnest-map operator.
+        if (jobGenParams.getRetainNull()) {
+            if (retainInput) {
+                LeftOuterUnnestMapOperator secondaryIndexLeftOuterUnnestOp = 
new LeftOuterUnnestMapOperator(
+                        secondaryIndexUnnestVars, new 
MutableObject<ILogicalExpression>(secondaryIndexSearchFunc),
+                        secondaryIndexOutputTypes);
+                secondaryIndexLeftOuterUnnestOp.getInputs().add(new 
MutableObject<ILogicalOperator>(inputOp));
+                
context.computeAndSetTypeEnvironmentForOperator(secondaryIndexLeftOuterUnnestOp);
+                
secondaryIndexLeftOuterUnnestOp.setExecutionMode(ExecutionMode.PARTITIONED);
+                return secondaryIndexLeftOuterUnnestOp;
+            } else {
+                // Left-outer-join without retainNull and retainInput doesn't 
make sense.
+                throw new AlgebricksException("Left-outer-join should 
propagate all inputs from the outer branch.");
+            }
+        } else {
+            // If this is not a left-outer-join case, then we use UNNEST-MAP 
operator.
+            UnnestMapOperator secondaryIndexUnnestOp = new 
UnnestMapOperator(secondaryIndexUnnestVars,
+                    new 
MutableObject<ILogicalExpression>(secondaryIndexSearchFunc), 
secondaryIndexOutputTypes,
+                    retainInput);
+            secondaryIndexUnnestOp.getInputs().add(new 
MutableObject<ILogicalOperator>(inputOp));
+            
context.computeAndSetTypeEnvironmentForOperator(secondaryIndexUnnestOp);
+            secondaryIndexUnnestOp.setExecutionMode(ExecutionMode.PARTITIONED);
+            return secondaryIndexUnnestOp;
+               }
+       }
 
     public static UnnestMapOperator 
createPrimaryIndexUnnestMap(AbstractDataSourceOperator dataSourceOp,
             Dataset dataset, ARecordType recordType, ILogicalOperator inputOp, 
IOptimizationContext context,
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
index 370b90d..b1356f7 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/BTreeAccessMethod.java
@@ -34,6 +34,7 @@
 import org.apache.asterix.lang.common.util.FunctionUtil;
 import org.apache.asterix.metadata.entities.Dataset;
 import org.apache.asterix.metadata.entities.Index;
+import org.apache.asterix.om.functions.AsterixBuiltinFunctions;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.optimizer.rules.util.EquivalenceClassUtils;
 import org.apache.commons.lang3.mutable.Mutable;
@@ -49,6 +50,7 @@
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.IndexedNLJoinExpressionAnnotation;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
 import 
org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
 import 
org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions.ComparisonKind;
@@ -59,8 +61,8 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator.ExecutionMode;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator;
-import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ExternalDataLookupOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.util.OperatorManipulationUtil;
@@ -481,11 +483,15 @@
             inputOp = probeSubTree.root;
         }
 
-        UnnestMapOperator secondaryIndexUnnestOp = 
AccessMethodUtils.createSecondaryIndexUnnestMap(dataset, recordType,
+               ILogicalOperator secondaryIndexUnnestOp = AccessMethodUtils
+                               .createSecondaryIndexUnnestMap(dataset, 
recordType,
                 chosenIndex, inputOp, jobGenParams, context, false, 
retainInput);
 
         // Generate the rest of the upstream plan which feeds the search 
results into the primary index.
         UnnestMapOperator primaryIndexUnnestOp = null;
+               LeftOuterUnnestMapOperator leftOuterPrimaryIndexUnnestOp = null;
+               ILogicalOperator finalPrimaryIndexUnnestOp = null;
+
         boolean isPrimaryIndex = chosenIndex.isPrimaryIndex();
         if (dataset.getDatasetType() == DatasetType.EXTERNAL) {
             // External dataset
@@ -494,12 +500,15 @@
                     retainNull);
             return externalDataAccessOp;
         } else if (!isPrimaryIndex) {
-            primaryIndexUnnestOp = 
AccessMethodUtils.createPrimaryIndexUnnestMap(dataSourceOp, dataset, recordType,
+                       finalPrimaryIndexUnnestOp = AccessMethodUtils
+                                       
.createPrimaryIndexUnnestMap(dataSourceOp, dataset,
+                                                       recordType,
                     secondaryIndexUnnestOp, context, true, retainInput, 
retainNull, false);
 
             // Adds equivalence classes --- one equivalent class between a 
primary key
             // variable and a record field-access expression.
-            
EquivalenceClassUtils.addEquivalenceClassesForPrimaryIndexAccess(primaryIndexUnnestOp,
+                       
EquivalenceClassUtils.addEquivalenceClassesForPrimaryIndexAccess(
+                                       finalPrimaryIndexUnnestOp,
                     dataSourceOp.getVariables(), recordType, dataset, context);
         } else {
             List<Object> primaryIndexOutputTypes = new ArrayList<Object>();
@@ -508,30 +517,89 @@
             } catch (IOException e) {
                 throw new AlgebricksException(e);
             }
-            List<LogicalVariable> scanVariables = dataSourceOp.getVariables();
-            primaryIndexUnnestOp = new UnnestMapOperator(scanVariables, 
secondaryIndexUnnestOp.getExpressionRef(),
-                    primaryIndexOutputTypes, retainInput);
-            primaryIndexUnnestOp.getInputs().add(new 
MutableObject<ILogicalOperator>(inputOp));
 
-            if (!primaryIndexPostProccessingIsNeeded) {
-                List<Mutable<ILogicalExpression>> remainingFuncExprs = new 
ArrayList<Mutable<ILogicalExpression>>();
-                getNewConditionExprs(conditionRef, replacedFuncExprs, 
remainingFuncExprs);
-                // Generate new condition.
-                if (!remainingFuncExprs.isEmpty()) {
-                    ILogicalExpression pulledCond = 
createSelectCondition(remainingFuncExprs);
-                    conditionRef.setValue(pulledCond);
+                       List<LogicalVariable> scanVariables = 
dataSourceOp.getVariables();
+
+                       // Checks whether the primary index search can replace 
the given
+                       // SELECT condition.
+                       // If so, condition will be set to null and eventually 
the SELECT
+                       // operator will be removed.
+                       // If not, we create a new condition based on remaining 
ones.
+                       if (!primaryIndexPostProccessingIsNeeded) {
+                               List<Mutable<ILogicalExpression>> 
remainingFuncExprs = new ArrayList<Mutable<ILogicalExpression>>();
+                               getNewConditionExprs(conditionRef, 
replacedFuncExprs,
+                                               remainingFuncExprs);
+                               // Generate new condition.
+                               if (!remainingFuncExprs.isEmpty()) {
+                                       ILogicalExpression pulledCond = 
createSelectCondition(remainingFuncExprs);
+                                       conditionRef.setValue(pulledCond);
+                               } else {
+                                       conditionRef.setValue(null);
+                               }
+                       }
+
+                       // Checks whether LEFT_OUTER_UNNESTMAP operator is 
required.
+            boolean leftOuterUnnestMapRequired = false;
+            if (retainNull && retainInput) {
+                leftOuterUnnestMapRequired = true;
+            } else {
+                leftOuterUnnestMapRequired = false;
+            }
+
+                       if (conditionRef.getValue() != null) {
+                               // The job gen parameters are transferred to 
the actual job gen
+                               // via the UnnestMapOperator's function 
arguments.
+                               ArrayList<Mutable<ILogicalExpression>> 
primaryIndexFuncArgs = new ArrayList<Mutable<ILogicalExpression>>();
+                               
jobGenParams.writeToFuncArgs(primaryIndexFuncArgs);
+                               // An index search is expressed as an 
unnest-map over an
+                               // index-search function.
+                               IFunctionInfo primaryIndexSearch = FunctionUtil
+                                               
.getFunctionInfo(AsterixBuiltinFunctions.INDEX_SEARCH);
+                               UnnestingFunctionCallExpression 
primaryIndexSearchFunc = new UnnestingFunctionCallExpression(
+                                               primaryIndexSearch, 
primaryIndexFuncArgs);
+                               
primaryIndexSearchFunc.setReturnsUniqueValues(true);
+                               if (!leftOuterUnnestMapRequired) {
+                                       primaryIndexUnnestOp = new 
UnnestMapOperator(scanVariables,
+                                                       new 
MutableObject<ILogicalExpression>(
+                                                                       
primaryIndexSearchFunc),
+                                                       
primaryIndexOutputTypes, retainInput);
+                                       finalPrimaryIndexUnnestOp = 
primaryIndexUnnestOp;
+                               } else {
+                                       leftOuterPrimaryIndexUnnestOp = new 
LeftOuterUnnestMapOperator(
+                                                       scanVariables,
+                                                       new 
MutableObject<ILogicalExpression>(
+                                                                       
primaryIndexSearchFunc),
+                                                       
primaryIndexOutputTypes);
+                                       finalPrimaryIndexUnnestOp = 
leftOuterPrimaryIndexUnnestOp;
+                               }
+                       } else {
+                               if (!leftOuterUnnestMapRequired) {
+                                       primaryIndexUnnestOp = new 
UnnestMapOperator(scanVariables,
+                                                       ((UnnestMapOperator) 
secondaryIndexUnnestOp)
+                                                                       
.getExpressionRef(),
+                                                       
primaryIndexOutputTypes, retainInput);
+                                       finalPrimaryIndexUnnestOp = 
primaryIndexUnnestOp;
                 } else {
-                    conditionRef.setValue(null);
+                                       leftOuterPrimaryIndexUnnestOp = new 
LeftOuterUnnestMapOperator(
+                                                       scanVariables,
+                                                       
((LeftOuterUnnestMapOperator) secondaryIndexUnnestOp)
+                                                                       
.getExpressionRef(),
+                                                       
primaryIndexOutputTypes);
+                                       finalPrimaryIndexUnnestOp = 
leftOuterPrimaryIndexUnnestOp;
                 }
             }
 
+                       finalPrimaryIndexUnnestOp.getInputs().add(
+                                       new 
MutableObject<ILogicalOperator>(inputOp));
+
             // Adds equivalence classes --- one equivalent class between a 
primary key
             // variable and a record field-access expression.
-            
EquivalenceClassUtils.addEquivalenceClassesForPrimaryIndexAccess(primaryIndexUnnestOp,
 scanVariables,
-                    recordType, dataset, context);
-        }
+                       
EquivalenceClassUtils.addEquivalenceClassesForPrimaryIndexAccess(
+                                       finalPrimaryIndexUnnestOp, 
scanVariables, recordType,
+                                       dataset, context);
+               }
 
-        return primaryIndexUnnestOp;
+               return finalPrimaryIndexUnnestOp;
     }
 
     private int createKeyVarsAndExprs(int numKeys, LimitType[] keyLimits, 
ILogicalExpression[] searchKeyExprs,
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
index d8e4c6a..735c8c5 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/InvertedIndexAccessMethod.java
@@ -66,7 +66,6 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator.ExecutionMode;
 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;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.ReplicateOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
@@ -406,7 +405,8 @@
             inputOp = (AbstractLogicalOperator) probeSubTree.root;
         }
         jobGenParams.setKeyVarList(keyVarList);
-        UnnestMapOperator secondaryIndexUnnestOp = 
AccessMethodUtils.createSecondaryIndexUnnestMap(dataset, recordType,
+               ILogicalOperator secondaryIndexUnnestOp = AccessMethodUtils
+                               .createSecondaryIndexUnnestMap(dataset, 
recordType,
                 chosenIndex, inputOp, jobGenParams, context, true, 
retainInput);
 
         // Generate the rest of the upstream plan which feeds the search 
results into the primary index.
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/OptimizableOperatorSubTree.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/OptimizableOperatorSubTree.java
index 52582ba..5cb0f1e 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/OptimizableOperatorSubTree.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/OptimizableOperatorSubTree.java
@@ -82,8 +82,32 @@
         rootRef = subTreeOpRef;
         root = subTreeOpRef.getValue();
         // Examine the op's children to match the expected patterns.
+               // Pattern: limit? <- (assign | unnest)+ <- order? <- select <- 
(assign
+               // | unnest)+ <- datasource?
         AbstractLogicalOperator subTreeOp = (AbstractLogicalOperator) 
subTreeOpRef.getValue();
         do {
+                       // Skip LIMIT if one is present.
+                       if (subTreeOp.getOperatorTag() == 
LogicalOperatorTag.LIMIT) {
+                               subTreeOpRef = subTreeOp.getInputs().get(0);
+                               subTreeOp = (AbstractLogicalOperator) 
subTreeOpRef.getValue();
+                               // subTreeOp = (AbstractLogicalOperator)
+                               // subTreeOp.getInputs().get(0).getValue();
+                       }
+                       // Match (assign | unnest)+.
+                       while (subTreeOp.getOperatorTag() == 
LogicalOperatorTag.ASSIGN
+                                       || subTreeOp.getOperatorTag() == 
LogicalOperatorTag.UNNEST) {
+                               assignsAndUnnestsRefs.add(subTreeOpRef);
+                               assignsAndUnnests.add(subTreeOp);
+
+                               subTreeOpRef = subTreeOp.getInputs().get(0);
+                               subTreeOp = (AbstractLogicalOperator) 
subTreeOpRef.getValue();
+                       }
+                       ;
+                       // Skip ORDER operator if one is present.
+                       if (subTreeOp.getOperatorTag() == 
LogicalOperatorTag.ORDER) {
+                               subTreeOpRef = subTreeOp.getInputs().get(0);
+                               subTreeOp = (AbstractLogicalOperator) 
subTreeOpRef.getValue();
+                       }
             // Skip select operator.
             if (subTreeOp.getOperatorTag() == LogicalOperatorTag.SELECT) {
                 subTreeOpRef = subTreeOp.getInputs().get(0);
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java
index c5bd8af..2b2b8ea 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/RTreeAccessMethod.java
@@ -239,7 +239,8 @@
             assignSearchKeys.getInputs().add(probeSubTree.rootRef);
         }
 
-        UnnestMapOperator secondaryIndexUnnestOp = 
AccessMethodUtils.createSecondaryIndexUnnestMap(dataset, recordType,
+               ILogicalOperator secondaryIndexUnnestOp = AccessMethodUtils
+                               .createSecondaryIndexUnnestMap(dataset, 
recordType,
                 chosenIndex, assignSearchKeys, jobGenParams, context, false, 
retainInput);
 
         // Generate the rest of the upstream plan which feeds the search 
results into the primary index.
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
index 0715d46..a558a2c 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineAllNtsInSubplanVisitor.java
@@ -59,6 +59,7 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IntersectOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.MaterializeOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
@@ -566,6 +567,13 @@
     }
 
     @Override
+    public ILogicalOperator 
visitLeftOuterUnnestMapOperator(LeftOuterUnnestMapOperator op, Void arg)
+            throws AlgebricksException {
+        // This operator always propagates all inputs.
+        return visitSingleInputOperator(op);
+    }
+    
+    @Override
     public ILogicalOperator visitDataScanOperator(DataSourceScanOperator op, 
Void arg) throws AlgebricksException {
         return visitSingleInputOperator(op);
     }
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineLeftNtsInSubplanJoinFlatteningVisitor.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineLeftNtsInSubplanJoinFlatteningVisitor.java
index 5aaa59f..b9a8bfd 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineLeftNtsInSubplanJoinFlatteningVisitor.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/InlineLeftNtsInSubplanJoinFlatteningVisitor.java
@@ -47,6 +47,7 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IntersectOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.MaterializeOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
@@ -336,6 +337,12 @@
         }
         return op;
     }
+    
+    @Override
+    public ILogicalOperator 
visitLeftOuterUnnestMapOperator(LeftOuterUnnestMapOperator op, Void arg)
+            throws AlgebricksException {
+        return visitSingleInputOperator(op);
+    }
 
     @Override
     public ILogicalOperator visitDataScanOperator(DataSourceScanOperator op, 
Void arg) throws AlgebricksException {
diff --git 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanSpecialFlatteningCheckVisitor.java
 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanSpecialFlatteningCheckVisitor.java
index c691365..8c32ae3 100644
--- 
a/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanSpecialFlatteningCheckVisitor.java
+++ 
b/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/subplan/SubplanSpecialFlatteningCheckVisitor.java
@@ -33,6 +33,7 @@
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.IntersectOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterJoinOperator;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LeftOuterUnnestMapOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.LimitOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.MaterializeOperator;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.NestedTupleSourceOperator;
@@ -203,6 +204,11 @@
     public Boolean visitUnnestMapOperator(UnnestMapOperator op, Void arg) 
throws AlgebricksException {
         return false;
     }
+    
+    @Override
+    public Boolean visitLeftOuterUnnestMapOperator(LeftOuterUnnestMapOperator 
op, Void arg) throws AlgebricksException {
+        return false;
+    }
 
     @Override
     public Boolean visitDataScanOperator(DataSourceScanOperator op, Void arg) 
throws AlgebricksException {

-- 
To view, visit https://asterix-gerrit.ics.uci.edu/637
To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I6760319c2d3ff90c8b7d8ddeea3d9dd8f743366b
Gerrit-PatchSet: 1
Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-Owner: Taewoo Kim <[email protected]>

Reply via email to