1 // Copyright 2009 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.datastore
;
5 import static com
.google
.appengine
.api
.datastore
.Query
.FilterOperator
.GREATER_THAN
;
6 import static com
.google
.appengine
.api
.datastore
.Query
.FilterOperator
.LESS_THAN
;
7 import static com
.google
.appengine
.api
.datastore
.Query
.FilterOperator
.NOT_EQUAL
;
9 import com
.google
.appengine
.api
.datastore
.Query
.FilterPredicate
;
10 import com
.google
.appengine
.api
.datastore
.Query
.SortDirection
;
11 import com
.google
.appengine
.api
.datastore
.Query
.SortPredicate
;
13 import java
.util
.ArrayList
;
14 import java
.util
.Collections
;
15 import java
.util
.Iterator
;
16 import java
.util
.List
;
19 * This class splits a query with a not-equal filter as follows:
20 * <p>Create n + 1 queries components that restrict the queries
21 * range to values outside the given value. Consider the example:
23 * select from Person where age != 33
25 * this class would turn this into:
27 * select from Person where age < 33
28 * select from Person where age > 33
31 * <p>This class can also work for multiple inequality filters. For example:
33 * select from Person where age != 33 and age != 40 order by age desc
35 * we would turn this into:
37 * select from Person where age > 40
38 * select from Person where age > 33 and age < 40
39 * select from Person where age < 33
42 * <p>Just like other inequality filters, != can only filters can only
43 * be applied to a single property and the first sort order must
44 * be on the same property.
47 class NotEqualQuerySplitter
extends BaseQuerySplitter
{
49 public List
<QuerySplitComponent
> split(List
<FilterPredicate
> remainingFilters
,
50 List
<SortPredicate
> sorts
) {
51 String propertyName
= null;
52 List
<ComparableValue
> values
= null;
54 Iterator
<FilterPredicate
> itr
= remainingFilters
.iterator();
55 while (itr
.hasNext()) {
56 FilterPredicate filter
= itr
.next();
57 if (filter
.getOperator() == NOT_EQUAL
) {
58 if (propertyName
== null) {
59 propertyName
= filter
.getPropertyName();
60 values
= new ArrayList
<ComparableValue
>();
61 } else if (!propertyName
.equals(filter
.getPropertyName())) {
62 throw new IllegalArgumentException(
63 "Queries with NOT_EQUAL filters on different properties are not supported.");
66 values
.add(new ComparableValue(filter
.getValue()));
72 ArrayList
<QuerySplitComponent
> result
= new ArrayList
<QuerySplitComponent
>(values
.size() + 1);
73 result
.add(makeComponent(propertyName
, values
, sorts
));
76 return Collections
.emptyList();
80 * Constructs a {@link QuerySplitComponent} from the given set of not equal
83 * @param propertyName the property which != values
84 * @param values the list of values to consider
85 * @param sorts the sort predicates imposed on this query
86 * @return the resulting {@link QuerySplitComponent}
88 private QuerySplitComponent
makeComponent(String propertyName
, List
<ComparableValue
> values
,
89 List
<SortPredicate
> sorts
) {
90 QuerySplitComponent result
= new QuerySplitComponent(propertyName
, sorts
);
91 if (!sorts
.isEmpty() && (result
.getSortIndex() != 0)) {
92 throw new IllegalArgumentException(
93 "The first sort order must be on the same property as the NOT_EQUAL filter");
96 Collections
.sort(values
, getValueComparator(result
.getDirection()));
97 Object first
= values
.get(0).getValue();
98 Object last
= values
.get(values
.size() - 1).getValue();
99 if (result
.getDirection() == SortDirection
.DESCENDING
) {
100 result
.addFilters(new FilterPredicate(propertyName
, GREATER_THAN
, first
));
101 for (int i
= 1; i
< values
.size(); ++i
) {
102 Object prev
= values
.get(i
- 1).getValue();
103 Object next
= values
.get(i
).getValue();
104 result
.addFilters(new FilterPredicate(propertyName
, LESS_THAN
, prev
),
105 new FilterPredicate(propertyName
, GREATER_THAN
, next
));
108 result
.addFilters(new FilterPredicate(propertyName
, LESS_THAN
, last
));
112 result
.addFilters(new FilterPredicate(propertyName
, LESS_THAN
, first
));
114 for (int i
= 1; i
< values
.size(); ++i
) {
115 Object prev
= values
.get(i
- 1).getValue();
116 Object next
= values
.get(i
).getValue();
117 result
.addFilters(new FilterPredicate(propertyName
, GREATER_THAN
, prev
),
118 new FilterPredicate(propertyName
, LESS_THAN
, next
));
120 result
.addFilters(new FilterPredicate(propertyName
, GREATER_THAN
, last
));