Skip to content

Commit 3e18166

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 Amend #1 - changes as discussed in Pull Request - clean up test entity types Amend #2 - Eliminate DistinctOperationSupport class. In MongoDB, only distinct on a single field is supported, so the returnType from distinct was very different from the returnType of other query operations (all(), one() etc. (but so is count(), and it doesn't need it's own class)). In Couchbase, distinct on any fields in the entity is allowed - so the returned type could be the domainType or resultType. And as(resultType) still allows any resultType to be specified. This makes it unnecessary to have combinations of interfaces such as DistinctWithProjection and DistinctWithQuery. - Clean up the interfaces in ExecutableFindByQuery. There are two types of interfaces (a) the TerminatingFindByQuery which has the one(), oneValue() first(), firstValue(), all(), count(), exists() and stream(); and (b) the option interfaces (FindByQueryWithConsistency etc), which are essentially with-er interfaces. The changes are: 1) make all the with-er interfaces base interfaces instead of chaining them together. (I don't know why there isn't simply one interface with all the with-er methods). 2) make the ExecutableFindByQuery interface extend the Terminating interface and all the with-er interfaces.
1 parent 3364e0f commit 3e18166

22 files changed

+600
-338
lines changed

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

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.springframework.dao.IncorrectResultSizeDataAccessException;
2323
import org.springframework.data.couchbase.core.query.Query;
24+
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
2425
import org.springframework.lang.Nullable;
2526

2627
import com.couchbase.client.java.query.QueryScanConsistency;
@@ -107,40 +108,90 @@ default Optional<T> first() {
107108
* @author Christoph Strobl
108109
* @since 2.0
109110
*/
110-
interface FindByQueryWithQuery<T> extends TerminatingFindByQuery<T> {
111+
interface FindByQueryWithQuery<T> {
111112

112113
/**
113114
* Set the filter for the query to be used.
114115
*
115116
* @param query must not be {@literal null}.
116117
* @throws IllegalArgumentException if query is {@literal null}.
117118
*/
118-
TerminatingFindByQuery<T> matching(Query query);
119+
ExecutableFindByQuery<T> matching(Query query);
120+
121+
/**
122+
* Set the filter {@link QueryCriteriaDefinition criteria} to be used.
123+
*
124+
* @param criteria must not be {@literal null}.
125+
* @return new instance of {@link ExecutableFindByQuery}.
126+
* @throws IllegalArgumentException if criteria is {@literal null}.
127+
*/
128+
default ExecutableFindByQuery<T> matching(QueryCriteriaDefinition criteria) {
129+
return matching(Query.query(criteria));
130+
}
119131

120132
}
121133

122-
interface FindByQueryConsistentWith<T> extends FindByQueryWithQuery<T> {
134+
interface FindByQueryConsistentWith<T> {
123135

124136
/**
125137
* Allows to override the default scan consistency.
126138
*
127139
* @param scanConsistency the custom scan consistency to use for this query.
128140
*/
129-
FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency);
141+
ExecutableFindByQuery<T> consistentWith(QueryScanConsistency scanConsistency);
130142

131143
}
132144

133-
interface FindByQueryInCollection<T> extends FindByQueryConsistentWith<T> {
145+
interface FindByQueryInCollection<T> {
134146

135147
/**
136148
* Allows to override the default scan consistency.
137149
*
138150
* @param collection the collection to use for this query.
139151
*/
140-
FindByQueryInCollection<T> inCollection(String collection);
152+
ExecutableFindByQuery<T> inCollection(String collection);
153+
154+
}
155+
156+
/**
157+
* Result type override (Optional).
158+
*/
159+
interface FindByQueryWithProjection<T> {
160+
161+
/**
162+
* Define the target type fields should be mapped to. <br />
163+
* Skip this step if you are anyway only interested in the original domain type.
164+
*
165+
* @param resultType must not be {@literal null}.
166+
* @param <R> result type.
167+
* @return new instance of {@link FindByQueryWithProjection}.
168+
* @throws IllegalArgumentException if resultType is {@literal null}.
169+
*/
170+
<R> ExecutableFindByQuery<R> as(Class<R> resultType);
141171

142172
}
143173

144-
interface ExecutableFindByQuery<T> extends FindByQueryInCollection<T> {}
174+
/**
175+
* Distinct Find support.
176+
*/
177+
interface FindByQueryDistinct<T> {
178+
179+
/**
180+
* Finds the distinct values for a specified {@literal field} across a single collection
181+
*
182+
* @param distinctFields name of the field. Must not be {@literal null}.
183+
* @return new instance of {@link ExecutableFindByQuery}.
184+
* @throws IllegalArgumentException if field is {@literal null}.
185+
*/
186+
ExecutableFindByQuery<T> distinct(String[] distinctFields);
187+
188+
}
189+
190+
/**
191+
* {@link ExecutableFindByQuery} provides methods for constructing lookup operations in a fluent way.
192+
*/
193+
194+
interface ExecutableFindByQuery<T> extends TerminatingFindByQuery<T>, FindByQueryWithQuery<T>,
195+
FindByQueryConsistentWith<T>, FindByQueryInCollection<T>, FindByQueryWithProjection<T>, FindByQueryDistinct<T> {}
145196

146197
}

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

Lines changed: 37 additions & 14 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,28 +42,33 @@ 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, 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;
58+
private final String[] distinctFields;
5659

57-
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<T> domainType, final Query query,
58-
final QueryScanConsistency scanConsistency, final String collection) {
60+
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<?> domainType, final Class<T> returnType,
61+
final Query query, final QueryScanConsistency scanConsistency, final String collection,
62+
final String[] distinctFields) {
5963
this.template = template;
6064
this.domainType = domainType;
65+
this.returnType = returnType;
6166
this.query = query;
62-
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, query, scanConsistency,
63-
collection);
67+
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, returnType, query,
68+
scanConsistency, collection, distinctFields);
6469
this.scanConsistency = scanConsistency;
6570
this.collection = collection;
71+
this.distinctFields = distinctFields;
6672
}
6773

6874
@Override
@@ -81,24 +87,42 @@ public List<T> all() {
8187
}
8288

8389
@Override
84-
public TerminatingFindByQuery<T> matching(final Query query) {
90+
public ExecutableFindByQuery<T> matching(final Query query) {
8591
QueryScanConsistency scanCons;
8692
if (query.getScanConsistency() != null) {
8793
scanCons = query.getScanConsistency();
8894
} else {
8995
scanCons = scanConsistency;
9096
}
91-
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanCons, collection);
97+
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanCons, collection,
98+
distinctFields);
9299
}
93100

94101
@Override
95-
public FindByQueryConsistentWith<T> consistentWith(final QueryScanConsistency scanConsistency) {
96-
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency, collection);
102+
public ExecutableFindByQuery<T> consistentWith(final QueryScanConsistency scanConsistency) {
103+
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
104+
distinctFields);
97105
}
98106

99107
@Override
100-
public FindByQueryInCollection<T> inCollection(final String collection) {
101-
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency, collection);
108+
public ExecutableFindByQuery<T> inCollection(final String collection) {
109+
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
110+
distinctFields);
111+
}
112+
113+
@Override
114+
public <R> ExecutableFindByQuery<R> as(Class<R> returnType) {
115+
Assert.notNull(returnType, "returnType must not be null!");
116+
117+
return new ExecutableFindByQuerySupport<R>(template, domainType, returnType, query, scanConsistency, collection,
118+
distinctFields);
119+
}
120+
121+
@Override
122+
public ExecutableFindByQuery<T> distinct(String[] distinctFields) {
123+
Assert.notNull(distinctFields, "distinctFields must not be null!");
124+
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
125+
distinctFields);
102126
}
103127

104128
@Override
@@ -115,7 +139,6 @@ public long count() {
115139
public boolean exists() {
116140
return count() > 0;
117141
}
118-
119142
}
120143

121144
}

0 commit comments

Comments
 (0)