Skip to content

Commit 2c933eb

Browse files
committed
Add project(fields) to findByQuery similar to same method on findById.
Closes #1208, #1213.
1 parent edaf11e commit 2c933eb

17 files changed

+370
-112
lines changed

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ interface FindByQueryConsistentWith<T> extends FindByQueryInScope<T> {
221221
}
222222

223223
/**
224-
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
224+
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
225225
*
226226
* @param <T> the entity type to use for the results.
227227
*/
@@ -254,12 +254,30 @@ interface FindByQueryWithProjection<T> extends FindByQueryWithConsistency<T> {
254254
<R> FindByQueryWithConsistency<R> as(Class<R> returnType);
255255
}
256256

257+
/**
258+
* Fluent method to specify fields to project.
259+
*
260+
* @param <T> the entity type to use for the results.
261+
*/
262+
interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {
263+
264+
/**
265+
* Define the target type fields should be mapped to. <br />
266+
* Skip this step if you are anyway only interested in the original domain type.
267+
*
268+
* @param fields to project
269+
* @return new instance of {@link ReactiveFindByQueryOperation.FindByQueryWithConsistency}.
270+
* @throws IllegalArgumentException if returnType is {@literal null}.
271+
*/
272+
FindByQueryWithProjection<T> project(String[] fields);
273+
}
274+
257275
/**
258276
* Fluent method to specify DISTINCT fields
259277
*
260278
* @param <T> the entity type to use for the results.
261279
*/
262-
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjection<T>, WithDistinct<T> {
280+
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithDistinct<T> {
263281

264282
/**
265283
* Finds the distinct values for a specified {@literal field} across a single collection

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

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.stream.Stream;
2020

2121
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport;
22-
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
2322
import org.springframework.data.couchbase.core.query.Query;
2423
import org.springframework.util.Assert;
2524

@@ -45,7 +44,7 @@ public ExecutableFindByQueryOperationSupport(final CouchbaseTemplate template) {
4544
@Override
4645
public <T> ExecutableFindByQuery<T> findByQuery(final Class<T> domainType) {
4746
return new ExecutableFindByQuerySupport<T>(template, domainType, domainType, ALL_QUERY, null, null, null, null,
48-
null);
47+
null, null);
4948
}
5049

5150
static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T> {
@@ -60,22 +59,24 @@ static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T>
6059
private final String collection;
6160
private final QueryOptions options;
6261
private final String[] distinctFields;
62+
private final String[] fields;
6363

6464
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<?> domainType, final Class<T> returnType,
6565
final Query query, final QueryScanConsistency scanConsistency, final String scope, final String collection,
66-
final QueryOptions options, final String[] distinctFields) {
66+
final QueryOptions options, final String[] distinctFields, final String[] fields) {
6767
this.template = template;
6868
this.domainType = domainType;
6969
this.returnType = returnType;
7070
this.query = query;
7171
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, returnType, query,
72-
scanConsistency, scope, collection, options, distinctFields,
72+
scanConsistency, scope, collection, options, distinctFields, fields,
7373
new NonReactiveSupportWrapper(template.support()));
7474
this.scanConsistency = scanConsistency;
7575
this.scope = scope;
7676
this.collection = collection;
7777
this.options = options;
7878
this.distinctFields = distinctFields;
79+
this.fields = fields;
7980
}
8081

8182
@Override
@@ -102,38 +103,47 @@ public TerminatingFindByQuery<T> matching(final Query query) {
102103
scanCons = scanConsistency;
103104
}
104105
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
105-
options, distinctFields);
106+
options, distinctFields, fields);
106107
}
107108

108109
@Override
109110
@Deprecated
110111
public FindByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
111112
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
112-
collection, options, distinctFields);
113+
collection, options, distinctFields, fields);
113114
}
114115

115116
@Override
116117
public FindByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
117118
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
118-
collection, options, distinctFields);
119+
collection, options, distinctFields, fields);
119120
}
120121

121122
@Override
122123
public <R> FindByQueryWithConsistency<R> as(final Class<R> returnType) {
123124
Assert.notNull(returnType, "returnType must not be null!");
124125
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
125-
collection, options, distinctFields);
126+
collection, options, distinctFields, fields);
127+
}
128+
129+
@Override
130+
public FindByQueryWithProjection<T> project(String[] fields) {
131+
Assert.notNull(fields, "Fields must not be null");
132+
Assert.isNull(distinctFields, "only one of project(fields) and distinct(distinctFields) can be specified");
133+
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
134+
collection, options, distinctFields, fields);
126135
}
127136

128137
@Override
129138
public FindByQueryWithProjection<T> distinct(final String[] distinctFields) {
130-
Assert.notNull(distinctFields, "distinctFields must not be null!");
139+
Assert.notNull(distinctFields, "distinctFields must not be null");
140+
Assert.isNull(fields, "only one of project(fields) and distinct(distinctFields) can be specified");
131141
// Coming from an annotation, this cannot be null.
132142
// But a non-null but empty distinctFields means distinct on all fields
133143
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
134144
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
135145
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
136-
collection, options, dFields);
146+
collection, options, dFields, fields);
137147
}
138148

139149
@Override
@@ -144,8 +154,8 @@ public Stream<T> stream() {
144154
@Override
145155
public long count() {
146156
Long l = reactiveSupport.count().block();
147-
if ( l == null ){
148-
throw new CouchbaseQueryExecutionException("count query did not return a count : "+query.export());
157+
if (l == null) {
158+
throw new CouchbaseQueryExecutionException("count query did not return a count : " + query.export());
149159
}
150160
return l;
151161
}
@@ -159,19 +169,19 @@ public boolean exists() {
159169
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
160170
Assert.notNull(options, "Options must not be null.");
161171
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
162-
collection, options, distinctFields);
172+
collection, options, distinctFields, fields);
163173
}
164174

165175
@Override
166176
public FindByQueryInCollection<T> inScope(final String scope) {
167177
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
168-
collection, options, distinctFields);
178+
collection, options, distinctFields, fields);
169179
}
170180

171181
@Override
172182
public FindByQueryWithConsistency<T> inCollection(final String collection) {
173183
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
174-
collection, options, distinctFields);
184+
collection, options, distinctFields, fields);
175185
}
176186

177187
}

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,30 @@ interface FindByQueryWithProjection<T> extends FindByQueryWithConsistency<T> {
201201
<R> FindByQueryWithConsistency<R> as(Class<R> returnType);
202202
}
203203

204+
/**
205+
* Fluent method to specify fields to project.
206+
*
207+
* @param <T> the entity type to use for the results.
208+
*/
209+
interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {
210+
211+
/**
212+
* Define the target type fields should be mapped to. <br />
213+
* Skip this step if you are anyway only interested in the original domain type.
214+
*
215+
* @param fields to project
216+
* @return new instance of {@link FindByQueryWithConsistency}.
217+
* @throws IllegalArgumentException if returnType is {@literal null}.
218+
*/
219+
FindByQueryWithProjection<T> project(String[] fields);
220+
}
221+
204222
/**
205223
* Fluent method to specify DISTINCT fields
206224
*
207225
* @param <T> the entity type to use for the results.
208226
*/
209-
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjection<T>, WithDistinct<T> {
227+
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithDistinct<T> {
210228

211229
/**
212230
* Finds the distinct values for a specified {@literal field} across a single {@link } or view.

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

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public ReactiveFindByQueryOperationSupport(final ReactiveCouchbaseTemplate templ
5050
@Override
5151
public <T> ReactiveFindByQuery<T> findByQuery(final Class<T> domainType) {
5252
return new ReactiveFindByQuerySupport<>(template, domainType, domainType, ALL_QUERY, null, null, null, null, null,
53-
template.support());
53+
null, template.support());
5454
}
5555

5656
static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
@@ -63,12 +63,13 @@ static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
6363
private final String collection;
6464
private final String scope;
6565
private final String[] distinctFields;
66+
private final String[] fields;
6667
private final QueryOptions options;
6768
private final ReactiveTemplateSupport support;
6869

6970
ReactiveFindByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType,
7071
final Class<T> returnType, final Query query, final QueryScanConsistency scanConsistency, final String scope,
71-
final String collection, final QueryOptions options, final String[] distinctFields,
72+
final String collection, final QueryOptions options, final String[] distinctFields, final String[] fields,
7273
final ReactiveTemplateSupport support) {
7374
Assert.notNull(domainType, "domainType must not be null!");
7475
Assert.notNull(returnType, "returnType must not be null!");
@@ -81,6 +82,7 @@ static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
8182
this.collection = collection;
8283
this.options = options;
8384
this.distinctFields = distinctFields;
85+
this.fields = fields;
8486
this.support = support;
8587
}
8688

@@ -94,57 +96,65 @@ public FindByQueryWithQuery<T> matching(Query query) {
9496
scanCons = scanConsistency;
9597
}
9698
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
97-
options, distinctFields, support);
99+
options, distinctFields, fields, support);
98100
}
99101

100102
@Override
101103
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
102104
Assert.notNull(options, "Options must not be null.");
103105
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
104-
collection, options, distinctFields, support);
106+
collection, options, distinctFields, fields, support);
105107
}
106108

107109
@Override
108110
public FindByQueryInCollection<T> inScope(final String scope) {
109111
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
110-
collection, options, distinctFields, support);
112+
collection, options, distinctFields, fields, support);
111113
}
112114

113115
@Override
114116
public FindByQueryWithConsistency<T> inCollection(final String collection) {
115117
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
116-
collection, options, distinctFields, support);
118+
collection, options, distinctFields, fields, support);
117119
}
118120

119121
@Override
120122
@Deprecated
121123
public FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency) {
122124
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
123-
collection, options, distinctFields, support);
125+
collection, options, distinctFields, fields, support);
124126
}
125127

126128
@Override
127129
public FindByQueryWithConsistency<T> withConsistency(QueryScanConsistency scanConsistency) {
128130
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
129-
collection, options, distinctFields, support);
131+
collection, options, distinctFields, fields, support);
130132
}
131133

132-
@Override
133134
public <R> FindByQueryWithConsistency<R> as(Class<R> returnType) {
134135
Assert.notNull(returnType, "returnType must not be null!");
135136
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
136-
collection, options, distinctFields, support);
137+
collection, options, distinctFields, fields, support);
138+
}
139+
140+
@Override
141+
public FindByQueryWithProjection<T> project(String[] fields) {
142+
Assert.notNull(fields, "Fields must not be null");
143+
Assert.isNull(distinctFields, "only one of project(fields) and distinct(distinctFields) can be specified");
144+
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
145+
collection, options, distinctFields, fields, support);
137146
}
138147

139148
@Override
140149
public FindByQueryWithDistinct<T> distinct(final String[] distinctFields) {
141-
Assert.notNull(distinctFields, "distinctFields must not be null!");
150+
Assert.notNull(distinctFields, "distinctFields must not be null");
151+
Assert.isNull(fields, "only one of project(fields) and distinct(distinctFields) can be specified");
142152
// Coming from an annotation, this cannot be null.
143153
// But a non-null but empty distinctFields means distinct on all fields
144154
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
145155
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
146156
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
147-
collection, options, dFields, support);
157+
collection, options, dFields, fields, support);
148158
}
149159

150160
@Override
@@ -228,7 +238,7 @@ public Mono<Boolean> exists() {
228238

229239
private String assembleEntityQuery(final boolean count, String[] distinctFields, String collection) {
230240
return query.toN1qlSelectString(template, collection, this.domainType, this.returnType, count,
231-
query.getDistinctFields() != null ? query.getDistinctFields() : distinctFields);
241+
query.getDistinctFields() != null ? query.getDistinctFields() : distinctFields, fields);
232242
}
233243
}
234244
}

src/main/java/org/springframework/data/couchbase/core/query/Query.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -320,13 +320,13 @@ public String export(int[]... paramIndexPtrHolder) { // used only by tests
320320
}
321321

322322
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
323-
return toN1qlSelectString(template, null, domainClass, null, isCount, null);
323+
return toN1qlSelectString(template, null, domainClass, null, isCount, null, null);
324324
}
325325

326326
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass,
327-
Class returnClass, boolean isCount, String[] distinctFields) {
327+
Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
328328
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, collectionName, domainClass,
329-
returnClass, isCount, distinctFields);
329+
returnClass, isCount, distinctFields, fields);
330330
final StringBuilder statement = new StringBuilder();
331331
appendString(statement, n1ql.selectEntity); // select ...
332332
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
@@ -340,7 +340,7 @@ public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String coll
340340

341341
public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass) {
342342
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, collectionName, domainClass, null,
343-
false, null);
343+
false, null, null);
344344
final StringBuilder statement = new StringBuilder();
345345
appendString(statement, n1ql.delete); // delete ...
346346
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
@@ -350,7 +350,7 @@ public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String coll
350350
}
351351

352352
StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTemplate template, String collectionName,
353-
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields) {
353+
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
354354
String typeKey = template.getConverter().getTypeKey();
355355
final CouchbasePersistentEntity<?> persistentEntity = template.getConverter().getMappingContext()
356356
.getRequiredPersistentEntity(domainClass);
@@ -363,7 +363,7 @@ StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTem
363363
}
364364

365365
StringBasedN1qlQueryParser sbnqp = new StringBasedN1qlQueryParser(template.getBucketName(), collectionName,
366-
template.getConverter(), domainClass, returnClass, typeKey, typeValue, distinctFields);
366+
template.getConverter(), domainClass, returnClass, typeKey, typeValue, isCount, distinctFields, fields);
367367
return isCount ? sbnqp.getCountContext() : sbnqp.getStatementContext();
368368
}
369369

src/main/java/org/springframework/data/couchbase/core/query/StringQuery.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
*/
1616
package org.springframework.data.couchbase.core.query;
1717

18+
import java.util.Locale;
19+
1820
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
21+
import org.springframework.data.couchbase.core.support.TemplateUtils;
1922

2023
import com.couchbase.client.java.json.JsonArray;
2124
import com.couchbase.client.java.json.JsonValue;
22-
import org.springframework.data.couchbase.core.support.TemplateUtils;
23-
24-
import java.util.Locale;
2525

2626
/**
2727
* Query created from the string in @Query annotation in the repository interface.
@@ -56,7 +56,7 @@ private void appendInlineN1qlStatement(final StringBuilder sb) {
5656

5757
@Override
5858
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collection, Class domainClass,
59-
Class resultClass, boolean isCount, String[] distinctFields) {
59+
Class resultClass, boolean isCount, String[] distinctFields, String[] fields) {
6060
final StringBuilder statement = new StringBuilder();
6161
boolean makeCount = isCount && inlineN1qlQuery != null
6262
&& !inlineN1qlQuery.toLowerCase(Locale.ROOT).contains("count(");
@@ -95,6 +95,6 @@ public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String coll
9595
*/
9696
@Override
9797
public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass) {
98-
return toN1qlSelectString(template, collectionName, domainClass, domainClass, false, null);
98+
return toN1qlSelectString(template, collectionName, domainClass, domainClass, false, null, null);
9999
}
100100
}

0 commit comments

Comments
 (0)