summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian S. O'Neill <bronee@gmail.com>2006-09-03 21:48:14 +0000
committerBrian S. O'Neill <bronee@gmail.com>2006-09-03 21:48:14 +0000
commit1e947afa4b660a23a2dcb57463dd810fb73e6030 (patch)
treef8fdb7e2530bc07820f51d30c8e08cf737d6c5ac
parent39fce59a840b723eb013bc79285687986592b2da (diff)
Manage ordering properties with lists.
-rw-r--r--src/main/java/com/amazon/carbonado/qe/AbstractQuery.java16
-rw-r--r--src/main/java/com/amazon/carbonado/qe/CompositeScore.java44
-rw-r--r--src/main/java/com/amazon/carbonado/qe/EmptyQuery.java29
-rw-r--r--src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java13
-rw-r--r--src/main/java/com/amazon/carbonado/qe/OrderingList.java227
-rw-r--r--src/main/java/com/amazon/carbonado/qe/OrderingScore.java49
-rw-r--r--src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java2
-rw-r--r--src/main/java/com/amazon/carbonado/qe/StandardQuery.java67
-rw-r--r--src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java18
-rw-r--r--src/main/java/com/amazon/carbonado/spi/BaseQuery.java15
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java8
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestOrderingList.java110
-rw-r--r--src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java26
-rw-r--r--src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java17
14 files changed, 534 insertions, 107 deletions
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<S extends Storable> implements Query<S>, 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<orderingStrings.length; i++) {
- orderingStrings[i] = orderings[i].toString().intern();
- }
- return orderingStrings;
- }
-
protected AbstractQuery() {
}
diff --git a/src/main/java/com/amazon/carbonado/qe/CompositeScore.java b/src/main/java/com/amazon/carbonado/qe/CompositeScore.java
index 7028c54..be4ceb7 100644
--- a/src/main/java/com/amazon/carbonado/qe/CompositeScore.java
+++ b/src/main/java/com/amazon/carbonado/qe/CompositeScore.java
@@ -19,6 +19,7 @@
package com.amazon.carbonado.qe;
import java.util.Comparator;
+import java.util.Collections;
import java.util.List;
import com.amazon.carbonado.Storable;
@@ -44,12 +45,28 @@ public class CompositeScore<S extends Storable> {
*
* @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 <S extends Storable> CompositeScore<S> evaluate
+ (StorableIndex<S> index,
+ Filter<S> filter)
+ {
+ return evaluate(index, filter, null);
+ }
+
+ /**
+ * 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.
* @param orderings properties which define desired ordering
* @throws IllegalArgumentException if index is null or filter is not supported
*/
- public static <S extends Storable> CompositeScore<S> evaluate(StorableIndex<S> index,
- Filter<S> filter,
- OrderedProperty<S>... orderings)
+ public static <S extends Storable> CompositeScore<S> evaluate
+ (StorableIndex<S> index,
+ Filter<S> filter,
+ List<OrderedProperty<S>> orderings)
{
if (index == null) {
throw new IllegalArgumentException("Index required");
@@ -70,6 +87,25 @@ public class CompositeScore<S extends Storable> {
* @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 <S extends Storable> CompositeScore<S> evaluate
+ (OrderedProperty<S>[] indexProperties,
+ boolean unique,
+ boolean clustered,
+ Filter<S> 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.
+ *
+ * @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.
* @param orderings properties which define desired ordering
* @throws IllegalArgumentException if index is null or filter is not supported
*/
@@ -78,7 +114,7 @@ public class CompositeScore<S extends Storable> {
boolean unique,
boolean clustered,
Filter<S> filter,
- OrderedProperty<S>... orderings)
+ List<OrderedProperty<S>> orderings)
{
FilteringScore<S> 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<S extends Storable> extends AbstractQuery<S> {
private final Storage<S> mStorage;
// Properties that this query is ordered by.
- private final String[] mOrderings;
+ private final OrderingList<S> mOrderings;
/**
* @param storage required storage object
* @param orderings optional order-by properties
*/
- // FIXME: remove this
- public EmptyQuery(Storage<S> storage, OrderedProperty[] orderings) {
+ public EmptyQuery(Storage<S> storage, OrderingList<S> 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<S> storage, String[] orderings) {
- if (storage == null) {
- throw new IllegalArgumentException();
- }
- mStorage = storage;
- mOrderings = orderings == null ? EMPTY_ORDERINGS : orderings;
+ public EmptyQuery(Storage<S> storage, String... orderings) {
+ this(storage, OrderingList.get(storage.getStorableType(), orderings));
}
public Class<S> getStorableType() {
@@ -182,9 +180,8 @@ public final class EmptyQuery<S extends Storable> extends AbstractQuery<S> {
public Query<S> not() throws FetchException {
Query<S> 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<S extends Storable> extends AbstractQuery<S> {
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<mOrderings.length; i++) {
+ for (int i=0; i<mOrderings.size(); i++) {
if (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<S extends Storable> {
/**
* @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<S> filter, OrderedProperty<S>... orderings) {
+ public Result analyze(Filter<S> 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<S> filter, List<OrderedProperty<S>> 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<S extends Storable> extends AbstractList<OrderedProperty<S>> {
+ private static final OrderingList EMPTY_LIST = new OrderingList();
+
+ private static final Map<Class, OrderingList> cCache;
+
+ static {
+ cCache = new SoftValuedHashMap();
+ }
+
+ /**
+ * Returns a canonical empty instance.
+ */
+ public static <S extends Storable> OrderingList<S> emptyList() {
+ return EMPTY_LIST;
+ }
+
+ /**
+ * Returns a canonical instance composed of the given ordering.
+ *
+ * @throws IllegalArgumentException if ordering property is not in S
+ */
+ public static <S extends Storable> OrderingList<S> get(Class<S> 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 <S extends Storable> OrderingList<S> get(Class<S> type, String... orderings) {
+ if (orderings == null || orderings.length == 0) {
+ return EMPTY_LIST;
+ }
+
+ OrderingList<S> 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 <S extends Storable> OrderingList<S> get(OrderedProperty<S>... orderings) {
+ if (orderings == null || orderings.length == 0) {
+ return EMPTY_LIST;
+ }
+
+ Class<S> type = orderings[0].getChainedProperty().getPrimeProperty().getEnclosingType();
+
+ OrderingList<S> node = getListNode(type);
+ for (OrderedProperty<S> property : orderings) {
+ node = node.nextNode(property);
+ }
+
+ return node;
+ }
+
+ private static <S extends Storable> OrderingList<S> getListNode(Class<S> type) {
+ OrderingList<S> node;
+ synchronized (cCache) {
+ node = (OrderingList<S>) cCache.get(type);
+ if (node == null) {
+ node = new OrderingList<S>();
+ cCache.put(type, node);
+ }
+ }
+ return node;
+ }
+
+ private final OrderingList<S> mParent;
+ private final OrderedProperty<S> mProperty;
+ private final int mSize;
+
+ private Map<Object, OrderingList<S>> mNextNode;
+
+ private OrderedProperty<S>[] mOrderings;
+ private String[] mOrderingStrings;
+
+ private OrderingList() {
+ mParent = null;
+ mProperty = null;
+ mSize = 0;
+ }
+
+ private OrderingList(OrderingList<S> parent, OrderedProperty<S> 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<S> get(int index) {
+ return asArray()[index];
+ }
+
+ private OrderedProperty<S>[] asArray() {
+ if (mOrderings == null) {
+ OrderedProperty<S>[] orderings = new OrderedProperty[mSize];
+ OrderingList<S> 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<S> 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<S> nextNode(Class<S> type, String property) {
+ OrderingList<S> node;
+ if (mNextNode == null) {
+ mNextNode = new HashMap<Object, OrderingList<S>>();
+ node = null;
+ } else {
+ node = mNextNode.get(property);
+ }
+
+ if (node == null) {
+ OrderedProperty<S> op = OrderedProperty
+ .parse(StorableIntrospector.examine(type), property);
+
+ node = nextNode(op);
+ mNextNode.put(property, node);
+ }
+
+ return node;
+ }
+
+ private synchronized OrderingList<S> nextNode(OrderedProperty<S> property) {
+ OrderingList<S> node;
+ if (mNextNode == null) {
+ mNextNode = new HashMap<Object, OrderingList<S>>();
+ node = null;
+ } else {
+ node = mNextNode.get(property);
+ }
+
+ if (node == null) {
+ node = new OrderingList<S>(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
@@ -68,12 +68,28 @@ public class OrderingScore<S extends Storable> {
*
* @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 <S extends Storable> OrderingScore<S> evaluate
+ (StorableIndex<S> index,
+ Filter<S> filter)
+ {
+ return evaluate(index, filter, null);
+ }
+
+ /**
+ * 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.
* @param orderings properties which define desired ordering
* @throws IllegalArgumentException if index is null or filter is not supported
*/
- public static <S extends Storable> OrderingScore<S> evaluate(StorableIndex<S> index,
- Filter<S> filter,
- OrderedProperty<S>... orderings)
+ public static <S extends Storable> OrderingScore<S> evaluate
+ (StorableIndex<S> index,
+ Filter<S> filter,
+ List<OrderedProperty<S>> orderings)
{
if (index == null) {
throw new IllegalArgumentException("Index required");
@@ -94,6 +110,25 @@ public class OrderingScore<S extends Storable> {
* @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 <S extends Storable> OrderingScore<S> evaluate
+ (OrderedProperty<S>[] indexProperties,
+ boolean unique,
+ boolean clustered,
+ Filter<S> 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.
+ *
+ * @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.
* @param orderings properties which define desired ordering
* @throws IllegalArgumentException if index is null or filter is not supported
*/
@@ -102,7 +137,7 @@ public class OrderingScore<S extends Storable> {
boolean unique,
boolean clustered,
Filter<S> filter,
- OrderedProperty<S>... orderings)
+ List<OrderedProperty<S>> orderings)
{
if (indexProperties == null) {
throw new IllegalArgumentException("Index properties required");
@@ -111,7 +146,7 @@ public class OrderingScore<S extends Storable> {
// Get filter list early to detect errors.
List<PropertyFilter<S>> filterList = PropertyFilterList.get(filter);
- if (orderings == null || orderings.length == 0) {
+ if (orderings == null || orderings.size() == 0) {
return new OrderingScore<S>(clustered, indexProperties.length, null, null, false);
}
@@ -154,8 +189,8 @@ public class OrderingScore<S extends Storable> {
int indexPos = 0;
calcScore:
- for (int i=0; i<orderings.length; i++) {
- OrderedProperty<S> property = orderings[i];
+ for (int i=0; i<orderings.size(); i++) {
+ OrderedProperty<S> property = orderings.get(i);
ChainedProperty<S> 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<S extends Storable> 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<S extends Storable> extends AbstractQuery<S>
// Values for this query, which may be null.
private final FilterValues<S> mValues;
// Properties that this query is ordered by.
- private final String[] mOrderings;
+ private final OrderingList<S> mOrderings;
private volatile QueryExecutor<S> mExecutor;
/**
* @param values optional values object, defaults to open filter if null
- * @param orderings optional order-by properties
*/
- // FIXME: remove this
- protected StandardQuery(FilterValues<S> values, OrderedProperty<S>... orderings) {
- this(values, extractOrderingNames(orderings));
+ protected StandardQuery(FilterValues<S> 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<S> values, String... orderings) {
+ protected StandardQuery(FilterValues<S> values, OrderingList<S> orderings) {
mValues = values;
- mOrderings = orderings == null ? EMPTY_ORDERINGS : orderings;
+ if (orderings == null) {
+ orderings = OrderingList.emptyList();
+ }
+ mOrderings = orderings;
}
public Class<S> getStorableType() {
@@ -148,7 +149,7 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>
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<S> or(Filter<S> filter) throws FetchException {
@@ -158,7 +159,7 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>
}
Query<S> 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<S> not() throws FetchException {
@@ -168,21 +169,15 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>
}
Query<S> 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<S> orderBy(String property) throws FetchException {
- StandardQuery<S> 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<S> orderBy(String... properties) throws FetchException {
- StandardQuery<S> 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<S> fetch() throws FetchException {
@@ -190,8 +185,8 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>
}
public Cursor<S> fetchAfter(S start) throws FetchException {
- String[] orderings;
- if (start == null || (orderings = mOrderings).length == 0) {
+ OrderingList<S> orderings;
+ if (start == null || (orderings = mOrderings).size() == 0) {
return fetch();
}
@@ -200,24 +195,21 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>
Filter<S> 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<S> 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<S extends Storable> extends AbstractQuery<S>
app.append('"');
}
- if (mOrderings != null && mOrderings.length > 0) {
+ if (mOrderings != null && mOrderings.size() > 0) {
app.append(", orderBy=[");
- for (int i=0; i<mOrderings.length; i++) {
+ for (int i=0; i<mOrderings.size(); i++) {
if (i > 0) {
app.append(", ");
}
- app.append(mOrderings[i]);
+ app.append(mOrderings.get(i).toString());
}
app.append(']');
}
@@ -375,10 +367,10 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>
* 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<S> getExecutor(FilterValues<S> values, String... orderings);
+ protected abstract QueryExecutor<S> getExecutor(FilterValues<S> values,
+ OrderingList<S> orderings);
/**
* Return a new or cached instance of StandardQuery implementation, using
@@ -388,7 +380,8 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>
* @param values optional values object, defaults to open filter if null
* @param orderings order-by properties, never null
*/
- protected abstract StandardQuery<S> newInstance(FilterValues<S> values, String... orderings);
+ protected abstract StandardQuery<S> newInstance(FilterValues<S> values,
+ OrderingList<S> orderings);
private StandardQuery<S> newInstance(FilterValues<S> 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<S extends Storable> {
/**
* @param filter optional filter which must be {@link Filter#isBound bound}
- * @param orderings properties which define desired ordering
*/
- public Result analyze(Filter<S> filter, OrderedProperty<S>... orderings) {
+ public Result analyze(Filter<S> 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<S> filter, List<OrderedProperty<S>> 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<S extends Storable> {
}
private List<IndexedQueryAnalyzer<S>.Result>
- splitIntoSubResults(Filter<S> filter, OrderedProperty<S>... orderings)
+ splitIntoSubResults(Filter<S> filter, List<OrderedProperty<S>> orderings)
{
Splitter splitter = new Splitter(orderings);
filter.accept(splitter, null);
@@ -174,11 +182,11 @@ public class UnionQueryAnalyzer<S extends Storable> {
* only contain 'and' operations.
*/
private class Splitter extends Visitor<S, Object, Object> {
- private final OrderedProperty<S>[] mOrderings;
+ private final List<OrderedProperty<S>> mOrderings;
final List<IndexedQueryAnalyzer<S>.Result> mSubResults;
- Splitter(OrderedProperty<S>... orderings) {
+ Splitter(List<OrderedProperty<S>> orderings) {
mOrderings = orderings;
mSubResults = new ArrayList<IndexedQueryAnalyzer<S>.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<S extends Storable> extends AbstractQuery<S> implements Appender {
/**
@@ -61,6 +62,20 @@ public abstract class BaseQuery<S extends Storable> extends AbstractQuery<S> 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<orderingStrings.length; i++) {
+ orderingStrings[i] = orderings[i].toString().intern();
+ }
+ return orderingStrings;
+ }
+
private final Repository mRepository;
private final Storage<S> 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 <S extends Storable> OrderedProperty<S>[] makeOrderings(Class<S> type, String... props)
+ static <S extends Storable> List<OrderedProperty<S>> makeOrderings(Class<S> 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<StorableTestBasic>[] expectedOrdering =
+ List<OrderedProperty<StorableTestBasic>> 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<OrderedProperty<StorableTestBasic>> list_1 =
+ OrderingList.get(StorableTestBasic.class, "date");
+
+ assertEquals(1, list_1.size());
+ assertEquals("+date", list_1.get(0).toString());
+
+ List<OrderedProperty<StorableTestBasic>> 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<OrderedProperty<StorableTestBasic>> 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<OrderedProperty<StorableTestBasic>> 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<OrderedProperty<StorableTestBasic>> 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<OrderedProperty<StorableTestBasic>> 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 <S extends Storable> StorableIndex<S> makeIndex(Class<S> type, String... props) {
- return new StorableIndex<S>(makeOrderings(type, props), UNSPECIFIED);
+ List<OrderedProperty<S>> list = makeOrderings(type, props);
+ OrderedProperty<S>[] array = list.toArray(new OrderedProperty[0]);
+
+ return new StorableIndex<S>(array, UNSPECIFIED);
}
- static <S extends Storable> OrderedProperty<S>[] makeOrderings(Class<S> type, String... props)
+ static <S extends Storable> List<OrderedProperty<S>> makeOrderings(Class<S> type,
+ String... props)
{
StorableInfo<S> info = StorableIntrospector.examine(type);
- OrderedProperty<S>[] ops = new OrderedProperty[props.length];
+ List<OrderedProperty<S>> ops = new ArrayList<OrderedProperty<S>>(props.length);
for (int i=0; i<props.length; i++) {
- ops[i] = OrderedProperty.parse(info, props[i]);
+ ops.add(OrderedProperty.parse(info, props[i]));
}
return ops;
}
@@ -82,7 +88,7 @@ public class TestOrderingScore extends TestCase {
public void testOneProp() throws Exception {
StorableIndex<StorableTestBasic> ix;
- OrderedProperty<StorableTestBasic>[] ops;
+ List<OrderedProperty<StorableTestBasic>> ops;
OrderingScore<StorableTestBasic> score;
/////////////
@@ -180,7 +186,7 @@ public class TestOrderingScore extends TestCase {
public void testMultipleProps() throws Exception {
final StorableIndex<StorableTestBasic> ix;
- OrderedProperty<StorableTestBasic>[] ops;
+ List<OrderedProperty<StorableTestBasic>> ops;
OrderingScore<StorableTestBasic> score;
ix = makeIndex(StorableTestBasic.class, "id", "intProp");
@@ -317,7 +323,7 @@ public class TestOrderingScore extends TestCase {
public void testMidGap() throws Exception {
final StorableIndex<StorableTestBasic> ix;
- OrderedProperty<StorableTestBasic>[] ops;
+ List<OrderedProperty<StorableTestBasic>> ops;
OrderingScore score;
Filter<StorableTestBasic> filter;
@@ -401,7 +407,7 @@ public class TestOrderingScore extends TestCase {
public void testComparator() throws Exception {
StorableIndex<StorableTestBasic> ix_1, ix_2;
- OrderedProperty<StorableTestBasic>[] ops;
+ List<OrderedProperty<StorableTestBasic>> ops;
OrderingScore score_1, score_2;
Filter<StorableTestBasic> filter;
Comparator<OrderingScore<?>> comp = OrderingScore.fullComparator();
@@ -466,7 +472,7 @@ public class TestOrderingScore extends TestCase {
// properties are filtered out. Thus the index is not needed.
final StorableIndex<StorableTestBasic> ix;
- OrderedProperty<StorableTestBasic>[] ops;
+ List<OrderedProperty<StorableTestBasic>> ops;
OrderingScore score;
Filter<StorableTestBasic> 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<StorableTestBasic> ix;
- OrderedProperty<StorableTestBasic>[] ops;
+ List<OrderedProperty<StorableTestBasic>> ops;
OrderingScore score;
Filter<StorableTestBasic> 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<S extends Storable> implements Storage<S>, MasterSupport
}
public Query<S> query() throws FetchException {
- return new ToyQuery(null);
+ return new ToyQuery(null, null);
}
public Query<S> query(String filter) throws FetchException {
@@ -97,7 +98,7 @@ public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport
}
public Query<S> query(Filter<S> filter) throws FetchException {
- return new ToyQuery(filter.initialFilterValues());
+ return new ToyQuery(filter.initialFilterValues(), null);
}
public boolean addTrigger(Trigger<? super S> trigger) {
@@ -211,7 +212,7 @@ public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport
}
private class ToyQuery extends StandardQuery<S> {
- ToyQuery(FilterValues<S> values, String... orderings) {
+ ToyQuery(FilterValues<S> values, OrderingList<S> orderings) {
super(values, orderings);
}
@@ -223,19 +224,21 @@ public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport
return mRepo.enterTransaction(level);
}
- protected QueryExecutor<S> getExecutor(FilterValues<S> values, String... orderings) {
+ protected QueryExecutor<S> getExecutor(FilterValues<S> values, OrderingList<S> orderings) {
QueryExecutor<S> executor = new IterableQueryExecutor<S>(mType, mData, mDataLock);
if (values != null) {
executor = new FilteredQueryExecutor<S>(executor, values.getFilter());
}
- // FIXME: sorting
+ if (orderings.size() > 0) {
+ executor = new ArraySortedQueryExecutor<S>(executor, null, orderings);
+ }
return executor;
}
- protected StandardQuery<S> newInstance(FilterValues<S> values, String... orderings) {
+ protected StandardQuery<S> newInstance(FilterValues<S> values, OrderingList<S> orderings) {
return new ToyQuery(values, orderings);
}
}