Skip to content

Commit 3aaeaa0

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 3aaeaa0

35 files changed

+845
-361
lines changed

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/ReactiveExistsByIdOperationSupport.java

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

18+
import org.springframework.data.couchbase.core.query.OptionsBuilder;
1819
import reactor.core.publisher.Flux;
1920
import reactor.core.publisher.Mono;
2021
import reactor.util.function.Tuple2;
@@ -70,12 +71,11 @@ 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())).map(ExistsResult::exists))
7979
.onErrorMap(throwable -> {
8080
if (throwable instanceof RuntimeException) {
8181
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
@@ -85,6 +85,10 @@ public Mono<Boolean> one(final String id) {
8585
});
8686
}
8787

88+
private ExistsOptions buildOptions(ExistsOptions options) {
89+
return OptionsBuilder.buildExistsOptions(options);
90+
}
91+
8892
@Override
8993
public Mono<Map<String, Boolean>> all(final Collection<String> ids) {
9094
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: 0 additions & 2 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
/**

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).flatMap(docId ->
78+
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))
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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,6 @@ interface TerminatingInsertById<T> extends OneAndAllEntityReactive<T> {
6868
@Override
6969
Flux<? extends T> all(Collection<? extends T> objects);
7070

71-
InsertOptions buildOptions(InsertOptions options, CouchbaseDocument doc);
72-
7371
}
7472

7573
/**

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

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.slf4j.Logger;
2525
import org.slf4j.LoggerFactory;
2626
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
27+
import org.springframework.data.couchbase.core.query.OptionsBuilder;
2728
import org.springframework.data.couchbase.core.support.PseudoArgs;
2829
import org.springframework.util.Assert;
2930

@@ -34,8 +35,8 @@
3435

3536
public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOperation {
3637

37-
private static final Logger LOG = LoggerFactory.getLogger(ReactiveInsertByIdOperationSupport.class);
3838
private final ReactiveCouchbaseTemplate template;
39+
private static final Logger LOG = LoggerFactory.getLogger(ReactiveInsertByIdOperationSupport.class);
3940

4041
public ReactiveInsertByIdOperationSupport(final ReactiveCouchbaseTemplate template) {
4142
this.template = template;
@@ -79,8 +80,7 @@ static class ReactiveInsertByIdSupport<T> implements ReactiveInsertById<T> {
7980
@Override
8081
public Mono<T> one(T object) {
8182
PseudoArgs<InsertOptions> pArgs = new PseudoArgs(template, scope, collection, options, domainType);
82-
LOG.trace("statement: {} scope: {} collection: {} options: {}", "insertById", pArgs.getScope(),
83-
pArgs.getCollection(), pArgs.getOptions());
83+
LOG.trace("insertById {}", pArgs);
8484
return Mono.just(object).flatMap(support::encodeEntity)
8585
.flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
8686
.getCollection(pArgs.getCollection()).reactive()
@@ -101,20 +101,8 @@ public Flux<? extends T> all(Collection<? extends T> objects) {
101101
return Flux.fromIterable(objects).flatMap(this::one);
102102
}
103103

104-
@Override
105104
public InsertOptions buildOptions(InsertOptions options, CouchbaseDocument doc) { // CouchbaseDocument converted
106-
options = options != null ? options : InsertOptions.insertOptions();
107-
if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) {
108-
options.durability(persistTo, replicateTo);
109-
} else if (durabilityLevel != DurabilityLevel.NONE) {
110-
options.durability(durabilityLevel);
111-
}
112-
if (expiry != null) {
113-
options.expiry(expiry);
114-
} else if (doc.getExpiration() != 0) {
115-
options.expiry(Duration.ofSeconds(doc.getExpiration()));
116-
}
117-
return options;
105+
return OptionsBuilder.buildInsertOptions(options, persistTo, replicateTo, durabilityLevel, expiry, doc);
118106
}
119107

120108
@Override

0 commit comments

Comments
 (0)