Skip to content

Commit 4f6b678

Browse files
committed
Scopes and Collections for Repositories.
This adds scope and collection support to repositories via the DynamicInvocationHandler and PseudoArgs. It also adds annotations for scopes and collections. Closes #963
1 parent 39233d9 commit 4f6b678

File tree

53 files changed

+1121
-519
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+1121
-519
lines changed

src/main/java/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.util.HashSet;
2323
import java.util.Set;
2424

25-
import org.springframework.beans.factory.annotation.Autowired;
2625
import org.springframework.beans.factory.config.BeanDefinition;
2726
import org.springframework.context.annotation.Bean;
2827
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.springframework.data.couchbase.core;
1919

20+
import org.slf4j.Logger;
21+
import org.slf4j.LoggerFactory;
2022
import org.springframework.beans.BeansException;
2123
import org.springframework.context.ApplicationContext;
2224
import org.springframework.context.ApplicationContextAware;
@@ -38,9 +40,6 @@
3840
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
3941
import org.springframework.util.Assert;
4042

41-
import org.slf4j.Logger;
42-
import org.slf4j.LoggerFactory;
43-
4443
/**
4544
* Internal encode/decode support for CouchbaseTemplate.
4645
*

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public FindByIdInCollection<T> inScope(final String scope) {
8787

8888
@Override
8989
public FindByIdInScope<T> project(String... fields) {
90-
Assert.notEmpty(fields, "Fields must not be null nor empty.");
90+
Assert.notEmpty(fields, "Fields must not be null.");
9191
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields));
9292
}
9393

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,12 @@ public <R> FindByQueryWithConsistency<R> as(final Class<R> returnType) {
127127
@Override
128128
public FindByQueryWithProjection<T> distinct(final String[] distinctFields) {
129129
Assert.notNull(distinctFields, "distinctFields must not be null!");
130+
// Coming from an annotation, this cannot be null.
131+
// But a non-null but empty distinctFields means distinct on all fields
132+
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
133+
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
130134
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
131-
collection, options, distinctFields);
135+
collection, options, dFields);
132136
}
133137

134138
@Override

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ public interface ExecutableRemoveByIdOperation {
3939
* Removes a document.
4040
*/
4141
ExecutableRemoveById removeById(Class<?> domainType);
42+
4243
/**
4344
* Removes a document.
4445
*/
4546
@Deprecated
4647
ExecutableRemoveById removeById();
48+
4749
/**
4850
* Terminating operations invoking the actual execution.
4951
*/

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

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

18-
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
18+
import reactor.core.publisher.Mono;
1919

20+
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
2021
import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent;
21-
import reactor.core.publisher.Mono;
2222

2323
/**
2424
* Wrapper of {@link TemplateSupport} methods to adapt them to {@link ReactiveTemplateSupport}.

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import org.slf4j.Logger;
2727
import org.slf4j.LoggerFactory;
28+
import org.springframework.data.couchbase.core.query.OptionsBuilder;
2829
import org.springframework.data.couchbase.core.support.PseudoArgs;
2930
import org.springframework.util.Assert;
3031

@@ -70,12 +71,12 @@ static class ReactiveExistsByIdSupport implements ReactiveExistsById {
7071

7172
@Override
7273
public Mono<Boolean> one(final String id) {
73-
PseudoArgs<ExistsOptions> pArgs = new PseudoArgs<>(template, scope, collection,
74-
options != null ? options : ExistsOptions.existsOptions(), domainType);
75-
LOG.trace("statement: {} scope: {} collection: {}", "exitsById", pArgs.getScope(), pArgs.getCollection());
74+
PseudoArgs<ExistsOptions> pArgs = new PseudoArgs<>(template, scope, collection, options, domainType);
75+
LOG.trace("existsById {}", pArgs);
7676
return Mono.just(id)
7777
.flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
78-
.getCollection(pArgs.getCollection()).reactive().exists(id, pArgs.getOptions()).map(ExistsResult::exists))
78+
.getCollection(pArgs.getCollection()).reactive().exists(id, buildOptions(pArgs.getOptions()))
79+
.map(ExistsResult::exists))
7980
.onErrorMap(throwable -> {
8081
if (throwable instanceof RuntimeException) {
8182
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
@@ -85,6 +86,10 @@ public Mono<Boolean> one(final String id) {
8586
});
8687
}
8788

89+
private ExistsOptions buildOptions(ExistsOptions options) {
90+
return OptionsBuilder.buildExistsOptions(options);
91+
}
92+
8893
@Override
8994
public Mono<Map<String, Boolean>> all(final Collection<String> ids) {
9095
return Flux.fromIterable(ids).flatMap(id -> one(id).map(result -> Tuples.of(id, result)))

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

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperation {
3737

3838
private final ReactiveCouchbaseTemplate template;
39-
4039
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByIdOperationSupport.class);
4140

4241
ReactiveFindByIdOperationSupport(ReactiveCouchbaseTemplate template) {
@@ -71,19 +70,19 @@ static class ReactiveFindByIdSupport<T> implements ReactiveFindById<T> {
7170

7271
@Override
7372
public Mono<T> one(final String id) {
74-
return Mono.just(id).flatMap(docId -> {
75-
GetOptions gOptions = options != null ? options : getOptions();
76-
if (gOptions.build().transcoder() == null) {
77-
gOptions.transcoder(RawJsonTranscoder.INSTANCE);
78-
}
79-
if (fields != null && !fields.isEmpty()) {
80-
gOptions.project(fields);
81-
}
82-
PseudoArgs<GetOptions> pArgs = new PseudoArgs(template, scope, collection, gOptions, domainType);
83-
LOG.trace("statement: {} scope: {} collection: {}", "findById", pArgs.getScope(), pArgs.getCollection());
84-
return template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getCollection(pArgs.getCollection())
85-
.reactive().get(docId, pArgs.getOptions());
86-
}).flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType))
73+
GetOptions gOptions = options != null ? options : getOptions();
74+
if (gOptions.build().transcoder() == null) {
75+
gOptions.transcoder(RawJsonTranscoder.INSTANCE);
76+
}
77+
if (fields != null && !fields.isEmpty()) {
78+
gOptions.project(fields);
79+
}
80+
PseudoArgs<GetOptions> pArgs = new PseudoArgs(template, scope, collection, gOptions, domainType);
81+
LOG.trace("findById {}", pArgs);
82+
return Mono.just(id)
83+
.flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
84+
.getCollection(pArgs.getCollection()).reactive().get(docId, pArgs.getOptions()))
85+
.flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType))
8786
.onErrorResume(throwable -> {
8887
if (throwable instanceof RuntimeException) {
8988
if (throwable instanceof DocumentNotFoundException) {
@@ -123,7 +122,7 @@ public FindByIdInCollection<T> inScope(final String scope) {
123122

124123
@Override
125124
public FindByIdInScope<T> project(String... fields) {
126-
Assert.notEmpty(fields, "Fields must not be null nor empty.");
125+
Assert.notNull(fields, "Fields must not be null");
127126
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields),
128127
support);
129128
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ interface TerminatingFindByQuery<T> extends OneAndAllReactive<T> {
8989
*/
9090
Mono<Boolean> exists();
9191

92-
QueryOptions buildOptions(QueryOptions options);
93-
9492
}
9593

9694
/**
@@ -170,7 +168,7 @@ interface FindByQueryConsistentWith<T> extends FindByQueryInScope<T> {
170168
}
171169

172170
/**
173-
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
171+
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
174172
*
175173
* @param <T> the entity type to use for the results.
176174
*/

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

Lines changed: 56 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
4141
private static final Query ALL_QUERY = new Query();
4242

4343
private final ReactiveCouchbaseTemplate template;
44-
4544
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByQueryOperationSupport.class);
4645

4746
public ReactiveFindByQueryOperationSupport(final ReactiveCouchbaseTemplate template) {
@@ -62,7 +61,7 @@ static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
6261
private final Query query;
6362
private final QueryScanConsistency scanConsistency;
6463
private final String collection;
65-
private String scope;
64+
private final String scope;
6665
private final String[] distinctFields;
6766
private final QueryOptions options;
6867
private final ReactiveTemplateSupport support;
@@ -138,10 +137,14 @@ public <R> FindByQueryWithConsistency<R> as(Class<R> returnType) {
138137
}
139138

140139
@Override
141-
public FindByQueryWithDistinct<T> distinct(String[] distinctFields) {
140+
public FindByQueryWithDistinct<T> distinct(final String[] distinctFields) {
142141
Assert.notNull(distinctFields, "distinctFields must not be null!");
142+
// Coming from an annotation, this cannot be null.
143+
// But a non-null but empty distinctFields means distinct on all fields
144+
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
145+
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
143146
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
144-
collection, options, distinctFields, support);
147+
collection, options, dFields, support);
145148
}
146149

147150
@Override
@@ -156,73 +159,65 @@ public Mono<T> first() {
156159

157160
@Override
158161
public Flux<T> all() {
159-
return Flux.defer(() -> {
160-
PseudoArgs<QueryOptions> pArgs = new PseudoArgs(template, scope, collection,
161-
options != null ? options : QueryOptions.queryOptions(), domainType);
162-
String statement = assembleEntityQuery(false, distinctFields, pArgs.getCollection());
163-
LOG.trace("statement: {} {}", "findByQuery", statement);
164-
Mono<ReactiveQueryResult> allResult = pArgs.getScope() == null
165-
? template.getCouchbaseClientFactory().getCluster().reactive().query(statement,
166-
buildOptions(pArgs.getOptions()))
167-
: template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement,
168-
buildOptions(pArgs.getOptions()));
169-
return allResult.onErrorMap(throwable -> {
170-
if (throwable instanceof RuntimeException) {
171-
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
172-
} else {
173-
return throwable;
162+
PseudoArgs<QueryOptions> pArgs = new PseudoArgs(template, scope, collection, options, domainType);
163+
String statement = assembleEntityQuery(false, distinctFields, pArgs.getCollection());
164+
LOG.trace("findByQuery {} statement: {}", pArgs, statement);
165+
Mono<ReactiveQueryResult> allResult = pArgs.getScope() == null
166+
? template.getCouchbaseClientFactory().getCluster().reactive().query(statement,
167+
buildOptions(pArgs.getOptions()))
168+
: template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement,
169+
buildOptions(pArgs.getOptions()));
170+
return Flux.defer(() -> allResult.onErrorMap(throwable -> {
171+
if (throwable instanceof RuntimeException) {
172+
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
173+
} else {
174+
return throwable;
175+
}
176+
}).flatMapMany(ReactiveQueryResult::rowsAsObject).flatMap(row -> {
177+
String id = "";
178+
long cas = 0;
179+
if (distinctFields == null) {
180+
if (row.getString(TemplateUtils.SELECT_ID) == null) {
181+
return Flux.error(new CouchbaseException(
182+
"query did not project " + TemplateUtils.SELECT_ID + ". Either use #{#n1ql.selectEntity} or project "
183+
+ TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement));
174184
}
175-
}).flatMapMany(ReactiveQueryResult::rowsAsObject).flatMap(row -> {
176-
String id = "";
177-
long cas = 0;
178-
if (distinctFields == null) {
179-
if (row.getString(TemplateUtils.SELECT_ID) == null) {
180-
return Flux.error(new CouchbaseException(
181-
"query did not project " + TemplateUtils.SELECT_ID + ". Either use #{#n1ql.selectEntity} or project "
182-
+ TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement));
183-
}
184-
id = row.getString(TemplateUtils.SELECT_ID);
185-
if (row.getLong(TemplateUtils.SELECT_CAS) == null) {
186-
return Flux.error(new CouchbaseException(
187-
"query did not project " + TemplateUtils.SELECT_CAS + ". Either use #{#n1ql.selectEntity} or project "
188-
+ TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement));
189-
}
190-
cas = row.getLong(TemplateUtils.SELECT_CAS);
191-
row.removeKey(TemplateUtils.SELECT_ID);
192-
row.removeKey(TemplateUtils.SELECT_CAS);
185+
id = row.getString(TemplateUtils.SELECT_ID);
186+
if (row.getLong(TemplateUtils.SELECT_CAS) == null) {
187+
return Flux.error(new CouchbaseException(
188+
"query did not project " + TemplateUtils.SELECT_CAS + ". Either use #{#n1ql.selectEntity} or project "
189+
+ TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement));
193190
}
194-
return support.decodeEntity(id, row.toString(), cas, returnType);
195-
});
196-
});
191+
cas = row.getLong(TemplateUtils.SELECT_CAS);
192+
row.removeKey(TemplateUtils.SELECT_ID);
193+
row.removeKey(TemplateUtils.SELECT_CAS);
194+
}
195+
return support.decodeEntity(id, row.toString(), cas, returnType);
196+
}));
197197
}
198198

199-
@Override
200-
public QueryOptions buildOptions(QueryOptions options) {
199+
private QueryOptions buildOptions(QueryOptions options) {
201200
QueryOptions opts = query.buildQueryOptions(options, scanConsistency);
202201
return opts;
203202
}
204203

205204
@Override
206205
public Mono<Long> count() {
207-
return Mono.defer(() -> {
208-
PseudoArgs<QueryOptions> pArgs = new PseudoArgs(template, scope, collection, options, domainType);
209-
String statement = assembleEntityQuery(true, distinctFields, pArgs.getCollection());
210-
LOG.trace("statement: {} {}", "findByQuery", statement);
211-
Mono<ReactiveQueryResult> countResult = pArgs.getScope() == null
212-
? template.getCouchbaseClientFactory().getCluster().reactive().query(statement,
213-
buildOptions(pArgs.getOptions()))
214-
: template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement,
215-
buildOptions(pArgs.getOptions()));
216-
return countResult.onErrorMap(throwable -> {
217-
if (throwable instanceof RuntimeException) {
218-
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
219-
} else {
220-
return throwable;
221-
}
222-
}).flatMapMany(ReactiveQueryResult::rowsAsObject).map(row -> {
223-
return row.getLong(TemplateUtils.SELECT_COUNT);
224-
}).next();
225-
});
206+
PseudoArgs<QueryOptions> pArgs = new PseudoArgs(template, scope, collection, options, domainType);
207+
String statement = assembleEntityQuery(true, distinctFields, pArgs.getCollection());
208+
LOG.trace("findByQuery {} statement: {}", pArgs, statement);
209+
Mono<ReactiveQueryResult> countResult = pArgs.getScope() == null
210+
? template.getCouchbaseClientFactory().getCluster().reactive().query(statement,
211+
buildOptions(pArgs.getOptions()))
212+
: template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement,
213+
buildOptions(pArgs.getOptions()));
214+
return Mono.defer(() -> countResult.onErrorMap(throwable -> {
215+
if (throwable instanceof RuntimeException) {
216+
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
217+
} else {
218+
return throwable;
219+
}
220+
}).flatMapMany(ReactiveQueryResult::rowsAsObject).map(row -> row.getLong(TemplateUtils.SELECT_COUNT)).next());
226221
}
227222

228223
@Override

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,16 @@ static class ReactiveFindFromReplicasByIdSupport<T> implements ReactiveFindFromR
6868

6969
@Override
7070
public Mono<T> any(final String id) {
71-
return Mono.just(id).flatMap(docId -> {
72-
GetAnyReplicaOptions garOptions = options != null ? options : getAnyReplicaOptions();
73-
if (garOptions.build().transcoder() == null) {
74-
garOptions.transcoder(RawJsonTranscoder.INSTANCE);
75-
}
76-
PseudoArgs<GetAnyReplicaOptions> pArgs = new PseudoArgs<>(template, scope, collection, garOptions, domainType);
77-
LOG.trace("statement: {} scope: {} collection: {}", "getAnyReplica", pArgs.getScope(), pArgs.getCollection());
78-
return template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getCollection(pArgs.getCollection())
79-
.reactive().getAnyReplica(docId, pArgs.getOptions());
80-
}).flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), returnType))
71+
GetAnyReplicaOptions garOptions = options != null ? options : getAnyReplicaOptions();
72+
if (garOptions.build().transcoder() == null) {
73+
garOptions.transcoder(RawJsonTranscoder.INSTANCE);
74+
}
75+
PseudoArgs<GetAnyReplicaOptions> pArgs = new PseudoArgs<>(template, scope, collection, garOptions, domainType);
76+
LOG.trace("getAnyReplica {}", pArgs);
77+
return Mono.just(id)
78+
.flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
79+
.getCollection(pArgs.getCollection()).reactive().getAnyReplica(docId, pArgs.getOptions()))
80+
.flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), returnType))
8181
.onErrorMap(throwable -> {
8282
if (throwable instanceof RuntimeException) {
8383
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.time.Duration;
2222
import java.util.Collection;
2323

24-
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
2524
import org.springframework.data.couchbase.core.support.InCollection;
2625
import org.springframework.data.couchbase.core.support.InScope;
2726
import org.springframework.data.couchbase.core.support.OneAndAllEntityReactive;
@@ -68,8 +67,6 @@ interface TerminatingInsertById<T> extends OneAndAllEntityReactive<T> {
6867
@Override
6968
Flux<? extends T> all(Collection<? extends T> objects);
7069

71-
InsertOptions buildOptions(InsertOptions options, CouchbaseDocument doc);
72-
7370
}
7471

7572
/**

0 commit comments

Comments
 (0)