Skip to content

Commit 042f002

Browse files
committed
Added Query.distinct(). However, useless without project().
1 parent a407016 commit 042f002

4 files changed

Lines changed: 81 additions & 49 deletions

File tree

src/main/java/com/googlecode/objectify/cmd/SimpleQuery.java

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,101 +6,106 @@
66

77
/**
88
* A restricted set of query operations that apply to both kindless queries and typed queries.
9-
*
9+
*
1010
* @author Jeff Schnitzer <jeff@infohazard.org>
1111
*/
1212
public interface SimpleQuery<T> extends QueryExecute<T>
1313
{
1414
/**
1515
* <p>Create a filter on the key of an entity. Examples:</p>
16-
*
16+
*
1717
* <ul>
1818
* <li>{@code filterKey(">=", key)} (standard inequalities)</li>
1919
* <li>{@code filterKey("=", key)} (wouldn't you rather do a load-by-key?)</li>
2020
* <li>{@code filterKey("", key)} (if no operator, = is assumed)</li>
2121
* <li>{@code filterKey("!=", key)}</li>
2222
* <li>{@code filterKey("in", keyList)} (wouldn't you rather do a batch load-by-key?)</li>
2323
* </ul>
24-
*
24+
*
2525
* <p>The key parameter can be anything key-ish; a Key<?>, a native datastore key, a Ref, a pojo entity, etc.</p>
26-
*
27-
* <p>See the Google documentation for
26+
*
27+
* <p>See the Google documentation for
2828
* <a href="http://code.google.com/appengine/docs/java/datastore/queries.html#Introduction_to_Indexes">indexes</a>
2929
* for an explanation of what you can and cannot filter for.</p>
3030
*/
3131
public SimpleQuery<T> filterKey(String condition, Object value);
32-
32+
3333
/**
3434
* An alias for {@code filterKey("=", value)}
3535
*/
3636
public SimpleQuery<T> filterKey(Object value);
37-
37+
3838
/**
3939
* Restricts result set only to objects which have the given ancestor
4040
* somewhere in the chain. Doesn't need to be the immediate parent.
41-
*
41+
*
4242
* @param keyOrEntity can be a Key, a Key<T>, or an Objectify entity object.
4343
*/
4444
public SimpleQuery<T> ancestor(Object keyOrEntity);
45-
45+
4646
/**
4747
* Limit the fetched result set to a certain number of values.
48-
*
48+
*
4949
* @param value must be >= 0. A value of 0 indicates no limit.
5050
*/
5151
public SimpleQuery<T> limit(int value);
52-
52+
5353
/**
5454
* Starts the query results at a particular zero-based offset. This can be extraordinarily
5555
* expensive because each skipped entity is billed as a "minor datastore operation". If you
5656
* can, you probably want to use cursors instead.
57-
*
57+
*
5858
* @param value must be >= 0
5959
*/
6060
public SimpleQuery<T> offset(int value);
61-
61+
6262
/**
6363
* Starts query results at the specified Cursor. You can obtain a Cursor from
6464
* a QueryResultIterator by calling the getCursor() method.
65-
*
65+
*
6666
* Note that limit() and offset() are NOT encoded within a cursor; they operate
6767
* on the results of the query after a cursor is established.
6868
*/
6969
public SimpleQuery<T> startAt(Cursor value);
70-
70+
7171
/**
7272
* Ends query results at the specified Cursor. You can obtain a Cursor from
7373
* a QueryResultIterator by calling the getCursor() method.
74-
*
74+
*
7575
* Note that limit() and offset() are NOT encoded within a cursor; they operate
7676
* on the results of the query after a cursor is established.
7777
*/
7878
public SimpleQuery<T> endAt(Cursor value);
79-
79+
8080
/**
8181
* Sets the internal chunking and prefetching strategy within the low-level API. Affects
8282
* performance only; the result set will be the same.
83-
*
83+
*
8484
* @param value must be >= 0
8585
*/
8686
public SimpleQuery<T> chunk(int value);
87-
87+
8888
/**
8989
* <p>Sets the internal chunking and prefetching strategy within the low-level API to attempt to get all
9090
* results at once. Affects performance only; the result set will be the same.</p>
91-
*
91+
*
9292
* <p>Same as chunk(Integer.MAX_VALUE).</p>
9393
*/
9494
public SimpleQuery<T> chunkAll();
95-
95+
96+
/**
97+
* Determines whether this is a SELECT DISTINCT query.
98+
*/
99+
public SimpleQuery<T> distinct(boolean value);
100+
96101
/**
97102
* <p>This method forces Objectify to (or not to) hybridize the query into a keys-only fetch plus batch get.</p>
98-
*
103+
*
99104
* <p>If Objectify knows you are fetching an entity type that can be cached, it automatically converts
100105
* queries into a "hybrid" of keys-only query followed by a batch fetch of the keys. This is cheaper (keys-only
101106
* results are 1/7th the price of a full fetch) and, if the cache hits, significantly faster. However,
102107
* there are some circumstances in which you may wish to force behavior one way or another:</p>
103-
*
108+
*
104109
* <ul>
105110
* <li>Issuing a kindless query (which Objectify will not auto-hybridize) when you know a significant portion
106111
* of the result set is cacheable.</li>
@@ -110,50 +115,50 @@ public interface SimpleQuery<T> extends QueryExecute<T>
110115
* <li>Hybrid queries have a slightly different consistency model than normal queries. You may wish to
111116
* force hybridization off to ensure a weakly consistent query (see below).</li>
112117
* </ul>
113-
*
118+
*
114119
* <p>For the most part, hybridization only affects the performance and cost of queries. However, it is possible
115120
* for hybridization to alter the content of the result set. In the HRD, queries
116121
* always have EVENTUAL consistency but get operations may have either STRONG or EVENTUAL consistency depending
117122
* on the read policy (see {@code Objectify.consistency()}. The keys-only query results will always be EVENTUAL and may
118123
* return data that has been deleted, but Objectify will exclude these results when the more-current batch get fails to return
119124
* data. So the count of the returned data may actually be less than the limit(), even if there are plenty of
120125
* actual results. {@code hybrid(false)} will prevent this behavior.</p>
121-
*
126+
*
122127
* <p>Note that in hybrid queries, the chunk size defines the batch size.</p>
123128
*/
124129
public SimpleQuery<T> hybrid(boolean force);
125-
130+
126131
/**
127132
* Switches to a keys-only query. Keys-only responses are billed as "minor datastore operations"
128133
* which are significantly cheaper (~7X) than fetching whole entities.
129134
*/
130135
public QueryKeys<T> keys();
131-
136+
132137
/**
133138
* <p>Count the total number of values in the result. <em>limit</em> and <em>offset</em> are obeyed.
134139
* This is somewhat faster than fetching, but the time still grows with the number of results.
135140
* The datastore actually walks through the result set and counts for you.</p>
136-
*
141+
*
137142
* <p>Immediately executes the query; there is no async version of this method.</p>
138-
*
143+
*
139144
* <p>WARNING: Each counted entity is billed as a "datastore minor operation". Counting large numbers
140145
* of entities can quickly create massive bills.</p>
141146
*/
142147
public int count();
143148

144149
/**
145150
* Gets the first entity in the result set. Obeys the offset value.
146-
*
151+
*
147152
* @return an asynchronous Ref containing the first result. The Ref will hold null if the result set is empty.
148153
*/
149154
public Ref<T> first();
150-
155+
151156
/**
152157
* <p>Generates a string that consistently and uniquely specifies this query. There
153158
* is no way to convert this string back into a query and there is no guarantee that
154159
* the string will be consistent across versions of Objectify.</p>
155-
*
156-
* <p>In particular, this value is useful as a key for a simple memcache query cache.</p>
160+
*
161+
* <p>In particular, this value is useful as a key for a simple memcache query cache.</p>
157162
*/
158163
public String toString();
159164
}

src/main/java/com/googlecode/objectify/impl/cmd/QueryImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,11 @@ void setKeysOnly() {
271271
this.actual.setKeysOnly();
272272
}
273273

274+
/** Modifies the instance */
275+
void setDistinct(boolean value) {
276+
this.actual.setDistinct(value);
277+
}
278+
274279
/* (non-Javadoc)
275280
* @see java.lang.Object#toString()
276281
*/
@@ -348,6 +353,9 @@ public int compare(SortPredicate o1, SortPredicate o2)
348353
if (this.endAt != null)
349354
bld.append(",endAt=").append(this.endAt.toWebSafeString());
350355

356+
if (this.actual.getDistinct())
357+
bld.append(",distinct=true");
358+
351359
bld.append('}');
352360

353361
return bld.toString();

src/main/java/com/googlecode/objectify/impl/cmd/SimpleQueryImpl.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,21 @@
99
/**
1010
* Base for command classes that include methods for defining a query (filter, order, limit, etc).
1111
* Does not include the methods for executing a query.
12-
*
12+
*
1313
* @author Jeff Schnitzer <jeff@infohazard.org>
1414
*/
1515
abstract class SimpleQueryImpl<T> implements SimpleQuery<T>
1616
{
1717
/** */
1818
protected LoaderImpl loader;
19-
19+
2020
/**
2121
* There is a special case - if loader is null, use 'this' as the LoaderImpl
2222
*/
2323
SimpleQueryImpl(LoaderImpl loader) {
2424
this.loader = loader == null ? (LoaderImpl)this : loader;
2525
}
26-
26+
2727
/**
2828
* Create an initial query object; for a real FindType this will have a class, otherwise it will be generic.
2929
* For the real QueryImpl itself this is a clone() operation.
@@ -40,7 +40,7 @@ public QueryImpl<T> filterKey(String condition, Object value)
4040
q.addFilter(Entity.KEY_RESERVED_PROPERTY + " " + condition, value);
4141
return q;
4242
}
43-
43+
4444
/* (non-Javadoc)
4545
* @see com.googlecode.objectify.cmd.SimpleQuery#filterKey(java.lang.Object)
4646
*/
@@ -49,7 +49,7 @@ public QueryImpl<T> filterKey(Object value)
4949
{
5050
return filterKey("=", value);
5151
}
52-
52+
5353
/* (non-Javadoc)
5454
* @see com.googlecode.objectify.cmd.Query#ancestor(java.lang.Object)
5555
*/
@@ -115,7 +115,18 @@ public QueryImpl<T> chunk(int value)
115115
q.setChunk(value);
116116
return q;
117117
}
118-
118+
119+
/* (non-Javadoc)
120+
* @see com.googlecode.objectify.cmd.SimpleQuery#distinct(boolean)
121+
*/
122+
@Override
123+
public QueryImpl<T> distinct(boolean value)
124+
{
125+
QueryImpl<T> q = createQuery();
126+
q.setDistinct(value);
127+
return q;
128+
}
129+
119130
/* (non-Javadoc)
120131
* @see com.googlecode.objectify.cmd.SimpleQuery#hybrid(boolean)
121132
*/

src/main/java/com/googlecode/objectify/util/cmd/SimpleQueryWrapper.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,53 +11,53 @@
1111

1212
/**
1313
* Simple wrapper/decorator for a SimpleQuery. Use it like this:
14-
* {@code class MySimpleQuery<T> extends SimpleQueryWrapper<MyQuery<T>, T>}
14+
* {@code class MySimpleQuery<T> extends SimpleQueryWrapper<MyQuery<T>, T>}
1515
*
1616
* @author Jeff Schnitzer <jeff@infohazard.org>
1717
*/
1818
public class SimpleQueryWrapper<H extends SimpleQueryWrapper<H, T>, T> implements SimpleQuery<T>, Cloneable
1919
{
2020
/** */
2121
SimpleQuery<T> base;
22-
22+
2323
/** */
24-
public SimpleQueryWrapper(SimpleQuery<T> base)
24+
public SimpleQueryWrapper(SimpleQuery<T> base)
2525
{
2626
this.base = base;
2727
}
28-
28+
2929
@Override
3030
public H filterKey(String condition, Object value)
3131
{
3232
H next = this.clone();
3333
next.base = base.filterKey(condition, value);
3434
return next;
3535
}
36-
36+
3737
@Override
3838
public SimpleQuery<T> filterKey(Object value)
3939
{
4040
H next = this.clone();
4141
next.base = base.filterKey(value);
4242
return next;
4343
}
44-
44+
4545
@Override
4646
public H ancestor(Object keyOrEntity)
4747
{
4848
H next = this.clone();
4949
next.base = base.ancestor(keyOrEntity);
5050
return next;
5151
}
52-
52+
5353
@Override
5454
public H limit(int value)
5555
{
5656
H next = this.clone();
5757
next.base = base.limit(value);
5858
return next;
5959
}
60-
60+
6161
@Override
6262
public H offset(int value)
6363
{
@@ -127,7 +127,15 @@ public H chunkAll()
127127
next.base = base.chunkAll();
128128
return next;
129129
}
130-
130+
131+
@Override
132+
public H distinct(boolean value)
133+
{
134+
H next = this.clone();
135+
next.base = base.distinct(value);
136+
return next;
137+
}
138+
131139
@Override
132140
public H hybrid(boolean force)
133141
{
@@ -141,7 +149,7 @@ public QueryResultIterator<T> iterator()
141149
{
142150
return base.iterator();
143151
}
144-
152+
145153
@Override
146154
public QueryKeys<T> keys()
147155
{

0 commit comments

Comments
 (0)