diff options
Diffstat (limited to 'src/main/java/com/amazon')
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) {
  | 
