From 1e947afa4b660a23a2dcb57463dd810fb73e6030 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 3 Sep 2006 21:48:14 +0000 Subject: Manage ordering properties with lists. --- .../com/amazon/carbonado/qe/AbstractQuery.java | 16 -- .../com/amazon/carbonado/qe/CompositeScore.java | 44 +++- .../java/com/amazon/carbonado/qe/EmptyQuery.java | 29 ++- .../amazon/carbonado/qe/IndexedQueryAnalyzer.java | 13 +- .../java/com/amazon/carbonado/qe/OrderingList.java | 227 +++++++++++++++++++++ .../com/amazon/carbonado/qe/OrderingScore.java | 49 ++++- .../amazon/carbonado/qe/SortedQueryExecutor.java | 2 + .../com/amazon/carbonado/qe/StandardQuery.java | 67 +++--- .../amazon/carbonado/qe/UnionQueryAnalyzer.java | 18 +- .../java/com/amazon/carbonado/spi/BaseQuery.java | 15 ++ .../carbonado/qe/TestIndexedQueryExecutor.java | 8 +- .../com/amazon/carbonado/qe/TestOrderingList.java | 110 ++++++++++ .../com/amazon/carbonado/qe/TestOrderingScore.java | 26 ++- .../com/amazon/carbonado/repo/toy/ToyStorage.java | 17 +- 14 files changed, 534 insertions(+), 107 deletions(-) create mode 100644 src/main/java/com/amazon/carbonado/qe/OrderingList.java create mode 100644 src/test/java/com/amazon/carbonado/qe/TestOrderingList.java (limited to 'src') diff --git a/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java b/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java index eb2e977..74d6e8d 100644 --- a/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java +++ b/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java @@ -44,22 +44,6 @@ import com.amazon.carbonado.util.Appender; * @author Brian S O'Neill */ public abstract class AbstractQuery implements Query, Appender { - // FIXME: remove this - protected static final String[] EMPTY_ORDERINGS = {}; - - // FIXME: remove this - protected static String[] extractOrderingNames(OrderedProperty[] orderings) { - String[] orderingStrings; - if (orderings == null || orderings.length == 0) { - return EMPTY_ORDERINGS; - } - orderingStrings = new String[orderings.length]; - for (int i=0; i { + /** + * Evaluates the given index for its filtering and ordering capabilities + * against the given filter and order-by properties. + * + * @param index index to evaluate + * @param filter optional filter which cannot contain any logical 'or' operations. + * @throws IllegalArgumentException if index is null or filter is not supported + */ + public static CompositeScore evaluate + (StorableIndex index, + Filter filter) + { + return evaluate(index, filter, null); + } + /** * Evaluates the given index for its filtering and ordering capabilities * against the given filter and order-by properties. @@ -47,9 +63,10 @@ public class CompositeScore { * @param orderings properties which define desired ordering * @throws IllegalArgumentException if index is null or filter is not supported */ - public static CompositeScore evaluate(StorableIndex index, - Filter filter, - OrderedProperty... orderings) + public static CompositeScore evaluate + (StorableIndex index, + Filter filter, + List> orderings) { if (index == null) { throw new IllegalArgumentException("Index required"); @@ -62,6 +79,25 @@ public class CompositeScore { orderings); } + /** + * Evaluates the given index properties for its filtering and ordering + * capabilities against the given filter and order-by properties. + * + * @param indexProperties index properties to evaluate + * @param unique true if index is unique + * @param clustered true if index is clustered + * @param filter optional filter which cannot contain any logical 'or' operations. + * @throws IllegalArgumentException if index is null or filter is not supported + */ + public static CompositeScore evaluate + (OrderedProperty[] indexProperties, + boolean unique, + boolean clustered, + Filter filter) + { + return evaluate(indexProperties, unique, clustered, filter, null); + } + /** * Evaluates the given index properties for its filtering and ordering * capabilities against the given filter and order-by properties. @@ -78,7 +114,7 @@ public class CompositeScore { boolean unique, boolean clustered, Filter filter, - OrderedProperty... orderings) + List> orderings) { FilteringScore filteringScore = FilteringScore .evaluate(indexProperties, unique, clustered, filter); diff --git a/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java b/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java index ba18531..4918f08 100644 --- a/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java +++ b/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java @@ -44,31 +44,29 @@ public final class EmptyQuery extends AbstractQuery { private final Storage mStorage; // Properties that this query is ordered by. - private final String[] mOrderings; + private final OrderingList mOrderings; /** * @param storage required storage object * @param orderings optional order-by properties */ - // FIXME: remove this - public EmptyQuery(Storage storage, OrderedProperty[] orderings) { + public EmptyQuery(Storage storage, OrderingList orderings) { if (storage == null) { throw new IllegalArgumentException(); } mStorage = storage; - mOrderings = extractOrderingNames(orderings); + if (orderings == null) { + orderings = OrderingList.emptyList(); + } + mOrderings = orderings; } /** * @param storage required storage object * @param orderings optional order-by properties */ - public EmptyQuery(Storage storage, String[] orderings) { - if (storage == null) { - throw new IllegalArgumentException(); - } - mStorage = storage; - mOrderings = orderings == null ? EMPTY_ORDERINGS : orderings; + public EmptyQuery(Storage storage, String... orderings) { + this(storage, OrderingList.get(storage.getStorableType(), orderings)); } public Class getStorableType() { @@ -182,9 +180,8 @@ public final class EmptyQuery extends AbstractQuery { public Query not() throws FetchException { Query query = mStorage.query(); - String[] orderings = mOrderings; - if (orderings.length > 0) { - query = query.orderBy(orderings); + if (mOrderings.size() > 0) { + query = query.orderBy(mOrderings.asStringArray()); } return query; } @@ -246,13 +243,13 @@ public final class EmptyQuery extends AbstractQuery { app.append(", filter="); getFilter().appendTo(app); - if (mOrderings != null && mOrderings.length > 0) { + if (mOrderings != null && mOrderings.size() > 0) { app.append(", orderBy=["); - for (int i=0; i 0) { app.append(", "); } - app.append(mOrderings[i]); + app.append(mOrderings.get(i).toString()); } app.append(']'); } diff --git a/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java index dfe13cc..92c21dd 100644 --- a/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java +++ b/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java @@ -76,10 +76,19 @@ public class IndexedQueryAnalyzer { /** * @param filter optional filter which which must be {@link Filter#isBound * bound} and cannot contain any logical 'or' operations. - * @param orderings properties which define desired ordering * @throws IllegalArgumentException if filter is not supported */ - public Result analyze(Filter filter, OrderedProperty... orderings) { + public Result analyze(Filter filter) { + return analyze(filter, null); + } + + /** + * @param filter optional filter which which must be {@link Filter#isBound + * bound} and cannot contain any logical 'or' operations. + * @param orderings optional properties which define desired ordering + * @throws IllegalArgumentException if filter is not supported + */ + public Result analyze(Filter filter, List> orderings) { if (!filter.isBound()) { // Strictly speaking, this is not required, but it detects the // mistake of not properly calling initialFilterValues. diff --git a/src/main/java/com/amazon/carbonado/qe/OrderingList.java b/src/main/java/com/amazon/carbonado/qe/OrderingList.java new file mode 100644 index 0000000..907453d --- /dev/null +++ b/src/main/java/com/amazon/carbonado/qe/OrderingList.java @@ -0,0 +1,227 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * 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 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 com.amazon.carbonado.qe; + +import java.util.AbstractList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.cojen.util.SoftValuedHashMap; + +import com.amazon.carbonado.Storable; + +import com.amazon.carbonado.info.OrderedProperty; +import com.amazon.carbonado.info.StorableIntrospector; + +/** + * Produces unmodifiable lists of {@link OrderedProperty orderings}. Instances + * are immutable, canonical and cached. Calls to "equals" and "hashCode" are + * fast. + * + * @author Brian S O'Neill + */ +public class OrderingList extends AbstractList> { + private static final OrderingList EMPTY_LIST = new OrderingList(); + + private static final Map cCache; + + static { + cCache = new SoftValuedHashMap(); + } + + /** + * Returns a canonical empty instance. + */ + public static OrderingList emptyList() { + return EMPTY_LIST; + } + + /** + * Returns a canonical instance composed of the given ordering. + * + * @throws IllegalArgumentException if ordering property is not in S + */ + public static OrderingList get(Class type, String property) { + if (property == null) { + return EMPTY_LIST; + } + return getListNode(type).nextNode(type, property); + } + + /** + * Returns a canonical instance composed of the given orderings. + * + * @throws IllegalArgumentException if any ordering property is not in S + */ + public static OrderingList get(Class type, String... orderings) { + if (orderings == null || orderings.length == 0) { + return EMPTY_LIST; + } + + OrderingList node = getListNode(type); + for (String property : orderings) { + node = node.nextNode(type, property); + } + + return node; + } + + /** + * Returns a canonical instance composed of the given orderings. + */ + public static OrderingList get(OrderedProperty... orderings) { + if (orderings == null || orderings.length == 0) { + return EMPTY_LIST; + } + + Class type = orderings[0].getChainedProperty().getPrimeProperty().getEnclosingType(); + + OrderingList node = getListNode(type); + for (OrderedProperty property : orderings) { + node = node.nextNode(property); + } + + return node; + } + + private static OrderingList getListNode(Class type) { + OrderingList node; + synchronized (cCache) { + node = (OrderingList) cCache.get(type); + if (node == null) { + node = new OrderingList(); + cCache.put(type, node); + } + } + return node; + } + + private final OrderingList mParent; + private final OrderedProperty mProperty; + private final int mSize; + + private Map> mNextNode; + + private OrderedProperty[] mOrderings; + private String[] mOrderingStrings; + + private OrderingList() { + mParent = null; + mProperty = null; + mSize = 0; + } + + private OrderingList(OrderingList parent, OrderedProperty property) { + if (property == null) { + throw new IllegalArgumentException("Ordering property is null"); + } + mParent = parent; + mProperty = property; + mSize = parent.mSize + 1; + } + + public int size() { + return mSize; + } + + public OrderedProperty get(int index) { + return asArray()[index]; + } + + private OrderedProperty[] asArray() { + if (mOrderings == null) { + OrderedProperty[] orderings = new OrderedProperty[mSize]; + OrderingList node = this; + for (int i=mSize; --i>=0; ) { + orderings[i] = node.mProperty; + node = node.mParent; + } + mOrderings = orderings; + } + return mOrderings; + } + + /** + * Returns the orderings as qualified string property names. Each is + * prefixed with a '+' or '-'. + */ + String[] asStringArray() { + if (mOrderingStrings == null) { + String[] orderings = new String[mSize]; + OrderingList node = this; + for (int i=mSize; --i>=0; ) { + orderings[i] = node.mProperty.toString(); + node = node.mParent; + } + mOrderingStrings = orderings; + } + return mOrderingStrings; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + return super.equals(other); + } + + private synchronized OrderingList nextNode(Class type, String property) { + OrderingList node; + if (mNextNode == null) { + mNextNode = new HashMap>(); + node = null; + } else { + node = mNextNode.get(property); + } + + if (node == null) { + OrderedProperty op = OrderedProperty + .parse(StorableIntrospector.examine(type), property); + + node = nextNode(op); + mNextNode.put(property, node); + } + + return node; + } + + private synchronized OrderingList nextNode(OrderedProperty property) { + OrderingList node; + if (mNextNode == null) { + mNextNode = new HashMap>(); + node = null; + } else { + node = mNextNode.get(property); + } + + if (node == null) { + node = new OrderingList(this, property); + mNextNode.put(property, node); + } + + return node; + } +} diff --git a/src/main/java/com/amazon/carbonado/qe/OrderingScore.java b/src/main/java/com/amazon/carbonado/qe/OrderingScore.java index 76b273f..67edcdd 100644 --- a/src/main/java/com/amazon/carbonado/qe/OrderingScore.java +++ b/src/main/java/com/amazon/carbonado/qe/OrderingScore.java @@ -62,6 +62,21 @@ import com.amazon.carbonado.info.StorableIndex; * @see CompositeScore */ public class OrderingScore { + /** + * Evaluates the given index for its ordering capabilities against the + * given filter and order-by properties. + * + * @param index index to evaluate + * @param filter optional filter which cannot contain any logical 'or' operations. + * @throws IllegalArgumentException if index is null or filter is not supported + */ + public static OrderingScore evaluate + (StorableIndex index, + Filter filter) + { + return evaluate(index, filter, null); + } + /** * Evaluates the given index for its ordering capabilities against the * given filter and order-by properties. @@ -71,9 +86,10 @@ public class OrderingScore { * @param orderings properties which define desired ordering * @throws IllegalArgumentException if index is null or filter is not supported */ - public static OrderingScore evaluate(StorableIndex index, - Filter filter, - OrderedProperty... orderings) + public static OrderingScore evaluate + (StorableIndex index, + Filter filter, + List> orderings) { if (index == null) { throw new IllegalArgumentException("Index required"); @@ -86,6 +102,25 @@ public class OrderingScore { orderings); } + /** + * Evaluates the given index properties for its ordering capabilities + * against the given filter and order-by properties. + * + * @param indexProperties index properties to evaluate + * @param unique true if index is unique + * @param clustered true if index is clustered + * @param filter optional filter which cannot contain any logical 'or' operations. + * @throws IllegalArgumentException if index is null or filter is not supported + */ + public static OrderingScore evaluate + (OrderedProperty[] indexProperties, + boolean unique, + boolean clustered, + Filter filter) + { + return evaluate(indexProperties, unique, clustered, filter, null); + } + /** * Evaluates the given index properties for its ordering capabilities * against the given filter and order-by properties. @@ -102,7 +137,7 @@ public class OrderingScore { boolean unique, boolean clustered, Filter filter, - OrderedProperty... orderings) + List> orderings) { if (indexProperties == null) { throw new IllegalArgumentException("Index properties required"); @@ -111,7 +146,7 @@ public class OrderingScore { // Get filter list early to detect errors. List> filterList = PropertyFilterList.get(filter); - if (orderings == null || orderings.length == 0) { + if (orderings == null || orderings.size() == 0) { return new OrderingScore(clustered, indexProperties.length, null, null, false); } @@ -154,8 +189,8 @@ public class OrderingScore { int indexPos = 0; calcScore: - for (int i=0; i property = orderings[i]; + for (int i=0; i property = orderings.get(i); ChainedProperty chained = property.getChainedProperty(); if (seen.contains(chained)) { diff --git a/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java index 822cde8..48defa7 100644 --- a/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java @@ -54,6 +54,8 @@ public abstract class SortedQueryExecutor extends AbstractQu /** * @param executor executor to wrap + * @param handledOrderings optional handled orderings + * @param remainderOrderings required remainder orderings * @throws IllegalArgumentException if executor is null or if remainder * orderings is empty */ diff --git a/src/main/java/com/amazon/carbonado/qe/StandardQuery.java b/src/main/java/com/amazon/carbonado/qe/StandardQuery.java index 7152e55..8c423e6 100644 --- a/src/main/java/com/amazon/carbonado/qe/StandardQuery.java +++ b/src/main/java/com/amazon/carbonado/qe/StandardQuery.java @@ -38,6 +38,7 @@ import com.amazon.carbonado.filter.FilterValues; import com.amazon.carbonado.filter.OpenFilter; import com.amazon.carbonado.filter.RelOp; +import com.amazon.carbonado.info.Direction; import com.amazon.carbonado.info.OrderedProperty; import com.amazon.carbonado.util.Appender; @@ -53,27 +54,27 @@ public abstract class StandardQuery extends AbstractQuery // Values for this query, which may be null. private final FilterValues mValues; // Properties that this query is ordered by. - private final String[] mOrderings; + private final OrderingList mOrderings; private volatile QueryExecutor mExecutor; /** * @param values optional values object, defaults to open filter if null - * @param orderings optional order-by properties */ - // FIXME: remove this - protected StandardQuery(FilterValues values, OrderedProperty... orderings) { - this(values, extractOrderingNames(orderings)); + protected StandardQuery(FilterValues values) { + this(values, null); } /** * @param values optional values object, defaults to open filter if null - * @param orderings optional order-by properties, not cloned, which may be - * prefixed with '+' or '-' + * @param orderings optional order-by properties */ - protected StandardQuery(FilterValues values, String... orderings) { + protected StandardQuery(FilterValues values, OrderingList orderings) { mValues = values; - mOrderings = orderings == null ? EMPTY_ORDERINGS : orderings; + if (orderings == null) { + orderings = OrderingList.emptyList(); + } + mOrderings = orderings; } public Class getStorableType() { @@ -148,7 +149,7 @@ public abstract class StandardQuery extends AbstractQuery newQuery = getStorage().query(values.getFilter().and(filter)); newQuery = newQuery.withValues(values.getValues()); } - return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings); + return mOrderings.size() == 0 ? newQuery : newQuery.orderBy(mOrderings.asStringArray()); } public Query or(Filter filter) throws FetchException { @@ -158,7 +159,7 @@ public abstract class StandardQuery extends AbstractQuery } Query newQuery = getStorage().query(values.getFilter().or(filter)); newQuery = newQuery.withValues(values.getValues()); - return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings); + return mOrderings.size() == 0 ? newQuery : newQuery.orderBy(mOrderings.asStringArray()); } public Query not() throws FetchException { @@ -168,21 +169,15 @@ public abstract class StandardQuery extends AbstractQuery } Query newQuery = getStorage().query(values.getFilter().not()); newQuery = newQuery.withValues(values.getSuppliedValues()); - return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings); + return mOrderings.size() == 0 ? newQuery : newQuery.orderBy(mOrderings.asStringArray()); } public Query orderBy(String property) throws FetchException { - StandardQuery query = newInstance(mValues, property); - // Get executor to ensure order property is correct. - query.executor(); - return query; + return newInstance(mValues, OrderingList.get(getStorableType(), property)); } public Query orderBy(String... properties) throws FetchException { - StandardQuery query = newInstance(mValues, properties); - // Get executor to ensure order properties are correct. - query.executor(); - return query; + return newInstance(mValues, OrderingList.get(getStorableType(), properties)); } public Cursor fetch() throws FetchException { @@ -190,8 +185,8 @@ public abstract class StandardQuery extends AbstractQuery } public Cursor fetchAfter(S start) throws FetchException { - String[] orderings; - if (start == null || (orderings = mOrderings).length == 0) { + OrderingList orderings; + if (start == null || (orderings = mOrderings).size() == 0) { return fetch(); } @@ -200,24 +195,21 @@ public abstract class StandardQuery extends AbstractQuery Filter lastSubFilter = Filter.getOpenFilter(storableType); BeanPropertyAccessor accessor = BeanPropertyAccessor.forClass(storableType); - Object[] values = new Object[orderings.length]; + Object[] values = new Object[orderings.size()]; for (int i=0;;) { - String propertyName = orderings[i]; + OrderedProperty property = orderings.get(i); RelOp operator = RelOp.GT; - char c = propertyName.charAt(0); - if (c == '-') { - propertyName = propertyName.substring(1); + if (property.getDirection() == Direction.DESCENDING) { operator = RelOp.LT; - } else if (c == '+') { - propertyName = propertyName.substring(1); } + String propertyName = property.getChainedProperty().toString(); values[i] = accessor.getPropertyValue(start, propertyName); orderFilter = orderFilter.or(lastSubFilter.and(propertyName, operator)); - if (++i >= orderings.length) { + if (++i >= orderings.size()) { break; } @@ -336,13 +328,13 @@ public abstract class StandardQuery extends AbstractQuery app.append('"'); } - if (mOrderings != null && mOrderings.length > 0) { + if (mOrderings != null && mOrderings.size() > 0) { app.append(", orderBy=["); - for (int i=0; i 0) { app.append(", "); } - app.append(mOrderings[i]); + app.append(mOrderings.get(i).toString()); } app.append(']'); } @@ -375,10 +367,10 @@ public abstract class StandardQuery extends AbstractQuery * Return a new or cached executor. * * @param values optional values object, defaults to open filter if null - * @param orderings optional order-by properties, which may be prefixed - * with '+' or '-' + * @param orderings order-by properties, never null */ - protected abstract QueryExecutor getExecutor(FilterValues values, String... orderings); + protected abstract QueryExecutor getExecutor(FilterValues values, + OrderingList orderings); /** * Return a new or cached instance of StandardQuery implementation, using @@ -388,7 +380,8 @@ public abstract class StandardQuery extends AbstractQuery * @param values optional values object, defaults to open filter if null * @param orderings order-by properties, never null */ - protected abstract StandardQuery newInstance(FilterValues values, String... orderings); + protected abstract StandardQuery newInstance(FilterValues values, + OrderingList orderings); private StandardQuery newInstance(FilterValues values) { return newInstance(values, mOrderings); diff --git a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java index 9563097..b4e45fa 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.Collections; import java.util.List; import com.amazon.carbonado.Storable; @@ -58,9 +59,16 @@ public class UnionQueryAnalyzer { /** * @param filter optional filter which must be {@link Filter#isBound bound} - * @param orderings properties which define desired ordering */ - public Result analyze(Filter filter, OrderedProperty... orderings) { + public Result analyze(Filter filter) { + return analyze(filter, null); + } + + /** + * @param filter optional filter which must be {@link Filter#isBound bound} + * @param orderings optional properties which define desired ordering + */ + public Result analyze(Filter filter, List> orderings) { if (!filter.isBound()) { // Strictly speaking, this is not required, but it detects the // mistake of not properly calling initialFilterValues. @@ -87,7 +95,7 @@ public class UnionQueryAnalyzer { } private List.Result> - splitIntoSubResults(Filter filter, OrderedProperty... orderings) + splitIntoSubResults(Filter filter, List> orderings) { Splitter splitter = new Splitter(orderings); filter.accept(splitter, null); @@ -174,11 +182,11 @@ public class UnionQueryAnalyzer { * only contain 'and' operations. */ private class Splitter extends Visitor { - private final OrderedProperty[] mOrderings; + private final List> mOrderings; final List.Result> mSubResults; - Splitter(OrderedProperty... orderings) { + Splitter(List> orderings) { mOrderings = orderings; mSubResults = new ArrayList.Result>(); } diff --git a/src/main/java/com/amazon/carbonado/spi/BaseQuery.java b/src/main/java/com/amazon/carbonado/spi/BaseQuery.java index e1117cd..b1a1d45 100644 --- a/src/main/java/com/amazon/carbonado/spi/BaseQuery.java +++ b/src/main/java/com/amazon/carbonado/spi/BaseQuery.java @@ -49,6 +49,7 @@ import com.amazon.carbonado.qe.EmptyQuery; * BaseQuery supports binding filters to values. * * @author Brian S O'Neill + * @deprecated Use {@link com.amazon.carbonado.qe.StandardQuery} */ public abstract class BaseQuery extends AbstractQuery implements Appender { /** @@ -61,6 +62,20 @@ public abstract class BaseQuery extends AbstractQuery imp } } + protected static final String[] EMPTY_ORDERINGS = {}; + + protected static String[] extractOrderingNames(OrderedProperty[] orderings) { + String[] orderingStrings; + if (orderings == null || orderings.length == 0) { + return EMPTY_ORDERINGS; + } + orderingStrings = new String[orderings.length]; + for (int i=0; i mStorage; // Values for this query. diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java index 3f865e6..b3eef91 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java @@ -21,6 +21,7 @@ package com.amazon.carbonado.qe; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; import junit.framework.TestCase; import junit.framework.TestSuite; @@ -56,7 +57,8 @@ public class TestIndexedQueryExecutor extends TestCase { return TestOrderingScore.makeIndex(type, props); } - static OrderedProperty[] makeOrderings(Class type, String... props) + static List> makeOrderings(Class type, + String... props) { return TestOrderingScore.makeOrderings(type, props); } @@ -679,9 +681,9 @@ public class TestIndexedQueryExecutor extends TestCase { assertTrue(executor.mReverseOrder); assertEquals(values.getFilter(), executor.getFilter()); - OrderedProperty[] expectedOrdering = + List> expectedOrdering = makeOrderings(StorableTestBasic.class, "intProp", "-doubleProp", "stringProp"); - assertEquals(Arrays.asList(expectedOrdering), executor.getOrdering()); + assertEquals(expectedOrdering, executor.getOrdering()); } /** diff --git a/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java b/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java new file mode 100644 index 0000000..b696984 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java @@ -0,0 +1,110 @@ +/* + * Copyright 2006 Amazon Technologies, Inc. or its affiliates. + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks + * of Amazon Technologies, Inc. or its affiliates. All rights reserved. + * + * 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 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 com.amazon.carbonado.qe; + +import java.util.List; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import com.amazon.carbonado.info.OrderedProperty; + +import com.amazon.carbonado.stored.StorableTestBasic; + +/** + * + * + * @author Brian S O'Neill + */ +public class TestOrderingList extends TestCase { + public static void main(String[] args) { + junit.textui.TestRunner.run(suite()); + } + + public static TestSuite suite() { + return new TestSuite(TestOrderingList.class); + } + + public TestOrderingList(String name) { + super(name); + } + + public void testEmpty() throws Exception { + assertEquals(0, OrderingList.get(StorableTestBasic.class).size()); + } + + public void testSingle() throws Exception { + List> list_1 = + OrderingList.get(StorableTestBasic.class, "date"); + + assertEquals(1, list_1.size()); + assertEquals("+date", list_1.get(0).toString()); + + List> list_2 = + OrderingList.get(StorableTestBasic.class, "+date"); + + assertEquals(1, list_2.size()); + assertEquals("+date", list_2.get(0).toString()); + assertEquals(list_1, list_2); + assertTrue(list_1 == list_2); + + List> list_3 = + OrderingList.get(StorableTestBasic.class, "-date"); + + assertEquals(1, list_3.size()); + assertEquals("-date", list_3.get(0).toString()); + assertFalse(list_2.equals(list_3)); + assertFalse(list_2 == list_3); + } + + public void testDouble() throws Exception { + List> list_1 = + OrderingList.get(StorableTestBasic.class, "date", "intProp"); + + assertEquals(2, list_1.size()); + assertEquals("+date", list_1.get(0).toString()); + assertEquals("+intProp", list_1.get(1).toString()); + + List> list_2 = + OrderingList.get(StorableTestBasic.class, "+date", "+intProp"); + + assertEquals(2, list_2.size()); + assertEquals("+date", list_2.get(0).toString()); + assertEquals("+intProp", list_2.get(1).toString()); + assertEquals(list_1, list_2); + assertTrue(list_1 == list_2); + + List> list_3 = + OrderingList.get(StorableTestBasic.class, "-date", "-intProp"); + + assertEquals(2, list_3.size()); + assertEquals("-date", list_3.get(0).toString()); + assertEquals("-intProp", list_3.get(1).toString()); + assertFalse(list_2.equals(list_3)); + assertFalse(list_2 == list_3); + } + + public void testIllegal() throws Exception { + try { + OrderingList.get(StorableTestBasic.class, "foo"); + fail(); + } catch (IllegalArgumentException e) { + } + } +} diff --git a/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java b/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java index 3929f59..1545b70 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java +++ b/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java @@ -18,6 +18,8 @@ package com.amazon.carbonado.qe; +import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -53,15 +55,19 @@ public class TestOrderingScore extends TestCase { } static StorableIndex makeIndex(Class type, String... props) { - return new StorableIndex(makeOrderings(type, props), UNSPECIFIED); + List> list = makeOrderings(type, props); + OrderedProperty[] array = list.toArray(new OrderedProperty[0]); + + return new StorableIndex(array, UNSPECIFIED); } - static OrderedProperty[] makeOrderings(Class type, String... props) + static List> makeOrderings(Class type, + String... props) { StorableInfo info = StorableIntrospector.examine(type); - OrderedProperty[] ops = new OrderedProperty[props.length]; + List> ops = new ArrayList>(props.length); for (int i=0; i ix; - OrderedProperty[] ops; + List> ops; OrderingScore score; ///////////// @@ -180,7 +186,7 @@ public class TestOrderingScore extends TestCase { public void testMultipleProps() throws Exception { final StorableIndex ix; - OrderedProperty[] ops; + List> ops; OrderingScore score; ix = makeIndex(StorableTestBasic.class, "id", "intProp"); @@ -317,7 +323,7 @@ public class TestOrderingScore extends TestCase { public void testMidGap() throws Exception { final StorableIndex ix; - OrderedProperty[] ops; + List> ops; OrderingScore score; Filter filter; @@ -401,7 +407,7 @@ public class TestOrderingScore extends TestCase { public void testComparator() throws Exception { StorableIndex ix_1, ix_2; - OrderedProperty[] ops; + List> ops; OrderingScore score_1, score_2; Filter filter; Comparator> comp = OrderingScore.fullComparator(); @@ -466,7 +472,7 @@ public class TestOrderingScore extends TestCase { // properties are filtered out. Thus the index is not needed. final StorableIndex ix; - OrderedProperty[] ops; + List> ops; OrderingScore score; Filter filter; @@ -501,7 +507,7 @@ public class TestOrderingScore extends TestCase { // Test a unique index which has been fully specified. Ordering is not // needed at all. final StorableIndex ix; - OrderedProperty[] ops; + List> ops; OrderingScore score; Filter filter; diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java index dd29e4b..01f9fbf 100644 --- a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java +++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java @@ -48,10 +48,11 @@ import com.amazon.carbonado.filter.FilterValues; import com.amazon.carbonado.info.OrderedProperty; import com.amazon.carbonado.info.StorableIntrospector; +import com.amazon.carbonado.qe.ArraySortedQueryExecutor; import com.amazon.carbonado.qe.FilteredQueryExecutor; import com.amazon.carbonado.qe.IterableQueryExecutor; +import com.amazon.carbonado.qe.OrderingList; import com.amazon.carbonado.qe.QueryExecutor; -import com.amazon.carbonado.qe.SortedQueryExecutor; import com.amazon.carbonado.qe.StandardQuery; /** @@ -89,7 +90,7 @@ public class ToyStorage implements Storage, MasterSupport } public Query query() throws FetchException { - return new ToyQuery(null); + return new ToyQuery(null, null); } public Query query(String filter) throws FetchException { @@ -97,7 +98,7 @@ public class ToyStorage implements Storage, MasterSupport } public Query query(Filter filter) throws FetchException { - return new ToyQuery(filter.initialFilterValues()); + return new ToyQuery(filter.initialFilterValues(), null); } public boolean addTrigger(Trigger trigger) { @@ -211,7 +212,7 @@ public class ToyStorage implements Storage, MasterSupport } private class ToyQuery extends StandardQuery { - ToyQuery(FilterValues values, String... orderings) { + ToyQuery(FilterValues values, OrderingList orderings) { super(values, orderings); } @@ -223,19 +224,21 @@ public class ToyStorage implements Storage, MasterSupport return mRepo.enterTransaction(level); } - protected QueryExecutor getExecutor(FilterValues values, String... orderings) { + protected QueryExecutor getExecutor(FilterValues values, OrderingList orderings) { QueryExecutor executor = new IterableQueryExecutor(mType, mData, mDataLock); if (values != null) { executor = new FilteredQueryExecutor(executor, values.getFilter()); } - // FIXME: sorting + if (orderings.size() > 0) { + executor = new ArraySortedQueryExecutor(executor, null, orderings); + } return executor; } - protected StandardQuery newInstance(FilterValues values, String... orderings) { + protected StandardQuery newInstance(FilterValues values, OrderingList orderings) { return new ToyQuery(values, orderings); } } -- cgit v1.2.3