Skip to content

Commit 66015b2

Browse files
committed
DATACOUCH-588 - Part 2 of framework changes. Add support for projection and distinct.
Support for projection is only for properties of the top-level entity. For instance, in UserSubmission, only the properties below can be specified in the projection. Projection support does not provide means of specifying something like address.street - you can only project (or not project) the whole address property. However, the address type in your resultType could have a subset of the properties in Address. If the corresponding submissions in the resultType contained only the userId property public class UserSubmission extends ComparableEntity { private String id; private String username; private List<String> roles; private Address address; private List<Submission> submissions; Support for Distinct - I have appropriated the MongoDB model for Distinct. It defines a separate DistinctOperationSupport class (within ExecutableFindByQuerySupport) which supports the distinct( distinctFields ) api and execution. The DistinctOperationSupport class has only a distinctFields member, and a 'delegate' member, which is an ExecutableFindByQuerySupport object. TBH, I don't see the advantage over simply adding a distinctFields member to ExecutableFindByQuerySupport
1 parent 3364e0f commit 66015b2

13 files changed

+801
-99
lines changed

src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperation.java

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
import java.util.Optional;
2020
import java.util.stream.Stream;
2121

22+
import org.springframework.dao.DataAccessException;
2223
import org.springframework.dao.IncorrectResultSizeDataAccessException;
2324
import org.springframework.data.couchbase.core.query.Query;
25+
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
2426
import org.springframework.lang.Nullable;
2527

2628
import com.couchbase.client.java.query.QueryScanConsistency;
@@ -141,6 +143,125 @@ interface FindByQueryInCollection<T> extends FindByQueryConsistentWith<T> {
141143

142144
}
143145

144-
interface ExecutableFindByQuery<T> extends FindByQueryInCollection<T> {}
146+
/**
147+
* Result type override (Optional).
148+
*
149+
*/
150+
interface FindWithProjection<T> extends FindByQueryInCollection<T>, FindDistinct {
151+
152+
/**
153+
* Define the target type fields should be mapped to. <br />
154+
* Skip this step if you are anyway only interested in the original domain type.
155+
*
156+
* @param resultType must not be {@literal null}.
157+
* @param <R> result type.
158+
* @return new instance of {@link FindWithProjection}.
159+
* @throws IllegalArgumentException if resultType is {@literal null}.
160+
*/
161+
<R> FindWithProjection<R> as(Class<R> resultType);
162+
163+
}
164+
165+
/**
166+
* Distinct Find support.
167+
*
168+
*/
169+
interface FindDistinct {
170+
171+
/**
172+
* Finds the distinct values for a specified {@literal field} across a single collection
173+
*
174+
* @param distinctFields name of the field. Must not be {@literal null}.
175+
* @return new instance of {@link TerminatingDistinct}.
176+
* @throws IllegalArgumentException if field is {@literal null}.
177+
*/
178+
TerminatingDistinct<Object> distinct(String[] distinctFields);
179+
180+
}
181+
182+
/**
183+
* Result type override. Optional.
184+
*
185+
* @author Michael Reiche
186+
*/
187+
interface DistinctWithProjection {
188+
189+
/**
190+
* Define the target type the result should be mapped to. <br />
191+
* Skip this step if you are anyway fine with the default conversion.
192+
*
193+
* @param resultType must not be {@literal null}.
194+
* @param <R> result type.
195+
* @return new instance of {@link TerminatingDistinct}.
196+
* @throws IllegalArgumentException if resultType is {@literal null}.
197+
*/
198+
<R> TerminatingDistinct<R> as(Class<R> resultType);
199+
}
200+
201+
/**
202+
* Result restrictions. Optional.
203+
*
204+
*/
205+
interface DistinctWithQuery<T> extends DistinctWithProjection {
206+
207+
/**
208+
* Set the filter query to be used.
209+
*
210+
* @param query must not be {@literal null}.
211+
* @return new instance of {@link TerminatingDistinct}.
212+
* @throws IllegalArgumentException if query is {@literal null}.
213+
*/
214+
TerminatingDistinct<T> matching(Query query);
215+
216+
/**
217+
* Set the filter {@link QueryCriteriaDefinition criteria} to be used.
218+
*
219+
* @param criteria must not be {@literal null}.
220+
* @return new instance of {@link TerminatingDistinct}.
221+
* @throws IllegalArgumentException if criteria is {@literal null}.
222+
*/
223+
default TerminatingDistinct<T> matching(QueryCriteriaDefinition criteria) {
224+
return matching(Query.query(criteria));
225+
}
226+
227+
/**
228+
* Set the {@link QueryScanConsistency criteria} to be used.
229+
*
230+
* @param scanConsistency must not be {@literal null}.
231+
* @return new instance of {@link TerminatingDistinct}.
232+
* @throws IllegalArgumentException if criteria is {@literal null}.
233+
*/
234+
TerminatingDistinct<Object> consistentWith(QueryScanConsistency scanConsistency);
235+
}
236+
237+
/**
238+
* Terminating distinct find operations.
239+
*
240+
*/
241+
interface TerminatingDistinct<T> extends DistinctWithQuery<T> {
242+
243+
/**
244+
* Get all matching distinct field values.
245+
*
246+
* @return empty {@link List} if not match found. Never {@literal null}.
247+
* @throws DataAccessException if eg. result cannot be converted correctly which may happen if the document contains
248+
* {@link String} whereas the result type is specified as {@link Long}.
249+
*/
250+
List<T> all();
251+
252+
/**
253+
* Get the number of matching elements.
254+
*
255+
* @return total number of matching elements.
256+
*/
257+
long count();
258+
}
259+
260+
/**
261+
* {@link ExecutableFindByQuery} provides methods for constructing lookup operations in a fluent way.
262+
*
263+
*/
264+
265+
interface ExecutableFindByQuery<T> extends FindByQueryConsistentWith<T>, FindByQueryInCollection<T>, FindWithProjection<T>, FindDistinct {}
145266

146267
}

src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperationSupport.java

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport;
2222
import org.springframework.data.couchbase.core.query.Query;
23+
import org.springframework.util.Assert;
2324

2425
import com.couchbase.client.java.query.QueryScanConsistency;
2526

@@ -41,26 +42,28 @@ public ExecutableFindByQueryOperationSupport(final CouchbaseTemplate template) {
4142

4243
@Override
4344
public <T> ExecutableFindByQuery<T> findByQuery(final Class<T> domainType) {
44-
return new ExecutableFindByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED,
45-
"_default._default");
45+
return new ExecutableFindByQuerySupport<T>(template, domainType, domainType, ALL_QUERY,
46+
QueryScanConsistency.NOT_BOUNDED, null);
4647
}
4748

4849
static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T> {
4950

5051
private final CouchbaseTemplate template;
51-
private final Class<T> domainType;
52+
private final Class<?> domainType;
53+
private final Class<T> returnType;
5254
private final Query query;
5355
private final ReactiveFindByQuerySupport<T> reactiveSupport;
5456
private final QueryScanConsistency scanConsistency;
5557
private final String collection;
5658

57-
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<T> domainType, final Query query,
58-
final QueryScanConsistency scanConsistency, final String collection) {
59+
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<?> domainType, final Class<T> returnType,
60+
final Query query, final QueryScanConsistency scanConsistency, final String collection) {
5961
this.template = template;
6062
this.domainType = domainType;
63+
this.returnType = returnType;
6164
this.query = query;
62-
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, query, scanConsistency,
63-
collection);
65+
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, returnType, query,
66+
scanConsistency, collection);
6467
this.scanConsistency = scanConsistency;
6568
this.collection = collection;
6669
}
@@ -88,17 +91,31 @@ public TerminatingFindByQuery<T> matching(final Query query) {
8891
} else {
8992
scanCons = scanConsistency;
9093
}
91-
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanCons, collection);
94+
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanCons, collection);
9295
}
9396

9497
@Override
9598
public FindByQueryConsistentWith<T> consistentWith(final QueryScanConsistency scanConsistency) {
96-
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency, collection);
99+
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection);
97100
}
98101

99102
@Override
100103
public FindByQueryInCollection<T> inCollection(final String collection) {
101-
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency, collection);
104+
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection);
105+
}
106+
107+
@Override
108+
public <R> FindWithProjection<R> as(Class<R> returnType) {
109+
Assert.notNull(returnType, "returnType must not be null!");
110+
111+
return new ExecutableFindByQuerySupport<R>(template, domainType, returnType, query, scanConsistency, collection);
112+
}
113+
114+
@Override
115+
public TerminatingDistinct<Object> distinct(String[] distinctFields) {
116+
117+
Assert.notNull(distinctFields, "distinctFields must not be null!");
118+
return new DistinctOperationSupport(this, distinctFields);
102119
}
103120

104121
@Override
@@ -115,7 +132,83 @@ public long count() {
115132
public boolean exists() {
116133
return count() > 0;
117134
}
135+
}
136+
137+
/**
138+
* @author Michael Reiche
139+
*/
140+
static class DistinctOperationSupport<T> implements TerminatingDistinct<T> {
141+
142+
private final String[] distinctFields;
143+
private final ExecutableFindByQuerySupport<T> delegate;
144+
145+
public DistinctOperationSupport(ExecutableFindByQuerySupport<T> delegate, String[] distinctFields) {
146+
147+
this.delegate = delegate;
148+
this.distinctFields = distinctFields;
149+
}
150+
151+
/*
152+
* (non-Javadoc)
153+
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.DistinctWithProjection#as(java.lang.Class)
154+
*/
155+
@Override
156+
@SuppressWarnings("unchecked")
157+
public <R> TerminatingDistinct<R> as(Class<R> resultType) {
158+
159+
Assert.notNull(resultType, "resultType must not be null!");
160+
161+
return new DistinctOperationSupport<>((ExecutableFindByQuerySupport) delegate.as(resultType), distinctFields);
162+
}
163+
164+
/*
165+
* (non-Javadoc)
166+
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.DistinctWithQuery#matching(org.springframework.data.mongodb.core.query.Query)
167+
*/
168+
@Override
169+
public TerminatingDistinct<T> matching(Query query) {
170+
171+
Assert.notNull(query, "Query must not be null!");
172+
173+
return new DistinctOperationSupport<>((ExecutableFindByQuerySupport<T>) delegate.matching(query), distinctFields);
174+
}
175+
176+
/*
177+
* (non-Javadoc)
178+
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.DistinctWithProjection#as(java.lang.Class)
179+
*/
180+
@Override
181+
@SuppressWarnings("unchecked")
182+
public TerminatingDistinct<Object> consistentWith(QueryScanConsistency scanConsistency) {
183+
184+
Assert.notNull(scanConsistency, "scanConsistency must not be null!");
185+
186+
return new DistinctOperationSupport<>((ExecutableFindByQuerySupport) delegate.consistentWith(scanConsistency),
187+
distinctFields);
188+
}
118189

190+
/*
191+
* (non-Javadoc)
192+
* @see org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingDistinct#all()
193+
*/
194+
@Override
195+
public List<T> all() {
196+
ReactiveFindByQueryOperationSupport.TerminatingDistinct<T> query = delegate.reactiveSupport
197+
.distinct(distinctFields).as(delegate.returnType);
198+
return query.all().collectList().block();
199+
}
200+
201+
/**
202+
* Get the number of matching elements.
203+
*
204+
* @return total number of matching elements.
205+
*/
206+
@Override
207+
public long count() {
208+
ReactiveFindByQueryOperationSupport.TerminatingDistinct<T> query = delegate.reactiveSupport
209+
.distinct(distinctFields).as(delegate.returnType);
210+
return query.count().block();
211+
}
119212
}
120213

121214
}

0 commit comments

Comments
 (0)