[ASTERIXDB-3167][COMP] Opt. intersecting indexes leading to a poor plan
Change-Id: I856b923d21e6c4e9bc7e65dd9043bdf17bfe502b
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/17491
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
index 52f0279..9996e1f 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java
@@ -27,6 +27,7 @@
import java.util.Map;
import java.util.Set;
+import org.apache.asterix.common.config.DatasetConfig;
import org.apache.asterix.common.config.DatasetConfig.DatasetType;
import org.apache.asterix.common.config.DatasetConfig.IndexType;
import org.apache.asterix.common.exceptions.CompilationException;
@@ -293,6 +294,63 @@
return false;
}
+ protected List<List<String>> findKeyFieldNames(Index index) throws CompilationException {
+ List<List<String>> keyFieldNames = new ArrayList<>();
+ DatasetConfig.IndexType indexType = index.getIndexType();
+ switch (Index.IndexCategory.of(indexType)) {
+ case ARRAY:
+ Index.ArrayIndexDetails arrayIndexDetails = (Index.ArrayIndexDetails) index.getIndexDetails();
+ for (Index.ArrayIndexElement e : arrayIndexDetails.getElementList()) {
+ for (int i = 0; i < e.getProjectList().size(); i++) {
+ List<String> project = e.getProjectList().get(i);
+ keyFieldNames.add(ArrayIndexUtil.getFlattenedKeyFieldNames(e.getUnnestList(), project));
+ }
+ }
+ break;
+ case VALUE:
+ Index.ValueIndexDetails valueIndexDetails = (Index.ValueIndexDetails) index.getIndexDetails();
+ keyFieldNames = valueIndexDetails.getKeyFieldNames();
+ break;
+ case TEXT:
+ Index.TextIndexDetails textIndexDetails = (Index.TextIndexDetails) index.getIndexDetails();
+ keyFieldNames = textIndexDetails.getKeyFieldNames();
+ break;
+ default:
+ throw new CompilationException(ErrorCode.COMPILATION_UNKNOWN_INDEX_TYPE, String.valueOf(indexType));
+ }
+
+ return keyFieldNames;
+ }
+
+ protected List<IAType> findKeyTypes(Index index) throws CompilationException {
+ List<IAType> keyFieldTypes = new ArrayList<>();
+ DatasetConfig.IndexType indexType = index.getIndexType();
+ switch (Index.IndexCategory.of(indexType)) {
+ case ARRAY:
+ Index.ArrayIndexDetails arrayIndexDetails = (Index.ArrayIndexDetails) index.getIndexDetails();
+ for (Index.ArrayIndexElement e : arrayIndexDetails.getElementList()) {
+ for (int i = 0; i < e.getProjectList().size(); i++) {
+ List<String> project = e.getProjectList().get(i);
+ keyFieldTypes.add(e.getTypeList().get(i));
+ }
+ }
+ break;
+ case VALUE:
+ Index.ValueIndexDetails valueIndexDetails = (Index.ValueIndexDetails) index.getIndexDetails();
+ keyFieldTypes = valueIndexDetails.getKeyFieldTypes();
+ break;
+ case TEXT:
+ Index.TextIndexDetails textIndexDetails = (Index.TextIndexDetails) index.getIndexDetails();
+ keyFieldTypes = textIndexDetails.getKeyFieldTypes();
+ break;
+ default:
+ throw new CompilationException(ErrorCode.COMPILATION_UNKNOWN_INDEX_TYPE, String.valueOf(indexType));
+ }
+
+ return keyFieldTypes;
+
+ }
+
/**
* Removes irrelevant access methods candidates, based on whether the
* expressions in the query match those in the index. For example, some
@@ -318,34 +376,8 @@
indexExprAndVarIt.remove();
continue;
}
- List<List<String>> keyFieldNames;
- List<IAType> keyFieldTypes;
- switch (Index.IndexCategory.of(indexType)) {
- case ARRAY:
- Index.ArrayIndexDetails arrayIndexDetails = (Index.ArrayIndexDetails) index.getIndexDetails();
- keyFieldNames = new ArrayList<>();
- keyFieldTypes = new ArrayList<>();
- for (Index.ArrayIndexElement e : arrayIndexDetails.getElementList()) {
- for (int i = 0; i < e.getProjectList().size(); i++) {
- List<String> project = e.getProjectList().get(i);
- keyFieldNames.add(ArrayIndexUtil.getFlattenedKeyFieldNames(e.getUnnestList(), project));
- keyFieldTypes.add(e.getTypeList().get(i));
- }
- }
- break;
- case VALUE:
- Index.ValueIndexDetails valueIndexDetails = (Index.ValueIndexDetails) index.getIndexDetails();
- keyFieldNames = valueIndexDetails.getKeyFieldNames();
- keyFieldTypes = valueIndexDetails.getKeyFieldTypes();
- break;
- case TEXT:
- Index.TextIndexDetails textIndexDetails = (Index.TextIndexDetails) index.getIndexDetails();
- keyFieldNames = textIndexDetails.getKeyFieldNames();
- keyFieldTypes = textIndexDetails.getKeyFieldTypes();
- break;
- default:
- throw new CompilationException(ErrorCode.COMPILATION_UNKNOWN_INDEX_TYPE, String.valueOf(indexType));
- }
+ List<List<String>> keyFieldNames = findKeyFieldNames(index);
+ List<IAType> keyFieldTypes = findKeyTypes(index);
boolean allUsed = true;
int lastFieldMatched = -1;
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
index 43e482b..24a7fb2 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/IntroduceSelectAccessMethodRule.java
@@ -26,6 +26,7 @@
import java.util.TreeMap;
import org.apache.asterix.algebra.operators.CommitOperator;
+import org.apache.asterix.common.config.DatasetConfig;
import org.apache.asterix.common.exceptions.CompilationException;
import org.apache.asterix.common.exceptions.ErrorCode;
import org.apache.asterix.metadata.declared.MetadataProvider;
@@ -372,6 +373,76 @@
return lop;
}
+ // list1 is <= list2 in terms of size; so check if everything in list1 is also in list2 in the same order
+ protected boolean prefix(List<List<String>> list1, List<List<String>> list2) {
+ int i, j;
+
+ for (i = 0; i < list1.size(); i++) {
+ List<String> l1 = list1.get(i);
+ List<String> l2 = list2.get(i);
+ if (l1.size() != l2.size()) {
+ return false;
+ }
+ for (j = 0; j < l1.size(); j++) {
+ String s1 = l1.get(j);
+ String s2 = l2.get(j);
+ if (!(s1.equals(s2))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ protected void removeSmallerPrefixIndexes(List<Pair<IAccessMethod, Index>> indexes) throws CompilationException {
+ int len = indexes.size();
+ int i, j;
+ Index indexI, indexJ;
+ boolean include[];
+ include = new boolean[len];
+ for (i = 0; i < len; i++) {
+ include[i] = true; // Initially every index is included.
+ }
+
+ List<List<String>> fieldNamesI, fieldNamesJ;
+
+ for (i = 0; i < len - 1; i++) {
+ if (include[i]) {
+ IAccessMethod ami = indexes.get(i).first;
+ indexI = indexes.get(i).second;
+ DatasetConfig.IndexType typeI = indexI.getIndexType();
+ fieldNamesI = findKeyFieldNames(indexI);
+
+ for (j = i + 1; j < len; j++) {
+ if (include[j]) {
+ IAccessMethod amj = indexes.get(j).first;
+ if (ami == amj) { // should be the same accessMethods
+ indexJ = indexes.get(j).second;
+ DatasetConfig.IndexType typeJ = indexJ.getIndexType();
+ if (typeI == typeJ) {
+ fieldNamesJ = findKeyFieldNames(indexJ);
+ if (fieldNamesI.size() <= fieldNamesJ.size()) {
+ if (prefix(fieldNamesI, fieldNamesJ)) {
+ include[i] = false;
+ }
+ } else if (prefix(fieldNamesJ, fieldNamesI)) {
+ include[j] = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // remove the shorter indexes if any
+ for (i = len - 1; i >= 0; i--) { // removing from the end; seems safer that way
+ if (!include[i]) { // if this index can be removed it, do so;
+ indexes.remove(i);
+ }
+ }
+ }
+
/**
* Recursively traverse the given plan and check whether a SELECT operator exists.
* If one is found, maintain the path from the root to SELECT operator and
@@ -486,6 +557,7 @@
// Choose all indexes that will be applied.
chooseAllIndexes(analyzedAMs, chosenIndexes);
+ removeSmallerPrefixIndexes(chosenIndexes);
if (chosenIndexes == null || chosenIndexes.isEmpty()) {
// We can't apply any index for this SELECT operator