summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon/carbonado/qe
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/amazon/carbonado/qe')
-rw-r--r--src/main/java/com/amazon/carbonado/qe/CompositeScore.java30
-rw-r--r--src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java9
-rw-r--r--src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java4
-rw-r--r--src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java59
4 files changed, 87 insertions, 15 deletions
diff --git a/src/main/java/com/amazon/carbonado/qe/CompositeScore.java b/src/main/java/com/amazon/carbonado/qe/CompositeScore.java
index c8e9490..6d0fad5 100644
--- a/src/main/java/com/amazon/carbonado/qe/CompositeScore.java
+++ b/src/main/java/com/amazon/carbonado/qe/CompositeScore.java
@@ -166,24 +166,38 @@ public class CompositeScore<S extends Storable> {
return result;
}
- result = FilteringScore.rangeComparator()
- .compare(first.getFilteringScore(), second.getFilteringScore());
+ FilteringScore<?> firstScore = first.getFilteringScore();
+ FilteringScore<?> secondScore = second.getFilteringScore();
+
+ result = FilteringScore.rangeComparator().compare(firstScore, secondScore);
if (result != 0) {
return result;
}
- result = OrderingScore.fullComparator()
- .compare(first.getOrderingScore(), second.getOrderingScore());
+ if (considerOrdering(firstScore) && considerOrdering(secondScore)) {
+ // Only consider ordering if index is fast (clustered) or if
+ // index is used for any significant filtering. A full scan of
+ // an index just to get desired ordering can be very expensive
+ // due to random access I/O. A sort operation is often faster.
- if (result != 0) {
- return result;
+ result = OrderingScore.fullComparator()
+ .compare(first.getOrderingScore(), second.getOrderingScore());
+
+ if (result != 0) {
+ return result;
+ }
}
- result = FilteringScore.fullComparator()
- .compare(first.getFilteringScore(), second.getFilteringScore());
+ result = FilteringScore.fullComparator().compare(firstScore, secondScore);
return result;
}
+
+ private boolean considerOrdering(FilteringScore<?> score) {
+ return score.isIndexClustered()
+ || score.getIdentityCount() > 0
+ || score.hasRangeMatch();
+ }
}
}
diff --git a/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java
index 76f0b52..dfa76ac 100644
--- a/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java
+++ b/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java
@@ -371,6 +371,15 @@ public class IndexedQueryAnalyzer<S extends Storable> {
}
/**
+ * Returns true if local or foreign index is clustered. Scans of
+ * clustered indexes are generally faster.
+ */
+ public boolean isIndexClustered() {
+ return (mLocalIndex != null && mLocalIndex.isClustered())
+ || (mForeignIndex != null && mForeignIndex.isClustered());
+ }
+
+ /**
* Returns true if the given result uses the same index as this, and in
* the same way. The only allowed differences are in the remainder
* filter and orderings.
diff --git a/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java
index d87740b..cc342c6 100644
--- a/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java
+++ b/src/main/java/com/amazon/carbonado/qe/IndexedQueryExecutor.java
@@ -177,6 +177,10 @@ public class IndexedQueryExecutor<S extends Storable> extends AbstractQueryExecu
filter = filter == null ? p : filter.and(p);
}
+ if (filter == null) {
+ return Filter.getOpenFilter(getStorableType());
+ }
+
return filter;
}
diff --git a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java
index 5933f6c..c64e4d4 100644
--- a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java
+++ b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java
@@ -19,6 +19,7 @@
package com.amazon.carbonado.qe;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.HashSet;
@@ -184,15 +185,28 @@ public class UnionQueryAnalyzer<S extends Storable> implements QueryExecutorFact
// Keep looping until total ordering achieved.
while (true) {
- // For each ordering score, find the next free property. If
- // property is in the super key increment a tally associated with
- // property direction. Choose the property with the best tally and
- // augment the orderings with it and create new sub-results.
- // Remove the property from the super key and the key set. If any
- // key is now fully covered, a total ordering has been achieved.
+ // For each ordering score, iterate over the entire unused ordering
+ // properties and select the next free property. If property is in
+ // the super key increment a tally associated with property
+ // direction. Choose the property with the best tally and augment
+ // the orderings with it and create new sub-results. Remove the
+ // property from the super key and the key set. If any key is now
+ // fully covered, a total ordering has been achieved.
for (IndexedQueryAnalyzer<S>.Result result : subResults) {
OrderingScore<S> score = result.getCompositeScore().getOrderingScore();
+
+ OrderingList<S> unused = score.getUnusedOrdering();
+ if (unused.size() > 0) {
+ for (OrderedProperty<S> prop : unused) {
+ ChainedProperty<S> chainedProp = prop.getChainedProperty();
+ Tally tally = superKey.get(chainedProp);
+ if (tally != null) {
+ tally.increment(prop.getDirection());
+ }
+ }
+ }
+
OrderingList<S> free = score.getFreeOrdering();
if (free.size() > 0) {
OrderedProperty<S> prop = free.get(0);
@@ -237,7 +251,9 @@ public class UnionQueryAnalyzer<S extends Storable> implements QueryExecutorFact
/**
* Returns a list of all primary and alternate keys, stripped of ordering.
*/
- private List<Set<ChainedProperty<S>>> getKeys() {
+ private List<Set<ChainedProperty<S>>> getKeys()
+ throws SupportException, RepositoryException
+ {
StorableInfo<S> info = StorableIntrospector.examine(mIndexAnalyzer.getStorableType());
List<Set<ChainedProperty<S>>> keys = new ArrayList<Set<ChainedProperty<S>>>();
@@ -247,6 +263,26 @@ public class UnionQueryAnalyzer<S extends Storable> implements QueryExecutorFact
keys.add(stripOrdering(altKey.getProperties()));
}
+ // Also fold in all unique indexes, just in case they weren't reported
+ // as actual keys.
+ Collection<StorableIndex<S>> indexes =
+ mRepoAccess.storageAccessFor(getStorableType()).getAllIndexes();
+
+ for (StorableIndex<S> index : indexes) {
+ if (!index.isUnique()) {
+ continue;
+ }
+
+ int propCount = index.getPropertyCount();
+ Set<ChainedProperty<S>> props = new HashSet<ChainedProperty<S>>(propCount);
+
+ for (int i=0; i<propCount; i++) {
+ props.add(index.getOrderedProperty(i).getChainedProperty());
+ }
+
+ keys.add(props);
+ }
+
return keys;
}
@@ -332,6 +368,15 @@ public class UnionQueryAnalyzer<S extends Storable> implements QueryExecutorFact
full = result;
break;
}
+ if (!result.getCompositeScore().getFilteringScore().hasAnyMatches()) {
+ if (full == null) {
+ // This index is used only for its ordering, and it will be
+ // tentatively selected as the "full scan". If a result is
+ // found doesn't use an index for anything, then it becomes
+ // the "full scan" index.
+ full = result;
+ }
+ }
}
if (full == null) {