optimization for disjunctive selection predicates
- add rule to rewrite a disjunction of eq-comparisons on the
  same variable in a selection to a join
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/base/RuleCollections.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/base/RuleCollections.java
index c2469dc..aa60c74 100644
--- a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/base/RuleCollections.java
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/base/RuleCollections.java
@@ -53,6 +53,7 @@
 import edu.uci.ics.asterix.optimizer.rules.RemoveSortInFeedIngestionRule;
 import edu.uci.ics.asterix.optimizer.rules.RemoveUnusedOneToOneEquiJoinRule;
 import edu.uci.ics.asterix.optimizer.rules.ReplaceSinkOpWithCommitOpRule;
+import edu.uci.ics.asterix.optimizer.rules.DisjunctivePredicateToJoinRule;
 import edu.uci.ics.asterix.optimizer.rules.SetAsterixPhysicalOperatorsRule;
 import edu.uci.ics.asterix.optimizer.rules.SetClosedRecordConstructorsRule;
 import edu.uci.ics.asterix.optimizer.rules.SimilarityCheckRule;
@@ -158,6 +159,7 @@
         condPushDownAndJoinInference.add(new SimpleUnnestToProductRule());
         condPushDownAndJoinInference.add(new ComplexUnnestToProductRule());
         condPushDownAndJoinInference.add(new ComplexJoinInferenceRule());
+        condPushDownAndJoinInference.add(new DisjunctivePredicateToJoinRule());
         condPushDownAndJoinInference.add(new PushSelectIntoJoinRule());
         condPushDownAndJoinInference.add(new IntroJoinInsideSubplanRule());
         condPushDownAndJoinInference.add(new PushAssignDownThroughProductRule());
diff --git a/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java
new file mode 100644
index 0000000..0fb205d
--- /dev/null
+++ b/asterix-algebra/src/main/java/edu/uci/ics/asterix/optimizer/rules/DisjunctivePredicateToJoinRule.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2014 by The Regents of the University of California
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * you may obtain a copy of the License from
+ *
+ *     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 edu.uci.ics.asterix.optimizer.rules;
+
+import java.util.HashSet;
+import java.util.List;
+
+import org.apache.commons.lang3.mutable.Mutable;
+import org.apache.commons.lang3.mutable.MutableObject;
+
+import edu.uci.ics.asterix.om.base.AOrderedList;
+import edu.uci.ics.asterix.om.constants.AsterixConstantValue;
+import edu.uci.ics.asterix.om.functions.AsterixBuiltinFunctions;
+import edu.uci.ics.asterix.om.types.AOrderedListType;
+import edu.uci.ics.asterix.om.types.IAType;
+import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.ILogicalOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.IOptimizationContext;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalExpressionTag;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalOperatorTag;
+import edu.uci.ics.hyracks.algebricks.core.algebra.base.LogicalVariable;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.ConstantExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.IndexedNLJoinExpressionAnnotation;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.ScalarFunctionCallExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.UnnestingFunctionCallExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.expressions.VariableReferenceExpression;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.AlgebricksBuiltinFunctions;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.IFunctionInfo;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.EmptyTupleSourceOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.InnerJoinOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.SelectOperator;
+import edu.uci.ics.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator;
+import edu.uci.ics.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule;
+
+public class DisjunctivePredicateToJoinRule implements IAlgebraicRewriteRule {
+
+    @Override
+    public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
+            throws AlgebricksException {
+
+        SelectOperator select;
+        if ((select = asSelectOperator(opRef)) == null) {
+            return false;
+        }
+
+        AbstractFunctionCallExpression condEx;
+        if ((condEx = asFunctionCallExpression(select.getCondition(), AlgebricksBuiltinFunctions.OR)) == null) {
+            return false;
+        }
+
+        List<Mutable<ILogicalExpression>> args = condEx.getArguments();
+
+        VariableReferenceExpression varEx = null;
+        IAType valType = null;
+        HashSet<AsterixConstantValue> values = new HashSet<AsterixConstantValue>();
+
+        for (Mutable<ILogicalExpression> arg : args) {
+            AbstractFunctionCallExpression fctCall;
+            if ((fctCall = asFunctionCallExpression(arg, AlgebricksBuiltinFunctions.EQ)) == null) {
+                return false;
+            }
+
+            boolean haveConst = false;
+            boolean haveVar = false;
+            List<Mutable<ILogicalExpression>> fctArgs = fctCall.getArguments();
+            for (Mutable<ILogicalExpression> fctArg : fctArgs) {
+                final ILogicalExpression argExpr = fctArg.getValue();
+                switch (argExpr.getExpressionTag()) {
+                    case CONSTANT:
+                        haveConst = true;
+                        AsterixConstantValue value = (AsterixConstantValue) ((ConstantExpression) argExpr).getValue();
+                        if (valType == null) {
+                            valType = value.getObject().getType();
+                        } else if (!isCompatible(valType, value.getObject().getType())) {
+                            return false;
+                        }
+                        values.add(value);
+                        break;
+                    case VARIABLE:
+                        haveVar = true;
+                        final VariableReferenceExpression varArg = (VariableReferenceExpression) argExpr;
+                        if (varEx == null) {
+                            varEx = varArg;
+                        } else if (!varEx.getVariableReference().equals(varArg.getVariableReference())) {
+                            return false;
+                        }
+                        break;
+                    default:
+                        return false;
+                }
+            }
+            if (!(haveVar && haveConst)) {
+                return false;
+            }
+        }
+
+        AOrderedList list = new AOrderedList(new AOrderedListType(valType, "orderedlist"));
+        for (AsterixConstantValue value : values) {
+            list.add(value.getObject());
+        }
+
+        EmptyTupleSourceOperator ets = new EmptyTupleSourceOperator();
+        context.computeAndSetTypeEnvironmentForOperator(ets);
+
+        ILogicalExpression cExp = new ConstantExpression(new AsterixConstantValue(list));
+        Mutable<ILogicalExpression> mutCExp = new MutableObject<ILogicalExpression>(cExp);
+        IFunctionInfo scanFctInfo = AsterixBuiltinFunctions
+                .getAsterixFunctionInfo(AsterixBuiltinFunctions.SCAN_COLLECTION);
+        UnnestingFunctionCallExpression scanExp = new UnnestingFunctionCallExpression(scanFctInfo, mutCExp);
+        LogicalVariable scanVar = context.newVar();
+        UnnestOperator unn = new UnnestOperator(scanVar, new MutableObject<ILogicalExpression>(scanExp));
+        unn.getInputs().add(new MutableObject<ILogicalOperator>(ets));
+        context.computeAndSetTypeEnvironmentForOperator(unn);
+
+        IFunctionInfo eqFctInfo = AsterixBuiltinFunctions.getAsterixFunctionInfo(AlgebricksBuiltinFunctions.EQ);
+        AbstractFunctionCallExpression eqExp = new ScalarFunctionCallExpression(eqFctInfo);
+        eqExp.getArguments().add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(scanVar)));
+        eqExp.getArguments().add(new MutableObject<ILogicalExpression>(varEx.cloneExpression()));
+        eqExp.getAnnotations().put(IndexedNLJoinExpressionAnnotation.INSTANCE,
+                IndexedNLJoinExpressionAnnotation.INSTANCE);
+
+        InnerJoinOperator jOp = new InnerJoinOperator(new MutableObject<ILogicalExpression>(eqExp));
+        jOp.getInputs().add(new MutableObject<ILogicalOperator>(unn));
+        jOp.getInputs().add(select.getInputs().get(0));
+
+        opRef.setValue(jOp);
+        context.computeAndSetTypeEnvironmentForOperator(jOp);
+
+        return true;
+    }
+
+    @Override
+    public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context) {
+        return false;
+    }
+
+    /**
+     * This checks the compatibility the types of the constants to ensure that the comparison behaves as expected
+     * when joining. Right now this compatibility is defined as type equality, but it could we relaxed.
+     * Once type promotion works correctly in all parts of the system, this check should not be needed anymore.
+     * (see https://code.google.com/p/asterixdb/issues/detail?id=716)
+     * 
+     * @param t1
+     *            one type
+     * @param t2
+     *            another type
+     * @return true, if types are equal
+     */
+    private static boolean isCompatible(IAType t1, IAType t2) {
+        return t1.equals(t2);
+    }
+
+    // some helpers
+
+    private static SelectOperator asSelectOperator(ILogicalOperator op) {
+        return op.getOperatorTag() == LogicalOperatorTag.SELECT ? (SelectOperator) op : null;
+    }
+
+    private static SelectOperator asSelectOperator(Mutable<ILogicalOperator> op) {
+        return asSelectOperator(op.getValue());
+    }
+
+    private static AbstractFunctionCallExpression asFunctionCallExpression(ILogicalExpression ex, FunctionIdentifier fi) {
+        AbstractFunctionCallExpression fctCall = (ex.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL ? (AbstractFunctionCallExpression) ex
+                : null);
+        if (fctCall != null && (fi == null || fctCall.getFunctionIdentifier().equals(fi)))
+            return fctCall;
+        return null;
+    }
+
+    private static AbstractFunctionCallExpression asFunctionCallExpression(Mutable<ILogicalExpression> ex,
+            FunctionIdentifier fi) {
+        return asFunctionCallExpression(ex.getValue(), fi);
+    }
+
+}
diff --git a/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-1.aql b/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-1.aql
new file mode 100644
index 0000000..e46bd85
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-1.aql
@@ -0,0 +1,14 @@
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type TestType as {
+  "id" : string,
+  "idx" : string,
+  "no-idx" : string
+};
+
+create dataset TestSet(TestType) primary key "id";
+create index TestSetIndex on TestSet(idx);
+
+delete $x from dataset TestSet where $x.id = "one" or $x.id = "two";
diff --git a/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-2.aql b/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-2.aql
new file mode 100644
index 0000000..9166f7ec
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-2.aql
@@ -0,0 +1,14 @@
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type TestType as {
+  "id" : string,
+  "idx" : string,
+  "no-idx" : string
+};
+
+create dataset TestSet(TestType) primary key "id";
+create index TestSetIndex on TestSet(idx);
+
+delete $x from dataset TestSet where $x.idx = "one" or $x.idx = "two";
diff --git a/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-3.aql b/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-3.aql
new file mode 100644
index 0000000..c048ce2
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/queries/disjunction-to-join-delete-3.aql
@@ -0,0 +1,14 @@
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type TestType as {
+  "id" : string,
+  "idx" : string,
+  "no-idx" : string
+};
+
+create dataset TestSet(TestType) primary key "id";
+create index TestSetIndex on TestSet(idx);
+
+delete $x from dataset TestSet where $x.no-idx = "one" or $x.no-idx = "two";
diff --git a/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-1.plan b/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-1.plan
new file mode 100644
index 0000000..5bb2d07
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-1.plan
@@ -0,0 +1,23 @@
+-- COMMIT  |PARTITIONED|
+  -- STREAM_PROJECT  |PARTITIONED|
+    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+      -- INDEX_INSERT_DELETE  |PARTITIONED|
+        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+          -- STREAM_PROJECT  |PARTITIONED|
+            -- ASSIGN  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- INSERT_DELETE  |PARTITIONED|
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    -- MATERIALIZE  |PARTITIONED|
+                      -- HASH_PARTITION_EXCHANGE [$$12]  |PARTITIONED|
+                        -- ASSIGN  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- BTREE_SEARCH  |PARTITIONED|
+                                    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                      -- STABLE_SORT [$$15(ASC)]  |PARTITIONED|
+                                        -- HASH_PARTITION_EXCHANGE [$$15]  |PARTITIONED|
+                                          -- UNNEST  |UNPARTITIONED|
+                                            -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-2.plan b/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-2.plan
new file mode 100644
index 0000000..5e1f889
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-2.plan
@@ -0,0 +1,30 @@
+-- COMMIT  |PARTITIONED|
+  -- STREAM_PROJECT  |PARTITIONED|
+    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+      -- INDEX_INSERT_DELETE  |PARTITIONED|
+        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+          -- STREAM_PROJECT  |PARTITIONED|
+            -- ASSIGN  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- INSERT_DELETE  |PARTITIONED|
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    -- MATERIALIZE  |PARTITIONED|
+                      -- HASH_PARTITION_EXCHANGE [$$12]  |PARTITIONED|
+                        -- ASSIGN  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- STREAM_SELECT  |PARTITIONED|
+                                  -- ASSIGN  |PARTITIONED|
+                                    -- STREAM_PROJECT  |PARTITIONED|
+                                      -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                        -- BTREE_SEARCH  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- STABLE_SORT [$$19(ASC)]  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- STREAM_PROJECT  |PARTITIONED|
+                                                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                    -- BTREE_SEARCH  |PARTITIONED|
+                                                      -- BROADCAST_EXCHANGE  |PARTITIONED|
+                                                        -- UNNEST  |UNPARTITIONED|
+                                                          -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
diff --git a/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-3.plan b/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-3.plan
new file mode 100644
index 0000000..55fc23c
--- /dev/null
+++ b/asterix-app/src/test/resources/optimizerts/results/disjunction-to-join-delete-3.plan
@@ -0,0 +1,28 @@
+-- COMMIT  |PARTITIONED|
+  -- STREAM_PROJECT  |PARTITIONED|
+    -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+      -- INDEX_INSERT_DELETE  |PARTITIONED|
+        -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+          -- STREAM_PROJECT  |PARTITIONED|
+            -- ASSIGN  |PARTITIONED|
+              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                -- INSERT_DELETE  |PARTITIONED|
+                  -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                    -- MATERIALIZE  |PARTITIONED|
+                      -- HASH_PARTITION_EXCHANGE [$$12]  |PARTITIONED|
+                        -- ASSIGN  |PARTITIONED|
+                          -- STREAM_PROJECT  |PARTITIONED|
+                            -- ASSIGN  |PARTITIONED|
+                              -- STREAM_PROJECT  |PARTITIONED|
+                                -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                  -- HYBRID_HASH_JOIN [$$15][$$13]  |PARTITIONED|
+                                    -- HASH_PARTITION_EXCHANGE [$$15]  |PARTITIONED|
+                                      -- UNNEST  |UNPARTITIONED|
+                                        -- EMPTY_TUPLE_SOURCE  |UNPARTITIONED|
+                                    -- HASH_PARTITION_EXCHANGE [$$13]  |PARTITIONED|
+                                      -- STREAM_PROJECT  |PARTITIONED|
+                                        -- ASSIGN  |PARTITIONED|
+                                          -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                            -- DATASOURCE_SCAN  |PARTITIONED|
+                                              -- ONE_TO_ONE_EXCHANGE  |PARTITIONED|
+                                                -- EMPTY_TUPLE_SOURCE  |PARTITIONED|
diff --git a/asterix-app/src/test/resources/runtimets/queries/cross-dataverse/cross-dv02/cross-dv02.3.query.aql b/asterix-app/src/test/resources/runtimets/queries/cross-dataverse/cross-dv02/cross-dv02.3.query.aql
index 66b6e61..d0a1829 100644
--- a/asterix-app/src/test/resources/runtimets/queries/cross-dataverse/cross-dv02/cross-dv02.3.query.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/cross-dataverse/cross-dv02/cross-dv02.3.query.aql
@@ -8,4 +8,5 @@
 
 for $l in dataset('Metadata.Dataset')
 where $l.DataverseName='student' or $l.DataverseName='teacher'
+order by $l.DatasetName
 return $l
diff --git a/asterix-app/src/test/resources/runtimets/queries/cross-dataverse/cross-dv04/cross-dv04.3.query.aql b/asterix-app/src/test/resources/runtimets/queries/cross-dataverse/cross-dv04/cross-dv04.3.query.aql
index 3745d9c..e355f87 100644
--- a/asterix-app/src/test/resources/runtimets/queries/cross-dataverse/cross-dv04/cross-dv04.3.query.aql
+++ b/asterix-app/src/test/resources/runtimets/queries/cross-dataverse/cross-dv04/cross-dv04.3.query.aql
@@ -11,4 +11,5 @@
 
 for $l in dataset('Metadata.Dataset')
 where $l.DataverseName='student' or $l.DataverseName='teacher'
+order by $l.DatasetName
 return $l
diff --git a/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.1.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.1.ddl.aql
new file mode 100644
index 0000000..a841d97
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.1.ddl.aql
@@ -0,0 +1,12 @@
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+create type TestType as {
+  "id" : string,
+  "idx" : string,
+  "no-idx" : string
+};
+
+create dataset TestSet(TestType) primary key "id";
+create index TestSetIndex on TestSet(idx);
diff --git a/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.2.update.aql b/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.2.update.aql
new file mode 100644
index 0000000..a66da81
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.2.update.aql
@@ -0,0 +1,9 @@
+use dataverse test;
+
+insert into dataset TestSet
+for $x in {{ "one", "two", "three" }}
+return {
+  "id" : $x,
+  "idx" : $x,
+  "no-idx" : $x
+};
diff --git a/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.3.query.aql b/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.3.query.aql
new file mode 100644
index 0000000..ee82720
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.3.query.aql
@@ -0,0 +1,5 @@
+use dataverse test;
+
+for $x in dataset TestSet
+where $x.id = "one" or $x.id = "two" or $x.id = "two"
+return $x
diff --git a/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv02/cross-dv02.1.adm b/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv02/cross-dv02.1.adm
index c2674b7..aac07d7 100644
--- a/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv02/cross-dv02.1.adm
+++ b/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv02/cross-dv02.1.adm
@@ -1,4 +1,4 @@
-{ "DataverseName": "student", "DatasetName": "gdstd", "DataTypeName": "stdType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Tue Sep 24 22:20:30 PDT 2013", "DatasetId": 102, "PendingOp": 0 }
-{ "DataverseName": "student", "DatasetName": "ugdstd", "DataTypeName": "stdType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Tue Sep 24 22:20:30 PDT 2013", "DatasetId": 101, "PendingOp": 0 }
-{ "DataverseName": "teacher", "DatasetName": "prof", "DataTypeName": "tchrType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Tue Sep 24 22:20:31 PDT 2013", "DatasetId": 103, "PendingOp": 0 }
-{ "DataverseName": "teacher", "DatasetName": "pstdoc", "DataTypeName": "tchrType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Tue Sep 24 22:20:31 PDT 2013", "DatasetId": 104, "PendingOp": 0 }
+{ "DataverseName": "student", "DatasetName": "gdstd", "DataTypeName": "stdType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Apr 30 14:23:30 PDT 2014", "DatasetId": 102, "PendingOp": 0 }
+{ "DataverseName": "teacher", "DatasetName": "prof", "DataTypeName": "tchrType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Apr 30 14:23:30 PDT 2014", "DatasetId": 103, "PendingOp": 0 }
+{ "DataverseName": "teacher", "DatasetName": "pstdoc", "DataTypeName": "tchrType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Apr 30 14:23:31 PDT 2014", "DatasetId": 104, "PendingOp": 0 }
+{ "DataverseName": "student", "DatasetName": "ugdstd", "DataTypeName": "stdType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Apr 30 14:23:30 PDT 2014", "DatasetId": 101, "PendingOp": 0 }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv04/cross-dv04.1.adm b/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv04/cross-dv04.1.adm
index 7ff96e2..aac07d7 100644
--- a/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv04/cross-dv04.1.adm
+++ b/asterix-app/src/test/resources/runtimets/results/cross-dataverse/cross-dv04/cross-dv04.1.adm
@@ -1,4 +1,4 @@
-{ "DataverseName": "student", "DatasetName": "gdstd", "DataTypeName": "stdType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Tue Sep 24 22:22:48 PDT 2013", "DatasetId": 110, "PendingOp": 0 }
-{ "DataverseName": "student", "DatasetName": "ugdstd", "DataTypeName": "stdType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Tue Sep 24 22:22:48 PDT 2013", "DatasetId": 109, "PendingOp": 0 }
-{ "DataverseName": "teacher", "DatasetName": "prof", "DataTypeName": "tchrType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Tue Sep 24 22:22:48 PDT 2013", "DatasetId": 111, "PendingOp": 0 }
-{ "DataverseName": "teacher", "DatasetName": "pstdoc", "DataTypeName": "tchrType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Tue Sep 24 22:22:48 PDT 2013", "DatasetId": 112, "PendingOp": 0 }
+{ "DataverseName": "student", "DatasetName": "gdstd", "DataTypeName": "stdType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Apr 30 14:23:30 PDT 2014", "DatasetId": 102, "PendingOp": 0 }
+{ "DataverseName": "teacher", "DatasetName": "prof", "DataTypeName": "tchrType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Apr 30 14:23:30 PDT 2014", "DatasetId": 103, "PendingOp": 0 }
+{ "DataverseName": "teacher", "DatasetName": "pstdoc", "DataTypeName": "tchrType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Apr 30 14:23:31 PDT 2014", "DatasetId": 104, "PendingOp": 0 }
+{ "DataverseName": "student", "DatasetName": "ugdstd", "DataTypeName": "stdType", "DatasetType": "INTERNAL", "InternalDetails": { "FileStructure": "BTREE", "PartitioningStrategy": "HASH", "PartitioningKey": [ "id" ], "PrimaryKey": [ "id" ], "GroupName": "DEFAULT_NG_ALL_NODES", "Autogenerated": false, "CompactionPolicy": "prefix", "CompactionPolicyProperties": [ { "Name": "max-mergable-component-size", "Value": "1073741824" }, { "Name": "max-tolerance-component-count", "Value": "5" } ] }, "ExternalDetails": null, "Hints": {{  }}, "Timestamp": "Wed Apr 30 14:23:30 PDT 2014", "DatasetId": 101, "PendingOp": 0 }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/results/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.1.adm b/asterix-app/src/test/resources/runtimets/results/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.1.adm
new file mode 100644
index 0000000..72843b2
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/index-selection/disjunctive-predicate-1/disjunctive-predicate-1.1.adm
@@ -0,0 +1,2 @@
+{ "id": "one", "idx": "one", "no-idx": "one" }
+{ "id": "two", "idx": "two", "no-idx": "two" }
diff --git a/asterix-app/src/test/resources/runtimets/testsuite.xml b/asterix-app/src/test/resources/runtimets/testsuite.xml
index 33227b3..ea122c1 100644
--- a/asterix-app/src/test/resources/runtimets/testsuite.xml
+++ b/asterix-app/src/test/resources/runtimets/testsuite.xml
@@ -2419,6 +2419,11 @@
         <output-dir compare="Text">rtree-secondary-index</output-dir>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="index-selection">
+      <compilation-unit name="disjunctive-predicate-1">
+        <output-dir compare="Text">disjunctive-predicate-1</output-dir>
+      </compilation-unit>
+    </test-case>
   </test-group>
   <test-group name="inverted-index-join">
     <test-case FilePath="inverted-index-join">