From a2b87f48775ca6687de0eb4af4b846bd1f0cdebf Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Sun, 14 Oct 2007 02:31:22 +0000 Subject: Added support for "where exists" in queries via new syntax. --- .../java/com/amazon/carbonado/qe/EmptyQuery.java | 5 +- .../amazon/carbonado/qe/FilteredQueryExecutor.java | 10 ++- .../amazon/carbonado/qe/JoinedQueryExecutor.java | 3 +- .../java/com/amazon/carbonado/qe/QueryEngine.java | 17 +++-- .../java/com/amazon/carbonado/qe/QueryFactory.java | 7 +- .../com/amazon/carbonado/qe/StandardQuery.java | 89 ++++++++++++++-------- .../amazon/carbonado/qe/StandardQueryFactory.java | 30 +++++--- .../amazon/carbonado/qe/UnionQueryAnalyzer.java | 12 +++ 8 files changed, 115 insertions(+), 58 deletions(-) (limited to 'src/main/java/com/amazon/carbonado/qe') diff --git a/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java b/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java index 1a970e0..626b435 100644 --- a/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java +++ b/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java @@ -194,15 +194,14 @@ public final class EmptyQuery extends AbstractQuery { } public Query or(Filter filter) throws FetchException { - FilterValues values = filter == null ? null : filter.initialFilterValues(); - return mFactory.query(values, mOrdering); + return mFactory.query(filter, null, mOrdering); } /** * Returns a query that fetches everything, possibly in a specified order. */ public Query not() throws FetchException { - return mFactory.query(null, mOrdering); + return mFactory.query(null, null, mOrdering); } public Query orderBy(String property) throws FetchException { diff --git a/src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java index d431b3d..494d353 100644 --- a/src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/FilteredQueryExecutor.java @@ -26,10 +26,8 @@ import com.amazon.carbonado.Storable; import com.amazon.carbonado.cursor.FilteredCursor; -import com.amazon.carbonado.filter.ClosedFilter; import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.filter.FilterValues; -import com.amazon.carbonado.filter.OpenFilter; /** * QueryExecutor which wraps another and filters results. @@ -50,12 +48,16 @@ public class FilteredQueryExecutor extends AbstractQueryExec if (executor == null) { throw new IllegalArgumentException(); } - if (filter == null || filter instanceof OpenFilter || filter instanceof ClosedFilter) { + if (filter == null || filter.isOpen() || filter.isClosed()) { throw new IllegalArgumentException(); } mExecutor = executor; // Ensure filter is same as what will be provided by values. - mFilter = filter.initialFilterValues().getFilter(); + FilterValues values = filter.initialFilterValues(); + if (values != null) { + filter = values.getFilter(); + } + mFilter = filter; } public Cursor fetch(FilterValues values) throws FetchException { diff --git a/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java index b1d47e9..12238a6 100644 --- a/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java @@ -42,7 +42,6 @@ import com.amazon.carbonado.cursor.MultiTransformedCursor; import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.filter.FilterValues; -import com.amazon.carbonado.filter.OpenFilter; import com.amazon.carbonado.filter.RelOp; import com.amazon.carbonado.info.ChainedProperty; @@ -501,7 +500,7 @@ public class JoinedQueryExecutor throw new IllegalArgumentException("Outer loop executor filter must be bound"); } - if (targetFilter instanceof OpenFilter) { + if (targetFilter.isOpen()) { targetFilter = null; } diff --git a/src/main/java/com/amazon/carbonado/qe/QueryEngine.java b/src/main/java/com/amazon/carbonado/qe/QueryEngine.java index 56f6145..8218b66 100644 --- a/src/main/java/com/amazon/carbonado/qe/QueryEngine.java +++ b/src/main/java/com/amazon/carbonado/qe/QueryEngine.java @@ -49,13 +49,20 @@ public class QueryEngine extends StandardQueryFactory return mExecutorFactory.executor(filter, ordering); } - protected StandardQuery createQuery(FilterValues values, OrderingList ordering) { - return new Query(values, ordering, null); + protected StandardQuery createQuery(Filter filter, + FilterValues values, + OrderingList ordering) + { + return new Query(filter, values, ordering, null); } private class Query extends StandardQuery { - Query(FilterValues values, OrderingList ordering, QueryExecutor executor) { - super(values, ordering, executor); + Query(Filter filter, + FilterValues values, + OrderingList ordering, + QueryExecutor executor) + { + super(filter, values, ordering, executor); } protected Transaction enterTransaction(IsolationLevel level) { @@ -74,7 +81,7 @@ public class QueryEngine extends StandardQueryFactory OrderingList ordering, QueryExecutor executor) { - return new Query(values, ordering, executor); + return new Query(values.getFilter(), values, ordering, executor); } } } diff --git a/src/main/java/com/amazon/carbonado/qe/QueryFactory.java b/src/main/java/com/amazon/carbonado/qe/QueryFactory.java index b7ead1d..ed1bfb4 100644 --- a/src/main/java/com/amazon/carbonado/qe/QueryFactory.java +++ b/src/main/java/com/amazon/carbonado/qe/QueryFactory.java @@ -22,6 +22,7 @@ import com.amazon.carbonado.FetchException; import com.amazon.carbonado.Query; import com.amazon.carbonado.Storable; +import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.filter.FilterValues; /** @@ -35,8 +36,10 @@ public interface QueryFactory { /** * Returns a query that handles the given query specification. * - * @param values optional values object, defaults to open filter if null + * @param filter optional filter object, defaults to open filter if null + * @param values optional values object, defaults to filter initial values * @param ordering optional order-by properties */ - Query query(FilterValues values, OrderingList ordering) throws FetchException; + Query query(Filter filter, FilterValues values, OrderingList ordering) + throws FetchException; } diff --git a/src/main/java/com/amazon/carbonado/qe/StandardQuery.java b/src/main/java/com/amazon/carbonado/qe/StandardQuery.java index 2d7995e..2914bf8 100644 --- a/src/main/java/com/amazon/carbonado/qe/StandardQuery.java +++ b/src/main/java/com/amazon/carbonado/qe/StandardQuery.java @@ -32,10 +32,8 @@ import com.amazon.carbonado.Storable; import com.amazon.carbonado.Transaction; import com.amazon.carbonado.Query; -import com.amazon.carbonado.filter.ClosedFilter; import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.filter.FilterValues; -import com.amazon.carbonado.filter.OpenFilter; import com.amazon.carbonado.filter.RelOp; import com.amazon.carbonado.info.Direction; @@ -51,6 +49,8 @@ import com.amazon.carbonado.util.Appender; public abstract class StandardQuery extends AbstractQuery implements Appender { + // Filter for this query, which may be null. + private final Filter mFilter; // Values for this query, which may be null. private final FilterValues mValues; // Properties that this query is ordered by. @@ -59,15 +59,29 @@ public abstract class StandardQuery extends AbstractQuery private volatile QueryExecutor mExecutor; /** - * @param values optional values object, defaults to open filter if null + * @param filter optional filter object, defaults to open filter if null + * @param values optional values object, defaults to filter initial values * @param ordering optional order-by properties * @param executor optional executor to use (by default lazily obtains and caches executor) */ - protected StandardQuery(FilterValues values, + protected StandardQuery(Filter filter, + FilterValues values, OrderingList ordering, QueryExecutor executor) { + if (filter != null && filter.isOpen()) { + filter = null; + } + + if (values == null) { + if (filter != null) { + values = filter.initialFilterValues(); + } + } + + mFilter = filter; mValues = values; + if (ordering == null) { ordering = OrderingList.emptyList(); } @@ -80,11 +94,11 @@ public abstract class StandardQuery extends AbstractQuery } public Filter getFilter() { - FilterValues values = mValues; - if (values != null) { - return values.getFilter(); + Filter filter = mFilter; + if (filter == null) { + return Filter.getOpenFilter(getStorableType()); } - return Filter.getOpenFilter(getStorableType()); + return filter; } public FilterValues getFilterValues() { @@ -139,46 +153,57 @@ public abstract class StandardQuery extends AbstractQuery } public Query and(Filter filter) throws FetchException { + Filter newFilter; FilterValues newValues; - if (mValues == null) { + if (mFilter == null) { + newFilter = filter; newValues = filter.initialFilterValues(); } else { if (getBlankParameterCount() > 0) { throw new IllegalStateException("Blank parameters exist in query: " + this); } - newValues = mValues.getFilter().and(filter) - .initialFilterValues().withValues(mValues.getSuppliedValues()); + newFilter = mFilter.and(filter); + newValues = newFilter.initialFilterValues(); + if (mValues != null) { + newValues = newValues.withValues(mValues.getSuppliedValues()); + } } - return createQuery(newValues, mOrdering); + return createQuery(newFilter, newValues, mOrdering); } public Query or(Filter filter) throws FetchException { - if (mValues == null) { + if (mFilter == null) { throw new IllegalStateException("Query is already guaranteed to fetch everything"); } if (getBlankParameterCount() > 0) { throw new IllegalStateException("Blank parameters exist in query: " + this); } - FilterValues newValues = mValues.getFilter().or(filter) - .initialFilterValues().withValues(mValues.getSuppliedValues()); - return createQuery(newValues, mOrdering); + Filter newFilter = mFilter.or(filter); + FilterValues newValues = newFilter.initialFilterValues(); + if (mValues != null) { + newValues = newValues.withValues(mValues.getSuppliedValues()); + } + return createQuery(newFilter, newValues, mOrdering); } public Query not() throws FetchException { - if (mValues == null) { + if (mFilter == null) { return new EmptyQuery(queryFactory(), mOrdering); } - FilterValues newValues = mValues.getFilter().not() - .initialFilterValues().withValues(mValues.getSuppliedValues()); - return createQuery(newValues, mOrdering); + Filter newFilter = mFilter.not(); + FilterValues newValues = newFilter.initialFilterValues(); + if (mValues != null) { + newValues = newValues.withValues(mValues.getSuppliedValues()); + } + return createQuery(newFilter, newValues, mOrdering); } public Query orderBy(String property) throws FetchException { - return createQuery(mValues, OrderingList.get(getStorableType(), property)); + return createQuery(mFilter, mValues, OrderingList.get(getStorableType(), property)); } public Query orderBy(String... properties) throws FetchException { - return createQuery(mValues, OrderingList.get(getStorableType(), properties)); + return createQuery(mFilter, mValues, OrderingList.get(getStorableType(), properties)); } public Cursor fetch() throws FetchException { @@ -323,6 +348,9 @@ public abstract class StandardQuery extends AbstractQuery public int hashCode() { int hash = queryFactory().hashCode(); hash = hash * 31 + executorFactory().hashCode(); + if (mFilter != null) { + hash = hash * 31 + mFilter.hashCode(); + } if (mValues != null) { hash = hash * 31 + mValues.hashCode(); } @@ -339,6 +367,7 @@ public abstract class StandardQuery extends AbstractQuery StandardQuery other = (StandardQuery) obj; return queryFactory().equals(other.queryFactory()) && executorFactory().equals(other.executorFactory()) + && (mFilter == null ? (other.mFilter == null) : (mFilter.equals(other.mFilter))) && (mValues == null ? (other.mValues == null) : (mValues.equals(other.mValues))) && mOrdering.equals(other.mOrdering); } @@ -350,7 +379,7 @@ public abstract class StandardQuery extends AbstractQuery app.append(getStorableType().getName()); app.append(", filter="); Filter filter = getFilter(); - if (filter instanceof OpenFilter || filter instanceof ClosedFilter) { + if (filter.isOpen() || filter.isClosed()) { filter.appendTo(app); } else { app.append('"'); @@ -386,8 +415,7 @@ public abstract class StandardQuery extends AbstractQuery protected QueryExecutor executor() throws RepositoryException { QueryExecutor executor = mExecutor; if (executor == null) { - Filter filter = mValues == null ? null : mValues.getFilter(); - mExecutor = executor = executorFactory().executor(filter, mOrdering); + mExecutor = executor = executorFactory().executor(mFilter, mOrdering); } return executor; } @@ -406,8 +434,7 @@ public abstract class StandardQuery extends AbstractQuery */ protected void resetExecutor() throws RepositoryException { if (mExecutor != null) { - Filter filter = mValues == null ? null : mValues.getFilter(); - mExecutor = executorFactory().executor(filter, mOrdering); + mExecutor = executorFactory().executor(mFilter, mOrdering); } } @@ -443,7 +470,7 @@ public abstract class StandardQuery extends AbstractQuery * new filter values. The Filter in the FilterValues is the same as was * passed in the constructor. * - * @param values optional values object, defaults to open filter if null + * @param values non-null values object * @param ordering order-by properties, never null */ protected abstract StandardQuery newInstance(FilterValues values, @@ -454,9 +481,11 @@ public abstract class StandardQuery extends AbstractQuery return newInstance(values, mOrdering, mExecutor); } - private Query createQuery(FilterValues values, OrderingList ordering) + private Query createQuery(Filter filter, + FilterValues values, + OrderingList ordering) throws FetchException { - return queryFactory().query(values, ordering); + return queryFactory().query(filter, values, ordering); } } diff --git a/src/main/java/com/amazon/carbonado/qe/StandardQueryFactory.java b/src/main/java/com/amazon/carbonado/qe/StandardQueryFactory.java index 06767c5..1ce509b 100644 --- a/src/main/java/com/amazon/carbonado/qe/StandardQueryFactory.java +++ b/src/main/java/com/amazon/carbonado/qe/StandardQueryFactory.java @@ -28,7 +28,6 @@ import com.amazon.carbonado.FetchException; import com.amazon.carbonado.Query; import com.amazon.carbonado.RepositoryException; import com.amazon.carbonado.Storable; -import com.amazon.carbonado.filter.ClosedFilter; import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.filter.FilterValues; @@ -109,6 +108,8 @@ public abstract class StandardQueryFactory implements QueryF * @throws IllegalArgumentException if filter is null */ public Query query(Filter filter, OrderingList ordering) throws FetchException { + filter = filter.bind(); + Map, Query> map; synchronized (mFilterToQuery) { map = mFilterToQuery.get(filter); @@ -126,10 +127,10 @@ public abstract class StandardQueryFactory implements QueryF query = map.get(ordering); if (query == null) { FilterValues values = filter.initialFilterValues(); - if (values == null && filter instanceof ClosedFilter) { + if (values == null && filter.isClosed()) { query = new EmptyQuery(this, ordering); } else { - StandardQuery standardQuery = createQuery(values, ordering); + StandardQuery standardQuery = createQuery(filter, values, ordering); if (!mLazySetExecutor) { try { standardQuery.setExecutor(); @@ -149,16 +150,19 @@ public abstract class StandardQueryFactory implements QueryF /** * Returns a new or cached query for the given query specification. * - * @param values optional values object, defaults to open filter if null + * @param filter optional filter object, defaults to open filter if null + * @param values optional values object, defaults to filter initial values * @param ordering optional order-by properties */ - public Query query(FilterValues values, OrderingList ordering) throws FetchException { - Query query; - if (values == null) { - query = query(Filter.getOpenFilter(mType), ordering); - } else { - query = query(values.getFilter(), ordering).withValues(values.getSuppliedValues()); + public Query query(Filter filter, FilterValues values, OrderingList ordering) + throws FetchException + { + Query query = query(filter != null ? filter : Filter.getOpenFilter(mType), ordering); + + if (values != null) { + query = query.withValues(values.getSuppliedValues()); } + return query; } @@ -196,10 +200,12 @@ public abstract class StandardQueryFactory implements QueryF /** * Implement this method to return query implementations. * - * @param values optional values object, defaults to open filter if null + * @param filter optional filter object, defaults to open filter if null + * @param values optional values object, defaults to filter initial values * @param ordering optional order-by properties */ - protected abstract StandardQuery createQuery(FilterValues values, + protected abstract StandardQuery createQuery(Filter filter, + FilterValues values, OrderingList ordering) throws FetchException; diff --git a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java index fda5f29..1771c71 100644 --- a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java +++ b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java @@ -33,6 +33,7 @@ import com.amazon.carbonado.Storable; import com.amazon.carbonado.SupportException; import com.amazon.carbonado.filter.AndFilter; +import com.amazon.carbonado.filter.ExistsFilter; import com.amazon.carbonado.filter.Filter; import com.amazon.carbonado.filter.OrFilter; import com.amazon.carbonado.filter.PropertyFilter; @@ -625,6 +626,17 @@ public class UnionQueryAnalyzer implements QueryExecutorFact } } + // This method should only be called if root filter has no logical operators. + @Override + public RepositoryException visit(ExistsFilter filter, Object param) { + try { + subAnalyze(filter); + return null; + } catch (RepositoryException e) { + return e; + } + } + private void subAnalyze(Filter subFilter) throws SupportException, RepositoryException { IndexedQueryAnalyzer.Result subResult = mIndexAnalyzer.analyze(subFilter, mOrdering); -- cgit v1.2.3