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

wyk 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 5f2549e9f3 [ASTERIXDB-3229][COMP] Part 2: Intoduce Pushdown Processor
5f2549e9f3 is described below

commit 5f2549e9f3f99140d88617801cb3b5094b81c662
Author: Wail Alkowaileet <[email protected]>
AuthorDate: Mon Jul 31 08:50:09 2023 -0700

    [ASTERIXDB-3229][COMP] Part 2: Intoduce Pushdown Processor
    
    - user model changes: no
    - storage format changes: no
    - interface changes: no
    
    Details:
    This patch adds the pushdown processors for columnar filters.
    
    Change-Id: Id1f1b231a293abb1006ba4e906d64294c882b32d
    Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/17664
    Integration-Tests: Jenkins <[email protected]>
    Tested-by: Jenkins <[email protected]>
    Reviewed-by: Wail Alkowaileet <[email protected]>
    Reviewed-by: Ali Alsuliman <[email protected]>
---
 .../ExpressionValueAccessPushdownVisitor.java      |  36 +---
 .../pushdown/ExpressionValueFilterPushdown.java    |   6 +-
 .../OperatorValueAccessPushdownVisitor.java        |   4 +
 .../processor/AbstractFilterPushdownProcessor.java | 218 +++++++++++++++++++++
 .../processor/AbstractPushdownProcessor.java       |  32 +++
 .../processor/ColumnFilterPushdownProcessor.java   | 163 +++++++++++++++
 .../ColumnRangeFilterPushdownProcessor.java        | 147 ++++++++++++++
 .../pushdown/processor/IPushdownProcessor.java     |  25 +++
 .../InlineFilterExpressionsProcessor.java          |  95 +++++++++
 .../{ => schema}/ExpectedSchemaBuilder.java        |  59 +++---
 .../pushdown/schema/ObjectExpectedSchemaNode.java  |   2 +-
 .../ColumnFilterPathBuilderVisitor.java            |  13 +-
 .../ExpressionToExpectedSchemaNodeVisitor.java     | 126 ++++++++++++
 .../asterix/metadata/utils/PushdownUtil.java       | 179 +++++++++++++++++
 .../utils/filter/ArrayPathCheckerVisitor.java      |   2 +-
 15 files changed, 1034 insertions(+), 73 deletions(-)

diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueAccessPushdownVisitor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueAccessPushdownVisitor.java
index b71dcb4307..c57f653c7f 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueAccessPushdownVisitor.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueAccessPushdownVisitor.java
@@ -18,11 +18,12 @@
  */
 package org.apache.asterix.optimizer.rules.pushdown;
 
-import java.util.HashSet;
+import static org.apache.asterix.metadata.utils.PushdownUtil.ALLOWED_FUNCTIONS;
+import static 
org.apache.asterix.metadata.utils.PushdownUtil.SUPPORTED_FUNCTIONS;
+
 import java.util.List;
-import java.util.Set;
 
-import org.apache.asterix.om.functions.BuiltinFunctions;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaBuilder;
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
 import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
@@ -30,19 +31,10 @@ import 
org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
 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.IVariableTypeEnvironment;
-import 
org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
-import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
 import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionReferenceTransform;
 
 class ExpressionValueAccessPushdownVisitor implements 
ILogicalExpressionReferenceTransform {
-    //Set of allowed functions that can request a type in its entirety
-    static final Set<FunctionIdentifier> ALLOWED_FUNCTIONS = 
createAllowedFunctions();
-    //Set of supported array functions
-    static final Set<FunctionIdentifier> ARRAY_FUNCTIONS = 
createSupportedArrayFunctions();
-    //Set of supported functions that we can push down
-    static final Set<FunctionIdentifier> SUPPORTED_FUNCTIONS = 
createSupportedFunctions();
-
     private final ExpectedSchemaBuilder builder;
     private List<LogicalVariable> producedVariables;
     private IVariableTypeEnvironment typeEnv;
@@ -169,24 +161,4 @@ class ExpressionValueAccessPushdownVisitor implements 
ILogicalExpressionReferenc
             builder.unregisterVariable(variable);
         }
     }
-
-    private static Set<FunctionIdentifier> createSupportedArrayFunctions() {
-        return Set.of(BuiltinFunctions.GET_ITEM, BuiltinFunctions.ARRAY_STAR, 
BuiltinFunctions.SCAN_COLLECTION);
-    }
-
-    private static Set<FunctionIdentifier> createSupportedFunctions() {
-        Set<FunctionIdentifier> supportedFunctions = new HashSet<>();
-        supportedFunctions.add(BuiltinFunctions.FIELD_ACCESS_BY_NAME);
-        supportedFunctions.add(BuiltinFunctions.FIELD_ACCESS_BY_INDEX);
-        supportedFunctions.addAll(ARRAY_FUNCTIONS);
-        return supportedFunctions;
-    }
-
-    private static Set<FunctionIdentifier> createAllowedFunctions() {
-        return Set.of(BuiltinFunctions.IS_ARRAY, BuiltinFunctions.IS_OBJECT, 
BuiltinFunctions.IS_ATOMIC,
-                BuiltinFunctions.IS_NUMBER, BuiltinFunctions.IS_BOOLEAN, 
BuiltinFunctions.IS_STRING,
-                AlgebricksBuiltinFunctions.IS_MISSING, 
AlgebricksBuiltinFunctions.IS_NULL, BuiltinFunctions.IS_UNKNOWN,
-                BuiltinFunctions.LT, BuiltinFunctions.LE, BuiltinFunctions.EQ, 
BuiltinFunctions.GT, BuiltinFunctions.GE,
-                BuiltinFunctions.SCALAR_SQL_COUNT);
-    }
 }
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueFilterPushdown.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueFilterPushdown.java
index bff8bfffa9..29a2465836 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueFilterPushdown.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpressionValueFilterPushdown.java
@@ -37,10 +37,12 @@ import org.apache.asterix.om.base.IAObject;
 import org.apache.asterix.om.constants.AsterixConstantValue;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.types.ARecordType;
+import 
org.apache.asterix.optimizer.rules.pushdown.processor.ColumnRangeFilterPushdownProcessor;
 import 
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
-import 
org.apache.asterix.optimizer.rules.pushdown.schema.ColumnFilterPathBuilderVisitor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaBuilder;
 import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaNodeType;
 import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.visitor.ColumnFilterPathBuilderVisitor;
 import org.apache.asterix.runtime.projection.FunctionCallInformation;
 import 
org.apache.asterix.runtime.projection.ProjectionFiltrationWarningFactoryProvider;
 import org.apache.commons.lang3.mutable.Mutable;
@@ -66,6 +68,8 @@ import org.apache.hyracks.api.exceptions.SourceLocation;
  * TODO Filter could prevent REPLICATE (i.e., we can scan a dataset twice due 
to the fact one scan is filtered and
  * TODO the other is not or both have different filters)
  * TODO part of this class could potentially be used for external data dynamic 
prefixes
+ *
+ * @deprecated use {@link ColumnRangeFilterPushdownProcessor} and {@link 
ColumnFilterPathBuilderVisitor}
  */
 class ExpressionValueFilterPushdown {
     private final ExpectedSchemaBuilder builder;
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/OperatorValueAccessPushdownVisitor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/OperatorValueAccessPushdownVisitor.java
index 9eb25d09da..8e49bbb2a9 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/OperatorValueAccessPushdownVisitor.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/OperatorValueAccessPushdownVisitor.java
@@ -38,6 +38,7 @@ import 
org.apache.asterix.metadata.entities.ExternalDatasetDetails;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.types.ARecordType;
 import org.apache.asterix.om.utils.ConstantExpressionUtil;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaBuilder;
 import 
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
 import org.apache.asterix.runtime.projection.FunctionCallInformation;
 import org.apache.commons.lang3.mutable.Mutable;
@@ -93,7 +94,10 @@ import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisit
 
 /**
  * This visitor visits the entire plan and tries to build the information of 
the required values from all dataset
+ *
+ * @deprecated use {@link 
org.apache.asterix.optimizer.rules.pushdown.visitor.PushdownOperatorVisitor}
  */
+@Deprecated
 public class OperatorValueAccessPushdownVisitor implements 
ILogicalOperatorVisitor<Void, Void> {
     private static final List<LogicalVariable> EMPTY_VARIABLES = 
Collections.emptyList();
 
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractFilterPushdownProcessor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractFilterPushdownProcessor.java
new file mode 100644
index 0000000000..4dbb06d754
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractFilterPushdownProcessor.java
@@ -0,0 +1,218 @@
+/*
+ * 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.pushdown.processor;
+
+import static org.apache.asterix.metadata.utils.PushdownUtil.getConstant;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isAnd;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isCompare;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isConstant;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isFilterPath;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
+import org.apache.asterix.optimizer.rules.pushdown.descriptor.DefineDescriptor;
+import 
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+import org.apache.asterix.optimizer.rules.pushdown.descriptor.UseDescriptor;
+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.LogicalExpressionTag;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+
+abstract class AbstractFilterPushdownProcessor extends 
AbstractPushdownProcessor {
+    private final Set<ILogicalOperator> visitedOperators;
+
+    public AbstractFilterPushdownProcessor(PushdownContext pushdownContext, 
IOptimizationContext context) {
+        super(pushdownContext, context);
+        visitedOperators = new HashSet<>();
+    }
+
+    @Override
+    public final void process() throws AlgebricksException {
+        List<ScanDefineDescriptor> scanDefineDescriptors = 
pushdownContext.getRegisteredScans();
+        for (ScanDefineDescriptor scanDefineDescriptor : 
scanDefineDescriptors) {
+            if (skip(scanDefineDescriptor)) {
+                continue;
+            }
+            prepareScan(scanDefineDescriptor);
+            pushdownFilter(scanDefineDescriptor, scanDefineDescriptor);
+        }
+    }
+
+    /**
+     * Should skip pushing down a filter for the given data-scan
+     *
+     * @param scanDefineDescriptor data-scan descriptor
+     * @return true to skip, false otherwise
+     */
+    protected abstract boolean skip(ScanDefineDescriptor scanDefineDescriptor);
+
+    /**
+     * Prepare data-scan for a pushdown
+     *
+     * @param scanDefineDescriptor data-scan descriptor
+     */
+    protected abstract void prepareScan(ScanDefineDescriptor 
scanDefineDescriptor);
+
+    /**
+     * Prepare to pushdown a SELECT expression in the use-descriptor
+     *
+     * @param useDescriptor contains the SELECT operator and its expression
+     */
+    protected abstract void preparePushdown(UseDescriptor useDescriptor) 
throws AlgebricksException;
+
+    /**
+     * Is an expression pushable
+     *
+     * @param expression the expression to push down
+     * @return true if it is pushable, false otherwise
+     */
+    protected abstract boolean isPushable(AbstractFunctionCallExpression 
expression);
+
+    /**
+     * Handle a compare function
+     *
+     * @param expression compare expression
+     * @return true if the pushdown should continue, false otherwise
+     */
+    protected abstract boolean handleCompare(AbstractFunctionCallExpression 
expression) throws AlgebricksException;
+
+    /**
+     * Handle a value access path expression
+     *
+     * @param expression path expression
+     * @return true if the pushdown should continue, false otherwise
+     */
+    protected abstract boolean handlePath(AbstractFunctionCallExpression 
expression) throws AlgebricksException;
+
+    /**
+     * Put the filter expression to data-scan
+     *
+     * @param scanDefineDescriptor data-scan descriptor
+     * @param inlinedExpr          inlined filter expression
+     * @return true if the filter expression was set to data-scan, false 
otherwise
+     */
+    protected abstract boolean putFilterInformation(ScanDefineDescriptor 
scanDefineDescriptor,
+            ILogicalExpression inlinedExpr) throws AlgebricksException;
+
+    private boolean pushdownFilter(DefineDescriptor defineDescriptor, 
ScanDefineDescriptor scanDefineDescriptor)
+            throws AlgebricksException {
+        List<UseDescriptor> useDescriptors = 
pushdownContext.getUseDescriptors(defineDescriptor);
+        for (UseDescriptor useDescriptor : useDescriptors) {
+            /*
+             * Pushdown works only if the scope(use) and scope(scan) are the 
same, as we cannot pushdown when
+             * scope(use) > scope(scan) (e.g., after join or group-by)
+             */
+            if (useDescriptor.getScope() == scanDefineDescriptor.getScope()
+                    && useDescriptor.getOperator().getOperatorTag() == 
LogicalOperatorTag.SELECT) {
+                if (!inlineAndPushdownFilter(useDescriptor, 
scanDefineDescriptor)) {
+                    return false;
+                }
+            }
+        }
+
+        for (UseDescriptor useDescriptor : useDescriptors) {
+            DefineDescriptor nextDefineDescriptor = 
pushdownContext.getDefineDescriptor(useDescriptor);
+            if (useDescriptor.getScope() == scanDefineDescriptor.getScope() && 
nextDefineDescriptor != null) {
+                if (!pushdownFilter(nextDefineDescriptor, 
scanDefineDescriptor)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    private boolean inlineAndPushdownFilter(UseDescriptor useDescriptor, 
ScanDefineDescriptor scanDefineDescriptor)
+            throws AlgebricksException {
+        ILogicalOperator selectOp = useDescriptor.getOperator();
+        if (visitedOperators.contains(selectOp)) {
+            // Skip and follow through to find any other selects that can be 
pushed down
+            return true;
+        }
+
+        // Get a clone of the SELECT expression and inline it
+        ILogicalExpression inlinedExpr = 
pushdownContext.cloneAndInlineExpression(useDescriptor);
+        // Prepare for pushdown
+        preparePushdown(useDescriptor);
+        boolean pushdown =
+                pushdownFilterExpression(inlinedExpr) && 
putFilterInformation(scanDefineDescriptor, inlinedExpr);
+
+        // Do not push down a select twice.
+        visitedOperators.add(selectOp);
+        return pushdown;
+    }
+
+    protected final boolean pushdownFilterExpression(ILogicalExpression 
expression) throws AlgebricksException {
+        boolean pushdown = false;
+        if (isConstant(expression)) {
+            IAObject constantValue = getConstant(expression);
+            // Only non-derived types are allowed
+            pushdown = !constantValue.getType().getTypeTag().isDerivedType();
+        } else if (isAnd(expression)) {
+            pushdown = handleAnd((AbstractFunctionCallExpression) expression);
+        } else if (isCompare(expression)) {
+            pushdown = handleCompare((AbstractFunctionCallExpression) 
expression);
+        } else if (isFilterPath(expression)) {
+            pushdown = handlePath((AbstractFunctionCallExpression) expression);
+        } else if (expression.getExpressionTag() == 
LogicalExpressionTag.FUNCTION_CALL) {
+            // All functions including OR
+            pushdown = handleFunction((AbstractFunctionCallExpression) 
expression);
+        }
+        // PK variable should have (pushdown = false) as we should not involve 
the PK (at least currently)
+        return pushdown;
+    }
+
+    private boolean handleAnd(AbstractFunctionCallExpression expression) 
throws AlgebricksException {
+        List<Mutable<ILogicalExpression>> args = expression.getArguments();
+        Iterator<Mutable<ILogicalExpression>> argIter = args.iterator();
+        while (argIter.hasNext()) {
+            ILogicalExpression arg = argIter.next().getValue();
+            // Allow for partial pushdown of AND operands
+            if (!pushdownFilterExpression(arg)) {
+                // Remove the expression that cannot be pushed down
+                argIter.remove();
+            }
+        }
+        return !args.isEmpty();
+    }
+
+    private boolean handleFunction(AbstractFunctionCallExpression expression) 
throws AlgebricksException {
+        if (!expression.getFunctionInfo().isFunctional() || 
!isPushable(expression)) {
+            return false;
+        }
+
+        for (Mutable<ILogicalExpression> argRef : expression.getArguments()) {
+            ILogicalExpression arg = argRef.getValue();
+            // Either all arguments are pushable or none
+            if (!pushdownFilterExpression(arg)) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractPushdownProcessor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractPushdownProcessor.java
new file mode 100644
index 0000000000..75917658a7
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/AbstractPushdownProcessor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.pushdown.processor;
+
+import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
+import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+
+abstract class AbstractPushdownProcessor implements IPushdownProcessor {
+    protected final PushdownContext pushdownContext;
+    protected final IOptimizationContext context;
+
+    AbstractPushdownProcessor(PushdownContext pushdownContext, 
IOptimizationContext context) {
+        this.pushdownContext = pushdownContext;
+        this.context = context;
+    }
+}
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnFilterPushdownProcessor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnFilterPushdownProcessor.java
new file mode 100644
index 0000000000..8db7969e59
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnFilterPushdownProcessor.java
@@ -0,0 +1,163 @@
+/*
+ * 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.pushdown.processor;
+
+import static 
org.apache.asterix.metadata.utils.PushdownUtil.RANGE_FILTER_PUSHABLE_FUNCTIONS;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isNestedFunction;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isTypeFunction;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.asterix.common.config.DatasetConfig;
+import org.apache.asterix.metadata.entities.Dataset;
+import org.apache.asterix.metadata.utils.DatasetUtil;
+import org.apache.asterix.metadata.utils.filter.ArrayPathCheckerVisitor;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
+import 
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+import org.apache.asterix.optimizer.rules.pushdown.descriptor.UseDescriptor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaNodeType;
+import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.visitor.ColumnFilterPathBuilderVisitor;
+import 
org.apache.asterix.optimizer.rules.pushdown.visitor.ExpressionToExpectedSchemaNodeVisitor;
+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.core.algebra.base.ILogicalExpression;
+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.expressions.AbstractFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
+
+/**
+ * Computes a filter expression that can be pushed down to {@link 
DatasetConfig.DatasetFormat#COLUMN} datasets.
+ * The computed filter expression then will be evaluated to determine if a 
record should be assembled and returned as
+ * a result of a data-scan or not.
+ */
+public class ColumnFilterPushdownProcessor extends 
AbstractFilterPushdownProcessor {
+    protected final ExpressionToExpectedSchemaNodeVisitor exprToNodeVisitor;
+    protected final ColumnFilterPathBuilderVisitor pathBuilderVisitor;
+    protected final Map<ILogicalExpression, ARecordType> paths;
+    private final ArrayPathCheckerVisitor checkerVisitor;
+
+    public ColumnFilterPushdownProcessor(PushdownContext pushdownContext, 
IOptimizationContext context) {
+        super(pushdownContext, context);
+        exprToNodeVisitor = new ExpressionToExpectedSchemaNodeVisitor();
+        pathBuilderVisitor = new ColumnFilterPathBuilderVisitor();
+        paths = new HashMap<>();
+        checkerVisitor = new ArrayPathCheckerVisitor();
+    }
+
+    @Override
+    protected boolean skip(ScanDefineDescriptor scanDefineDescriptor) {
+        Dataset dataset = scanDefineDescriptor.getDataset();
+        LogicalOperatorTag scanOpTag = 
scanDefineDescriptor.getOperator().getOperatorTag();
+        /*
+         * Only use the filter with data-scan. For index-search (unnest-map), 
this could be expensive as this
+         * requires to rewind the columnar readers for each point-lookup -- 
decoding 1000s of values for each
+         * point-lookup. Hence, the query should rely on the secondary-index 
filtration and not the columnar filter.
+         */
+        return scanOpTag != LogicalOperatorTag.DATASOURCESCAN
+                || dataset.getDatasetFormatInfo().getFormat() != 
DatasetConfig.DatasetFormat.COLUMN
+                || !DatasetUtil.isFilterPushdownSupported(dataset);
+    }
+
+    @Override
+    protected void prepareScan(ScanDefineDescriptor scanDefineDescriptor) {
+        exprToNodeVisitor.reset(scanDefineDescriptor);
+    }
+
+    @Override
+    protected void preparePushdown(UseDescriptor useDescriptor) throws 
AlgebricksException {
+        
exprToNodeVisitor.setTypeEnv(useDescriptor.getOperator().computeOutputTypeEnvironment(context));
+        paths.clear();
+    }
+
+    @Override
+    protected boolean isPushable(AbstractFunctionCallExpression expression) {
+        FunctionIdentifier fid = expression.getFunctionIdentifier();
+        return RANGE_FILTER_PUSHABLE_FUNCTIONS.contains(fid) || 
!isNestedFunction(fid) && !isTypeFunction(fid);
+    }
+
+    @Override
+    protected boolean handleCompare(AbstractFunctionCallExpression expression) 
throws AlgebricksException {
+        List<Mutable<ILogicalExpression>> args = expression.getArguments();
+
+        Mutable<ILogicalExpression> leftRef = args.get(0);
+        Mutable<ILogicalExpression> rightRef = args.get(1);
+
+        ILogicalExpression left = leftRef.getValue();
+        ILogicalExpression right = rightRef.getValue();
+
+        return pushdownFilterExpression(left) && 
pushdownFilterExpression(right);
+    }
+
+    @Override
+    protected boolean handlePath(AbstractFunctionCallExpression expression) 
throws AlgebricksException {
+        IExpectedSchemaNode node = expression.accept(exprToNodeVisitor, null);
+        if (node == null || node.getType() != ExpectedSchemaNodeType.ANY) {
+            return false;
+        }
+        paths.put(expression, 
pathBuilderVisitor.buildPath((AnyExpectedSchemaNode) node));
+        return true;
+    }
+
+    @Override
+    protected boolean putFilterInformation(ScanDefineDescriptor 
scanDefineDescriptor, ILogicalExpression inlinedExpr)
+            throws AlgebricksException {
+        ILogicalExpression filterExpr = 
scanDefineDescriptor.getFilterExpression();
+        if (filterExpr != null) {
+            filterExpr = orExpression(filterExpr, inlinedExpr);
+            scanDefineDescriptor.setFilterExpression(filterExpr);
+        } else {
+            scanDefineDescriptor.setFilterExpression(inlinedExpr);
+        }
+
+        if (checkerVisitor.containsMultipleArrayPaths(paths.values())) {
+            // Cannot pushdown a filter with multiple unnest
+            // TODO allow rewindable column readers for filters
+            // TODO this is a bit conservative (maybe too conservative) as we 
can push part of expression down
+            return false;
+        }
+
+        scanDefineDescriptor.getFilterPaths().putAll(paths);
+        return true;
+    }
+
+    protected final AbstractFunctionCallExpression 
orExpression(ILogicalExpression filterExpr,
+            ILogicalExpression inlinedExpr) {
+        AbstractFunctionCallExpression funcExpr = 
(AbstractFunctionCallExpression) filterExpr;
+        if (!BuiltinFunctions.AND.equals(funcExpr.getFunctionIdentifier())) {
+            IFunctionInfo fInfo = 
context.getMetadataProvider().lookupFunction(AlgebricksBuiltinFunctions.OR);
+            List<Mutable<ILogicalExpression>> args = new ArrayList<>();
+            args.add(new MutableObject<>(filterExpr));
+            funcExpr = new ScalarFunctionCallExpression(fInfo, args);
+        }
+        funcExpr.getArguments().add(new MutableObject<>(inlinedExpr));
+        return funcExpr;
+    }
+}
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnRangeFilterPushdownProcessor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnRangeFilterPushdownProcessor.java
new file mode 100644
index 0000000000..47db154818
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/ColumnRangeFilterPushdownProcessor.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.asterix.optimizer.rules.pushdown.processor;
+
+import static 
org.apache.asterix.metadata.utils.PushdownUtil.RANGE_FILTER_PUSHABLE_FUNCTIONS;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isConstant;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isFilterPath;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.asterix.common.config.DatasetConfig;
+import org.apache.asterix.metadata.entities.Dataset;
+import org.apache.asterix.metadata.utils.DatasetUtil;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
+import 
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+import org.apache.asterix.optimizer.rules.pushdown.descriptor.UseDescriptor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaNodeType;
+import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
+import org.apache.asterix.runtime.projection.FunctionCallInformation;
+import 
org.apache.asterix.runtime.projection.ProjectionFiltrationWarningFactoryProvider;
+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.IOptimizationContext;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+
+/**
+ * Computes a range-filter expression for {@link 
DatasetConfig.DatasetFormat#COLUMN} datasets. Each column is such
+ * dataset contains a pair of normalized min-max values. The range filter 
expression can be utilized to filter out
+ * mega leaf nodes that do not satisfy the range filter expression.
+ */
+public class ColumnRangeFilterPushdownProcessor extends 
ColumnFilterPushdownProcessor {
+    private final Map<String, FunctionCallInformation> sourceInformationMap;
+
+    public ColumnRangeFilterPushdownProcessor(PushdownContext pushdownContext, 
IOptimizationContext context) {
+        super(pushdownContext, context);
+        sourceInformationMap = new HashMap<>();
+    }
+
+    @Override
+    protected boolean skip(ScanDefineDescriptor scanDefineDescriptor) {
+        Dataset dataset = scanDefineDescriptor.getDataset();
+        return dataset.getDatasetFormatInfo().getFormat() != 
DatasetConfig.DatasetFormat.COLUMN
+                || !DatasetUtil.isRangeFilterPushdownSupported(dataset);
+    }
+
+    @Override
+    protected void preparePushdown(UseDescriptor useDescriptor) throws 
AlgebricksException {
+        super.preparePushdown(useDescriptor);
+        sourceInformationMap.clear();
+    }
+
+    @Override
+    protected boolean isPushable(AbstractFunctionCallExpression expression) {
+        return 
RANGE_FILTER_PUSHABLE_FUNCTIONS.contains(expression.getFunctionIdentifier());
+    }
+
+    @Override
+    protected boolean handleCompare(AbstractFunctionCallExpression expression) 
throws AlgebricksException {
+        List<Mutable<ILogicalExpression>> args = expression.getArguments();
+
+        Mutable<ILogicalExpression> leftRef = args.get(0);
+        Mutable<ILogicalExpression> rightRef = args.get(1);
+
+        ILogicalExpression left = leftRef.getValue();
+        ILogicalExpression right = rightRef.getValue();
+
+        if (isConstant(left) && isFilterPath(right)) {
+            return pushdownRangeFilter(right, left, expression, true);
+        } else if (isConstant(right) && isFilterPath(left)) {
+            return pushdownRangeFilter(left, right, expression, false);
+        }
+        // Either it is a compare that doesn't involve a constant there's a 
function that wraps the value access path
+        return false;
+    }
+
+    @Override
+    protected boolean handlePath(AbstractFunctionCallExpression expression) {
+        // This means we got something like WHERE $r.getField("isVerified") -- 
where isVerified is a boolean field.
+        // Boolean range filters are not supported currently
+        return false;
+    }
+
+    @Override
+    protected boolean putFilterInformation(ScanDefineDescriptor 
scanDefineDescriptor, ILogicalExpression inlinedExpr) {
+        ILogicalExpression filterExpr = 
scanDefineDescriptor.getRangeFilterExpression();
+        if (filterExpr != null) {
+            filterExpr = orExpression(filterExpr, inlinedExpr);
+            scanDefineDescriptor.setRangeFilterExpression(filterExpr);
+        } else {
+            scanDefineDescriptor.setRangeFilterExpression(inlinedExpr);
+        }
+        scanDefineDescriptor.getFilterPaths().putAll(paths);
+        scanDefineDescriptor.getPathLocations().putAll(sourceInformationMap);
+
+        return true;
+    }
+
+    private boolean pushdownRangeFilter(ILogicalExpression pathExpr, 
ILogicalExpression constExpr,
+            AbstractFunctionCallExpression funcExpr, boolean leftConstant) 
throws AlgebricksException {
+        AnyExpectedSchemaNode node = getNode(pathExpr);
+        IAObject constantValue = ((AsterixConstantValue) ((ConstantExpression) 
constExpr).getValue()).getObject();
+        if (node == null || 
constantValue.getType().getTypeTag().isDerivedType()) {
+            return false;
+        }
+        String functionName = funcExpr.getFunctionIdentifier().getName();
+        SourceLocation sourceLocation = funcExpr.getSourceLocation();
+        FunctionCallInformation functionCallInfo = new 
FunctionCallInformation(functionName, sourceLocation,
+                
ProjectionFiltrationWarningFactoryProvider.getIncomparableTypesFactory(leftConstant));
+        ARecordType path = pathBuilderVisitor.buildPath(node, constantValue, 
sourceInformationMap, functionCallInfo);
+        paths.put(pathExpr, path);
+        return true;
+    }
+
+    private AnyExpectedSchemaNode getNode(ILogicalExpression expression) 
throws AlgebricksException {
+        IExpectedSchemaNode node = expression.accept(exprToNodeVisitor, null);
+        if (node == null || node.getType() != ExpectedSchemaNodeType.ANY) {
+            return null;
+        }
+        return (AnyExpectedSchemaNode) node;
+    }
+
+}
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/IPushdownProcessor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/IPushdownProcessor.java
new file mode 100644
index 0000000000..d7c7a4042c
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/IPushdownProcessor.java
@@ -0,0 +1,25 @@
+/*
+ * 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.pushdown.processor;
+
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+
+public interface IPushdownProcessor {
+    void process() throws AlgebricksException;
+}
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/InlineFilterExpressionsProcessor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/InlineFilterExpressionsProcessor.java
new file mode 100644
index 0000000000..6cec455450
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/processor/InlineFilterExpressionsProcessor.java
@@ -0,0 +1,95 @@
+/*
+ * 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.pushdown.processor;
+
+import static org.apache.asterix.metadata.utils.PushdownUtil.isAnd;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isOr;
+import static org.apache.asterix.metadata.utils.PushdownUtil.isSameFunction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.asterix.optimizer.rules.pushdown.PushdownContext;
+import 
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+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.IOptimizationContext;
+import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+
+/**
+ * Inline filter expressions of a scan.
+ * E.g.,
+ * and(a > 2) --> a > 2
+ * and(a > 2, and(b > 2)) --> and(a > 2, b > 2)
+ */
+public class InlineFilterExpressionsProcessor extends 
AbstractPushdownProcessor {
+    public InlineFilterExpressionsProcessor(PushdownContext pushdownContext, 
IOptimizationContext context) {
+        super(pushdownContext, context);
+    }
+
+    @Override
+    public void process() throws AlgebricksException {
+        List<ScanDefineDescriptor> scanDefineDescriptors = 
pushdownContext.getRegisteredScans();
+        for (ScanDefineDescriptor scanDefineDescriptor : 
scanDefineDescriptors) {
+            
scanDefineDescriptor.setFilterExpression(inline(scanDefineDescriptor.getFilterExpression()));
+            
scanDefineDescriptor.setRangeFilterExpression(inline(scanDefineDescriptor.getRangeFilterExpression()));
+        }
+    }
+
+    private ILogicalExpression inline(ILogicalExpression expression) {
+        if (expression == null || expression.getExpressionTag() != 
LogicalExpressionTag.FUNCTION_CALL) {
+            return expression;
+        }
+
+        AbstractFunctionCallExpression funcExpr = 
(AbstractFunctionCallExpression) expression;
+        List<Mutable<ILogicalExpression>> args = funcExpr.getArguments();
+        if ((isAnd(funcExpr) || isOr(funcExpr)) && args.size() == 1) {
+            // Fix the incorrect AND with a single argument. This could happen 
if the AND expression is partially pushed
+            return inline(args.get(0).getValue());
+        }
+
+        if (isAnd(funcExpr) || isOr(funcExpr)) {
+            List<Mutable<ILogicalExpression>> inlinedArgs = new ArrayList<>();
+            for (Mutable<ILogicalExpression> argRef : args) {
+                // inline arg first
+                ILogicalExpression arg = inline(argRef.getValue());
+
+                if (isSameFunction(funcExpr, arg)) {
+                    // funcExpr is AND/OR and arg is also the same. Inline 
AND/OR by adding arg arguments to funcExpr
+                    AbstractFunctionCallExpression argFuncExpr = 
(AbstractFunctionCallExpression) arg;
+                    inlinedArgs.addAll(argFuncExpr.getArguments());
+                } else {
+                    // funcExpr and arg are different. Set the inlined arg to 
argRef (argRef has un-inlined version).
+                    argRef.setValue(arg);
+                    inlinedArgs.add(argRef);
+                }
+            }
+
+            // Clear the original argument
+            args.clear();
+            // Add the new inlined arguments
+            args.addAll(inlinedArgs);
+        }
+
+        // either the original expression or the inlined AND/OR expression
+        return expression;
+    }
+}
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpectedSchemaBuilder.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaBuilder.java
similarity index 83%
rename from 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpectedSchemaBuilder.java
rename to 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaBuilder.java
index edebed94b6..345b545146 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/ExpectedSchemaBuilder.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ExpectedSchemaBuilder.java
@@ -16,27 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.optimizer.rules.pushdown;
+package org.apache.asterix.optimizer.rules.pushdown.schema;
 
-import static 
org.apache.asterix.optimizer.rules.pushdown.ExpressionValueAccessPushdownVisitor.ARRAY_FUNCTIONS;
-import static 
org.apache.asterix.optimizer.rules.pushdown.ExpressionValueAccessPushdownVisitor.SUPPORTED_FUNCTIONS;
+import static org.apache.asterix.metadata.utils.PushdownUtil.ARRAY_FUNCTIONS;
+import static 
org.apache.asterix.metadata.utils.PushdownUtil.SUPPORTED_FUNCTIONS;
 
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.asterix.metadata.utils.PushdownUtil;
 import org.apache.asterix.om.functions.BuiltinFunctions;
 import org.apache.asterix.om.types.ARecordType;
-import org.apache.asterix.om.utils.ConstantExpressionUtil;
-import 
org.apache.asterix.optimizer.rules.pushdown.schema.AbstractComplexExpectedSchemaNode;
-import 
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
-import 
org.apache.asterix.optimizer.rules.pushdown.schema.ArrayExpectedSchemaNode;
-import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaNodeType;
-import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
-import 
org.apache.asterix.optimizer.rules.pushdown.schema.ObjectExpectedSchemaNode;
-import 
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
-import 
org.apache.asterix.optimizer.rules.pushdown.schema.UnionExpectedSchemaNode;
 import 
org.apache.asterix.optimizer.rules.pushdown.visitor.ExpectedSchemaNodeToIATypeTranslatorVisitor;
 import org.apache.asterix.runtime.projection.DataProjectionFiltrationInfo;
 import org.apache.asterix.runtime.projection.FunctionCallInformation;
@@ -140,7 +132,7 @@ public class ExpectedSchemaBuilder {
         return varToNode.get(variable);
     }
 
-    IExpectedSchemaNode getNode(ILogicalExpression expr) {
+    public IExpectedSchemaNode getNode(ILogicalExpression expr) {
         if (expr.getExpressionTag() == LogicalExpressionTag.VARIABLE) {
             return getNode(VariableUtilities.getVariable(expr));
         }
@@ -210,7 +202,7 @@ public class ExpectedSchemaBuilder {
         return newNode;
     }
 
-    private void addChild(AbstractFunctionCallExpression parentExpr, 
IVariableTypeEnvironment typeEnv,
+    public static void addChild(AbstractFunctionCallExpression parentExpr, 
IVariableTypeEnvironment typeEnv,
             AbstractComplexExpectedSchemaNode parent, IExpectedSchemaNode 
child) throws AlgebricksException {
         switch (parent.getType()) {
             case OBJECT:
@@ -228,42 +220,35 @@ public class ExpectedSchemaBuilder {
         }
     }
 
-    private void handleObject(AbstractFunctionCallExpression parentExpr, 
IVariableTypeEnvironment typeEnv,
-            AbstractComplexExpectedSchemaNode parent, IExpectedSchemaNode 
child) throws AlgebricksException {
-        if 
(BuiltinFunctions.FIELD_ACCESS_BY_NAME.equals(parentExpr.getFunctionIdentifier()))
 {
-            ObjectExpectedSchemaNode objectNode = (ObjectExpectedSchemaNode) 
parent;
-            
objectNode.addChild(ConstantExpressionUtil.getStringArgument(parentExpr, 1), 
child);
-        } else {
-            //FIELD_ACCESS_BY_INDEX
-            ARecordType recordType = (ARecordType) 
typeEnv.getType(parentExpr.getArguments().get(0).getValue());
-            ObjectExpectedSchemaNode objectNode = (ObjectExpectedSchemaNode) 
parent;
-            int fieldIdx = ConstantExpressionUtil.getIntArgument(parentExpr, 
1);
-            objectNode.addChild(recordType.getFieldNames()[fieldIdx], child);
+    public static ExpectedSchemaNodeType 
getExpectedNestedNodeType(AbstractFunctionCallExpression funcExpr) {
+        FunctionIdentifier fid = funcExpr.getFunctionIdentifier();
+        if (BuiltinFunctions.FIELD_ACCESS_BY_NAME.equals(fid) || 
BuiltinFunctions.FIELD_ACCESS_BY_INDEX.equals(fid)) {
+            return ExpectedSchemaNodeType.OBJECT;
+        } else if (ARRAY_FUNCTIONS.contains(fid)) {
+            return ExpectedSchemaNodeType.ARRAY;
         }
+        throw new IllegalStateException("Function " + fid + " should not be 
pushed down");
+    }
+
+    private static void handleObject(AbstractFunctionCallExpression 
parentExpr, IVariableTypeEnvironment typeEnv,
+            AbstractComplexExpectedSchemaNode parent, IExpectedSchemaNode 
child) throws AlgebricksException {
+        String fieldName = PushdownUtil.getFieldName(parentExpr, typeEnv);
+        ObjectExpectedSchemaNode objectNode = (ObjectExpectedSchemaNode) 
parent;
+        objectNode.addChild(fieldName, child);
     }
 
-    private void handleArray(AbstractComplexExpectedSchemaNode parent, 
IExpectedSchemaNode child) {
+    private static void handleArray(AbstractComplexExpectedSchemaNode parent, 
IExpectedSchemaNode child) {
         ArrayExpectedSchemaNode arrayNode = (ArrayExpectedSchemaNode) parent;
         arrayNode.addChild(child);
     }
 
-    private void handleUnion(AbstractFunctionCallExpression parentExpr, 
AbstractComplexExpectedSchemaNode parent,
+    private static void handleUnion(AbstractFunctionCallExpression parentExpr, 
AbstractComplexExpectedSchemaNode parent,
             IExpectedSchemaNode child) throws AlgebricksException {
         UnionExpectedSchemaNode unionNode = (UnionExpectedSchemaNode) parent;
         ExpectedSchemaNodeType parentType = 
getExpectedNestedNodeType(parentExpr);
         addChild(parentExpr, null, unionNode.getChild(parentType), child);
     }
 
-    private static ExpectedSchemaNodeType 
getExpectedNestedNodeType(AbstractFunctionCallExpression funcExpr) {
-        FunctionIdentifier fid = funcExpr.getFunctionIdentifier();
-        if (BuiltinFunctions.FIELD_ACCESS_BY_NAME.equals(fid) || 
BuiltinFunctions.FIELD_ACCESS_BY_INDEX.equals(fid)) {
-            return ExpectedSchemaNodeType.OBJECT;
-        } else if (ARRAY_FUNCTIONS.contains(fid)) {
-            return ExpectedSchemaNodeType.ARRAY;
-        }
-        throw new IllegalStateException("Function " + fid + " should not be 
pushed down");
-    }
-
     private static boolean isVariable(ILogicalExpression expr) {
         return expr.getExpressionTag() == LogicalExpressionTag.VARIABLE;
     }
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ObjectExpectedSchemaNode.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ObjectExpectedSchemaNode.java
index 69812a305e..82a72146a0 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ObjectExpectedSchemaNode.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ObjectExpectedSchemaNode.java
@@ -67,7 +67,7 @@ public class ObjectExpectedSchemaNode extends 
AbstractComplexExpectedSchemaNode
         return new ARecordType("typeName", fieldNames, fieldTypes, false);
     }
 
-    protected String getChildFieldName(IExpectedSchemaNode requestedChild) {
+    public String getChildFieldName(IExpectedSchemaNode requestedChild) {
         String key = null;
         for (Map.Entry<String, IExpectedSchemaNode> child : 
children.entrySet()) {
             if (child.getValue() == requestedChild) {
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ColumnFilterPathBuilderVisitor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ColumnFilterPathBuilderVisitor.java
similarity index 88%
rename from 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ColumnFilterPathBuilderVisitor.java
rename to 
asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ColumnFilterPathBuilderVisitor.java
index 492aea76d3..94fff57f77 100644
--- 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/schema/ColumnFilterPathBuilderVisitor.java
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ColumnFilterPathBuilderVisitor.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.asterix.optimizer.rules.pushdown.schema;
+package org.apache.asterix.optimizer.rules.pushdown.visitor;
 
 import java.util.Map;
 
@@ -27,6 +27,13 @@ import org.apache.asterix.om.types.ATypeTag;
 import org.apache.asterix.om.types.BuiltinType;
 import org.apache.asterix.om.types.IAType;
 import org.apache.asterix.om.types.IATypeVisitor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ArrayExpectedSchemaNode;
+import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNodeVisitor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ObjectExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.UnionExpectedSchemaNode;
 import org.apache.asterix.runtime.projection.FunctionCallInformation;
 import 
org.apache.asterix.runtime.projection.ProjectionFiltrationWarningFactoryProvider;
 
@@ -38,6 +45,10 @@ public class ColumnFilterPathBuilderVisitor implements 
IExpectedSchemaNodeVisito
     private Map<String, FunctionCallInformation> sourceInformationMap;
     private int counter = 0;
 
+    public ARecordType buildPath(AnyExpectedSchemaNode anyNode) {
+        return buildPath(anyNode, null, null, null);
+    }
+
     public ARecordType buildPath(AnyExpectedSchemaNode anyNode, IAObject 
constant,
             Map<String, FunctionCallInformation> sourceInformationMap, 
FunctionCallInformation compareFunctionInfo) {
 
diff --git 
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpressionToExpectedSchemaNodeVisitor.java
 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpressionToExpectedSchemaNodeVisitor.java
new file mode 100644
index 0000000000..5168505d0b
--- /dev/null
+++ 
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/pushdown/visitor/ExpressionToExpectedSchemaNodeVisitor.java
@@ -0,0 +1,126 @@
+/*
+ * 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.pushdown.visitor;
+
+import static 
org.apache.asterix.metadata.utils.PushdownUtil.SUPPORTED_FUNCTIONS;
+
+import 
org.apache.asterix.optimizer.rules.pushdown.descriptor.ScanDefineDescriptor;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AbstractComplexExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.AnyExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaBuilder;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.ExpectedSchemaNodeType;
+import org.apache.asterix.optimizer.rules.pushdown.schema.IExpectedSchemaNode;
+import 
org.apache.asterix.optimizer.rules.pushdown.schema.RootExpectedSchemaNode;
+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.LogicalVariable;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.AggregateFunctionCallExpression;
+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.ScalarFunctionCallExpression;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.StatefulFunctionCallExpression;
+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.FunctionIdentifier;
+import 
org.apache.hyracks.algebricks.core.algebra.operators.logical.visitors.VariableUtilities;
+import 
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalExpressionVisitor;
+
+public class ExpressionToExpectedSchemaNodeVisitor implements 
ILogicalExpressionVisitor<IExpectedSchemaNode, Void> {
+    private IVariableTypeEnvironment typeEnv;
+
+    private ScanDefineDescriptor scanDefineDescriptor;
+
+    public void reset(ScanDefineDescriptor scanDefineDescriptor) {
+        this.scanDefineDescriptor = scanDefineDescriptor;
+    }
+
+    public void setTypeEnv(IVariableTypeEnvironment typeEnv) {
+        this.typeEnv = typeEnv;
+    }
+
+    @Override
+    public IExpectedSchemaNode visitConstantExpression(ConstantExpression 
expr, Void arg) throws AlgebricksException {
+        return null;
+    }
+
+    @Override
+    public IExpectedSchemaNode 
visitVariableReferenceExpression(VariableReferenceExpression expr, Void arg)
+            throws AlgebricksException {
+        LogicalVariable variable = VariableUtilities.getVariable(expr);
+        if (scanDefineDescriptor.getVariable() == variable
+                || scanDefineDescriptor.getMetaRecordVariable() == variable) {
+            return RootExpectedSchemaNode.ALL_FIELDS_ROOT_NODE;
+        }
+        return null;
+    }
+
+    @Override
+    public IExpectedSchemaNode 
visitScalarFunctionCallExpression(ScalarFunctionCallExpression expr, Void arg)
+            throws AlgebricksException {
+        return handleFunction(expr);
+    }
+
+    // Disabled expressions
+    @Override
+    public IExpectedSchemaNode 
visitAggregateFunctionCallExpression(AggregateFunctionCallExpression expr, Void 
arg)
+            throws AlgebricksException {
+        return null;
+    }
+
+    @Override
+    public IExpectedSchemaNode 
visitStatefulFunctionCallExpression(StatefulFunctionCallExpression expr, Void 
arg)
+            throws AlgebricksException {
+        return null;
+    }
+
+    @Override
+    public IExpectedSchemaNode 
visitUnnestingFunctionCallExpression(UnnestingFunctionCallExpression expr, Void 
arg)
+            throws AlgebricksException {
+        return handleFunction(expr);
+    }
+
+    private IExpectedSchemaNode handleFunction(AbstractFunctionCallExpression 
expr) throws AlgebricksException {
+        FunctionIdentifier fid = expr.getFunctionIdentifier();
+        if (!SUPPORTED_FUNCTIONS.contains(fid)) {
+            // If not a supported function, return null
+            return null;
+        }
+
+        // All supported functions have the node at their first argument
+        ILogicalExpression parentExpr = expr.getArguments().get(0).getValue();
+        IExpectedSchemaNode parent = parentExpr.accept(this, null);
+        if (parent == null) {
+            return null;
+        }
+
+        AbstractComplexExpectedSchemaNode newParent = replaceIfNeeded(parent, 
expr);
+        IExpectedSchemaNode myNode =
+                new AnyExpectedSchemaNode(newParent, expr.getSourceLocation(), 
expr.getFunctionIdentifier().getName());
+        ExpectedSchemaBuilder.addChild(expr, typeEnv, newParent, myNode);
+        return myNode;
+    }
+
+    private AbstractComplexExpectedSchemaNode 
replaceIfNeeded(IExpectedSchemaNode parent,
+            AbstractFunctionCallExpression funcExpr) {
+        ExpectedSchemaNodeType expectedType = 
ExpectedSchemaBuilder.getExpectedNestedNodeType(funcExpr);
+        return (AbstractComplexExpectedSchemaNode) 
parent.replaceIfNeeded(expectedType, parent.getSourceLocation(),
+                parent.getFunctionName());
+    }
+}
diff --git 
a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/PushdownUtil.java
 
b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/PushdownUtil.java
new file mode 100644
index 0000000000..3bdaca3bcd
--- /dev/null
+++ 
b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/PushdownUtil.java
@@ -0,0 +1,179 @@
+/*
+ * 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.metadata.utils;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.asterix.om.base.ABoolean;
+import org.apache.asterix.om.base.AMissing;
+import org.apache.asterix.om.base.ANull;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.constants.AsterixConstantValue;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.types.ARecordType;
+import org.apache.asterix.om.utils.ConstantExpressionUtil;
+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.LogicalExpressionTag;
+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.IAlgebricksConstantValue;
+import 
org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
+import 
org.apache.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+
+public class PushdownUtil {
+    //Set of allowed functions that can request a type in its entirety without 
marking it as leaf (i.e., ANY)
+    public static final Set<FunctionIdentifier> ALLOWED_FUNCTIONS = 
createAllowedFunctions();
+    //Set of supported array functions
+    public static final Set<FunctionIdentifier> ARRAY_FUNCTIONS = 
createSupportedArrayFunctions();
+    //Set of supported functions that we can push down (a.k.a. path functions)
+    public static final Set<FunctionIdentifier> SUPPORTED_FUNCTIONS = 
createSupportedFunctions();
+
+    public static final Set<FunctionIdentifier> FILTER_PUSHABLE_PATH_FUNCTIONS 
= createFilterPushablePathFunctions();
+    public static final Set<FunctionIdentifier> COMPARE_FUNCTIONS = 
createCompareFunctions();
+    public static final Set<FunctionIdentifier> 
RANGE_FILTER_PUSHABLE_FUNCTIONS = createRangeFilterPushableFunctions();
+
+    private PushdownUtil() {
+    }
+
+    public static String getFieldName(AbstractFunctionCallExpression 
fieldAccessExpr, IVariableTypeEnvironment typeEnv)
+            throws AlgebricksException {
+        if 
(BuiltinFunctions.FIELD_ACCESS_BY_NAME.equals(fieldAccessExpr.getFunctionIdentifier()))
 {
+            return ConstantExpressionUtil.getStringArgument(fieldAccessExpr, 
1);
+        } else {
+            //FIELD_ACCESS_BY_INDEX
+            ARecordType recordType = (ARecordType) 
typeEnv.getType(fieldAccessExpr.getArguments().get(0).getValue());
+            int fieldIdx = 
ConstantExpressionUtil.getIntArgument(fieldAccessExpr, 1);
+            return recordType.getFieldNames()[fieldIdx];
+        }
+    }
+
+    public static boolean isConstant(ILogicalExpression expression) {
+        return expression.getExpressionTag() == LogicalExpressionTag.CONSTANT;
+    }
+
+    public static boolean isFilterPath(ILogicalExpression expression) {
+        FunctionIdentifier fid = getFunctionIdentifier(expression);
+        return fid != null && FILTER_PUSHABLE_PATH_FUNCTIONS.contains(fid);
+    }
+
+    public static boolean isCompare(ILogicalExpression expression) {
+        FunctionIdentifier fid = getFunctionIdentifier(expression);
+        return fid != null && COMPARE_FUNCTIONS.contains(fid);
+    }
+
+    public static boolean isAnd(ILogicalExpression expression) {
+        FunctionIdentifier fid = getFunctionIdentifier(expression);
+        return BuiltinFunctions.AND.equals(fid);
+    }
+
+    public static boolean isOr(ILogicalExpression expression) {
+        FunctionIdentifier fid = getFunctionIdentifier(expression);
+        return BuiltinFunctions.OR.equals(fid);
+    }
+
+    public static boolean isTypeFunction(FunctionIdentifier fid) {
+        return fid.getName().startsWith("is");
+    }
+
+    public static boolean isNestedFunction(FunctionIdentifier fid) {
+        return isObjectFunction(fid) || isArrayFunction(fid) || 
BuiltinFunctions.DEEP_EQUAL.equals(fid);
+    }
+
+    public static boolean isObjectFunction(FunctionIdentifier fid) {
+        String functionName = fid.getName();
+        return functionName.contains("object") || 
BuiltinFunctions.PAIRS.equals(fid);
+    }
+
+    public static boolean isArrayFunction(FunctionIdentifier fid) {
+        String functionName = fid.getName();
+        return functionName.startsWith("array") || 
functionName.startsWith("strict") || functionName.startsWith("sql")
+                || BuiltinFunctions.GET_ITEM.equals(fid);
+    }
+
+    public static boolean isSameFunction(ILogicalExpression expr1, 
ILogicalExpression expr2) {
+        FunctionIdentifier fid1 = getFunctionIdentifier(expr1);
+        FunctionIdentifier fid2 = getFunctionIdentifier(expr2);
+        return fid1 != null && fid1.equals(fid2);
+    }
+
+    public static IAObject getConstant(ILogicalExpression expr) {
+        IAlgebricksConstantValue algebricksConstant = ((ConstantExpression) 
expr).getValue();
+        if (algebricksConstant.isTrue()) {
+            return ABoolean.TRUE;
+        } else if (algebricksConstant.isFalse()) {
+            return ABoolean.FALSE;
+        } else if (algebricksConstant.isMissing()) {
+            return AMissing.MISSING;
+        } else if (algebricksConstant.isNull()) {
+            return ANull.NULL;
+        }
+
+        return ((AsterixConstantValue) algebricksConstant).getObject();
+    }
+
+    private static FunctionIdentifier getFunctionIdentifier(ILogicalExpression 
expression) {
+        if (expression.getExpressionTag() != 
LogicalExpressionTag.FUNCTION_CALL) {
+            return null;
+        }
+        AbstractFunctionCallExpression funcExpr = 
(AbstractFunctionCallExpression) expression;
+        return funcExpr.getFunctionIdentifier();
+    }
+
+    private static Set<FunctionIdentifier> createSupportedArrayFunctions() {
+        return Set.of(BuiltinFunctions.GET_ITEM, BuiltinFunctions.ARRAY_STAR, 
BuiltinFunctions.SCAN_COLLECTION);
+    }
+
+    private static Set<FunctionIdentifier> createSupportedFunctions() {
+        Set<FunctionIdentifier> supportedFunctions = new HashSet<>();
+        supportedFunctions.add(BuiltinFunctions.FIELD_ACCESS_BY_NAME);
+        supportedFunctions.add(BuiltinFunctions.FIELD_ACCESS_BY_INDEX);
+        supportedFunctions.addAll(ARRAY_FUNCTIONS);
+        return supportedFunctions;
+    }
+
+    private static Set<FunctionIdentifier> createAllowedFunctions() {
+        return Set.of(BuiltinFunctions.IS_ARRAY, BuiltinFunctions.IS_OBJECT, 
BuiltinFunctions.IS_ATOMIC,
+                BuiltinFunctions.IS_NUMBER, BuiltinFunctions.IS_BOOLEAN, 
BuiltinFunctions.IS_STRING,
+                AlgebricksBuiltinFunctions.IS_MISSING, 
AlgebricksBuiltinFunctions.IS_NULL, BuiltinFunctions.IS_UNKNOWN,
+                BuiltinFunctions.LT, BuiltinFunctions.LE, BuiltinFunctions.EQ, 
BuiltinFunctions.GT, BuiltinFunctions.GE,
+                BuiltinFunctions.SCALAR_SQL_COUNT);
+    }
+
+    private static Set<FunctionIdentifier> createFilterPushablePathFunctions() 
{
+        Set<FunctionIdentifier> pushablePathFunctions = new 
HashSet<>(SUPPORTED_FUNCTIONS);
+        // TODO Add support for GET_ITEM.
+        pushablePathFunctions.remove(BuiltinFunctions.GET_ITEM);
+        return pushablePathFunctions;
+    }
+
+    private static Set<FunctionIdentifier> createCompareFunctions() {
+        return Set.of(AlgebricksBuiltinFunctions.LE, 
AlgebricksBuiltinFunctions.GE, AlgebricksBuiltinFunctions.LT,
+                AlgebricksBuiltinFunctions.GT, AlgebricksBuiltinFunctions.EQ);
+    }
+
+    private static Set<FunctionIdentifier> 
createRangeFilterPushableFunctions() {
+        Set<FunctionIdentifier> pushableFunctions = new 
HashSet<>(COMPARE_FUNCTIONS);
+        pushableFunctions.add(AlgebricksBuiltinFunctions.AND);
+        pushableFunctions.add(AlgebricksBuiltinFunctions.OR);
+        return pushableFunctions;
+    }
+}
diff --git 
a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/filter/ArrayPathCheckerVisitor.java
 
b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/filter/ArrayPathCheckerVisitor.java
index 594cf9be4f..b1ed40b622 100644
--- 
a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/filter/ArrayPathCheckerVisitor.java
+++ 
b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/utils/filter/ArrayPathCheckerVisitor.java
@@ -42,7 +42,7 @@ public class ArrayPathCheckerVisitor implements 
IATypeVisitor<Boolean, AbstractC
     private final Set<Object> seenCollections;
     private boolean firstPath;
 
-    ArrayPathCheckerVisitor() {
+    public ArrayPathCheckerVisitor() {
         seenCollections = new HashSet<>();
     }
 

Reply via email to