Skip to content

Commit d57d136

Browse files
committed
DATACOUCH-603 Do not cast query parameters in N1qlQueryCreator.
For IN and NOT_IN - they can take varargs, and array or a JsonArray don't cast the parameter, let parameter accessor handle that. JsonArray.toString() provides the brackets, don't include them in the format Object[] array needs explicit conversion, as toString() is not sufficient Also cleaned up some weirdness in QueryCriteria.
2 parents 0f7e9a0 + e72cdba commit d57d136

35 files changed

+607
-320
lines changed

Jenkinsfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pipeline {
3737

3838
stage("Test other configurations") {
3939
when {
40-
anyOf {
40+
allOf {
4141
branch 'master'
4242
not { triggeredBy 'UpstreamCause' }
4343
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ interface FindByQueryConsistentWith<T> extends FindByQueryWithQuery<T> {
126126
*
127127
* @param scanConsistency the custom scan consistency to use for this query.
128128
*/
129-
FindByQueryWithQuery<T> consistentWith(QueryScanConsistency scanConsistency);
129+
FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency);
130130

131131
}
132132

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.data.couchbase.core.query.Query;
2222

2323
import com.couchbase.client.java.query.QueryScanConsistency;
24+
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport;
2425

2526
public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQueryOperation {
2627

@@ -50,8 +51,7 @@ static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T>
5051
this.template = template;
5152
this.domainType = domainType;
5253
this.query = query;
53-
this.reactiveSupport = new ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport<T>(
54-
template.reactive(), domainType, query, scanConsistency);
54+
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, query, scanConsistency);
5555
this.scanConsistency = scanConsistency;
5656
}
5757

@@ -72,11 +72,17 @@ public List<T> all() {
7272

7373
@Override
7474
public TerminatingFindByQuery<T> matching(final Query query) {
75-
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency);
75+
QueryScanConsistency scanCons;
76+
if (query.getScanConsistency() != null) {
77+
scanCons = query.getScanConsistency();
78+
} else {
79+
scanCons = scanConsistency;
80+
}
81+
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanCons);
7682
}
7783

7884
@Override
79-
public FindByQueryWithQuery<T> consistentWith(final QueryScanConsistency scanConsistency) {
85+
public FindByQueryConsistentWith<T> consistentWith(final QueryScanConsistency scanConsistency) {
8086
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency);
8187
}
8288

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ interface FindByQueryConsistentWith<T> extends FindByQueryWithQuery<T> {
100100
*
101101
* @param scanConsistency the custom scan consistency to use for this query.
102102
*/
103-
FindByQueryWithQuery<T> consistentWith(QueryScanConsistency scanConsistency);
103+
FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency);
104104

105105
}
106106

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,17 @@ static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
6060

6161
@Override
6262
public TerminatingFindByQuery<T> matching(Query query) {
63-
return new ReactiveFindByQuerySupport<>(template, domainType, query, scanConsistency);
63+
QueryScanConsistency scanCons;
64+
if (query.getScanConsistency() != null) {
65+
scanCons = query.getScanConsistency();
66+
} else {
67+
scanCons = scanConsistency;
68+
}
69+
return new ReactiveFindByQuerySupport<>(template, domainType, query, scanCons);
6470
}
6571

6672
@Override
67-
public FindByQueryWithQuery<T> consistentWith(QueryScanConsistency scanConsistency) {
73+
public FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency) {
6874
return new ReactiveFindByQuerySupport<>(template, domainType, query, scanConsistency);
6975
}
7076

@@ -121,7 +127,7 @@ public Mono<Boolean> exists() {
121127
}
122128

123129
private String assembleEntityQuery(final boolean count) {
124-
return query.toN1qlString(template, this.domainType, count);
130+
return query.toN1qlSelectString(template, this.domainType, count);
125131
}
126132

127133
}

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

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

18-
import com.couchbase.client.java.kv.UpsertOptions;
1918
import org.springframework.data.couchbase.core.mapping.Document;
2019
import reactor.core.publisher.Flux;
2120
import reactor.core.publisher.Mono;
@@ -98,7 +97,7 @@ private InsertOptions buildInsertOptions() {
9897
} else if (durabilityLevel != DurabilityLevel.NONE) {
9998
options.durability(durabilityLevel);
10099
}
101-
if (expiry != null) {
100+
if (expiry != null && ! expiry.isZero()) {
102101
options.expiry(expiry);
103102
} else if (domainType.isAnnotationPresent(Document.class)) {
104103
Document documentAnn = domainType.getAnnotation(Document.class);

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

Lines changed: 7 additions & 9 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.support.TemplateUtils;
1819
import reactor.core.publisher.Flux;
1920

2021
import java.util.Optional;
@@ -58,14 +59,7 @@ static class ReactiveRemoveByQuerySupport<T> implements ReactiveRemoveByQuery<T>
5859
@Override
5960
public Flux<RemoveResult> all() {
6061
return Flux.defer(() -> {
61-
String bucket = "`" + template.getBucketName() + "`";
62-
63-
String typeKey = template.getConverter().getTypeKey();
64-
String typeValue = template.support().getJavaNameForEntity(domainType);
65-
String where = " WHERE `" + typeKey + "` = \"" + typeValue + "\"";
66-
67-
String returning = " RETURNING meta().*";
68-
String statement = "DELETE FROM " + bucket + " " + where + returning;
62+
String statement = assembleDeleteQuery();
6963

7064
return template.getCouchbaseClientFactory().getCluster().reactive().query(statement, buildQueryOptions())
7165
.onErrorMap(throwable -> {
@@ -75,7 +69,7 @@ public Flux<RemoveResult> all() {
7569
return throwable;
7670
}
7771
}).flatMapMany(ReactiveQueryResult::rowsAsObject)
78-
.map(row -> new RemoveResult(row.getString("id"), row.getLong("cas"), Optional.empty()));
72+
.map(row -> new RemoveResult(row.getString(TemplateUtils.SELECT_ID), row.getLong(TemplateUtils.SELECT_CAS), Optional.empty()));
7973
});
8074
}
8175

@@ -97,6 +91,10 @@ public RemoveByQueryWithQuery<T> consistentWith(final QueryScanConsistency scanC
9791
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency);
9892
}
9993

94+
private String assembleDeleteQuery() {
95+
return query.toN1qlRemoveString(template, this.domainType);
96+
}
97+
10098
}
10199

102100
}

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

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

18-
import com.couchbase.client.java.kv.Expiry;
1918
import org.springframework.data.couchbase.core.mapping.Document;
2019
import reactor.core.publisher.Flux;
2120
import reactor.core.publisher.Mono;
@@ -98,7 +97,7 @@ private UpsertOptions buildUpsertOptions() {
9897
} else if (durabilityLevel != DurabilityLevel.NONE) {
9998
options.durability(durabilityLevel);
10099
}
101-
if (expiry != null) {
100+
if (expiry != null && !expiry.isZero()) {
102101
options.expiry(expiry);
103102
} else if (domainType.isAnnotationPresent(Document.class)) {
104103
Document documentAnn = domainType.getAnnotation(Document.class);

src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ public void write(final Object source, final CouchbaseDocument target) {
402402
typeMapper.writeType(type, target);
403403
}
404404

405-
writeInternal(source, target, type);
405+
writeInternal(source, target, type, true);
406406
if (target.getId() == null) {
407407
throw new MappingException("An ID property is needed, but not found/could not be generated on this entity.");
408408
}
@@ -416,7 +416,8 @@ public void write(final Object source, final CouchbaseDocument target) {
416416
* @param typeHint the type information for the source.
417417
*/
418418
@SuppressWarnings("unchecked")
419-
protected void writeInternal(final Object source, CouchbaseDocument target, final TypeInformation<?> typeHint) {
419+
protected void writeInternal(final Object source, CouchbaseDocument target, final TypeInformation<?> typeHint,
420+
boolean withId) {
420421
if (source == null) {
421422
return;
422423
}
@@ -437,7 +438,7 @@ protected void writeInternal(final Object source, CouchbaseDocument target, fina
437438
}
438439

439440
CouchbasePersistentEntity<?> entity = mappingContext.getPersistentEntity(source.getClass());
440-
writeInternal(source, target, entity);
441+
writeInternal(source, target, entity, withId);
441442
addCustomTypeKeyIfNecessary(typeHint, source, target);
442443
}
443444

@@ -471,9 +472,10 @@ private String convertToString(Object propertyObj) {
471472
* @param source the source object.
472473
* @param target the target document.
473474
* @param entity the persistent entity to convert from.
475+
* @param withId one of the top-level properties is the id for the document
474476
*/
475477
protected void writeInternal(final Object source, final CouchbaseDocument target,
476-
final CouchbasePersistentEntity<?> entity) {
478+
final CouchbasePersistentEntity<?> entity, boolean withId) {
477479
if (source == null) {
478480
return;
479481
}
@@ -483,7 +485,7 @@ protected void writeInternal(final Object source, final CouchbaseDocument target
483485
}
484486

485487
final ConvertingPropertyAccessor<Object> accessor = getPropertyAccessor(source);
486-
final CouchbasePersistentProperty idProperty = entity.getIdProperty();
488+
final CouchbasePersistentProperty idProperty = withId ? entity.getIdProperty() : null;
487489
final CouchbasePersistentProperty versionProperty = entity.getVersionProperty();
488490

489491
GeneratedValue generatedValueInfo = null;
@@ -525,7 +527,7 @@ public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
525527
}
526528

527529
if (!conversions.isSimpleType(propertyObj.getClass())) {
528-
writePropertyInternal(propertyObj, target, prop);
530+
writePropertyInternal(propertyObj, target, prop, false);
529531
} else {
530532
writeSimpleInternal(propertyObj, target, prop.getFieldName());
531533
}
@@ -552,7 +554,7 @@ public void doWithAssociation(final Association<CouchbasePersistentProperty> ass
552554
Class<?> type = inverseProp.getType();
553555
Object propertyObj = accessor.getProperty(inverseProp, type);
554556
if (null != propertyObj) {
555-
writePropertyInternal(propertyObj, target, inverseProp);
557+
writePropertyInternal(propertyObj, target, inverseProp, false);
556558
}
557559
}
558560
});
@@ -568,7 +570,7 @@ public void doWithAssociation(final Association<CouchbasePersistentProperty> ass
568570
*/
569571
@SuppressWarnings("unchecked")
570572
private void writePropertyInternal(final Object source, final CouchbaseDocument target,
571-
final CouchbasePersistentProperty prop) {
573+
final CouchbasePersistentProperty prop, boolean withId) {
572574
if (source == null) {
573575
return;
574576
}
@@ -617,7 +619,7 @@ private void writePropertyInternal(final Object source, final CouchbaseDocument
617619
CouchbasePersistentEntity<?> entity = isSubtype(prop.getType(), source.getClass())
618620
? mappingContext.getRequiredPersistentEntity(source.getClass())
619621
: mappingContext.getRequiredPersistentEntity(type);
620-
writeInternal(source, propertyDoc, entity);
622+
writeInternal(source, propertyDoc, entity, false);
621623
target.put(name, propertyDoc);
622624
}
623625

@@ -660,7 +662,7 @@ private CouchbaseDocument writeMapInternal(final Map<Object, Object> source, fin
660662
} else {
661663
CouchbaseDocument embeddedDoc = new CouchbaseDocument();
662664
TypeInformation<?> valueTypeInfo = type.isMap() ? type.getMapValueType() : ClassTypeInformation.OBJECT;
663-
writeInternal(val, embeddedDoc, valueTypeInfo);
665+
writeInternal(val, embeddedDoc, valueTypeInfo, false);
664666
target.put(simpleKey, embeddedDoc);
665667
}
666668
} else {
@@ -706,7 +708,7 @@ private CouchbaseList writeCollectionInternal(final Collection<?> source, final
706708
} else {
707709

708710
CouchbaseDocument embeddedDoc = new CouchbaseDocument();
709-
writeInternal(element, embeddedDoc, componentType);
711+
writeInternal(element, embeddedDoc, componentType, false);
710712
target.put(embeddedDoc);
711713
}
712714

@@ -937,7 +939,7 @@ public <R> R getPropertyValue(final CouchbasePersistentProperty property) {
937939
String expression = property.getSpelExpression();
938940
Object value = expression != null ? evaluator.evaluate(expression) : source.get(property.getFieldName());
939941

940-
if (property.isIdProperty()) {
942+
if (property.isIdProperty() && parent == null) {
941943
return (R) source.getId();
942944
}
943945
if (value == null) {

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public class Query {
4848
private long skip;
4949
private int limit;
5050
private Sort sort = Sort.unsorted();
51+
private QueryScanConsistency queryScanConsistency;
5152

5253
static private final Pattern WHERE_PATTERN = Pattern.compile("\\sWHERE\\s");
5354

@@ -128,6 +129,27 @@ public Query with(final Pageable pageable) {
128129
return with(pageable.getSort());
129130
}
130131

132+
/**
133+
* queryScanConsistency
134+
*
135+
* @return queryScanConsistency
136+
*/
137+
public QueryScanConsistency getScanConsistency() {
138+
return queryScanConsistency;
139+
}
140+
141+
142+
/**
143+
* Sets the given scan consistency on the {@link Query} instance.
144+
*
145+
* @param queryScanConsistency
146+
* @return this
147+
*/
148+
public Query scanConsistency(final QueryScanConsistency queryScanConsistency) {
149+
this.queryScanConsistency = queryScanConsistency;
150+
return this;
151+
}
152+
131153
/**
132154
* Adds a {@link Sort} to the {@link Query} instance.
133155
*
@@ -239,7 +261,7 @@ public String export(int[]... paramIndexPtrHolder) { // used only by tests
239261
return sb.toString();
240262
}
241263

242-
public String toN1qlString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
264+
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
243265
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, domainClass, isCount);
244266
final StringBuilder statement = new StringBuilder();
245267
appendString(statement, n1ql.selectEntity); // select ...
@@ -250,6 +272,16 @@ public String toN1qlString(ReactiveCouchbaseTemplate template, Class domainClass
250272
return statement.toString();
251273
}
252274

275+
public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, Class domainClass) {
276+
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, domainClass, false);
277+
final StringBuilder statement = new StringBuilder();
278+
appendString(statement, n1ql.delete); // delete ...
279+
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
280+
appendWhere(statement, null); // criteria on this Query
281+
appendString(statement, n1ql.returning);
282+
return statement.toString();
283+
}
284+
253285
StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTemplate template, Class domainClass,
254286
boolean isCount) {
255287
String typeKey = template.getConverter().getTypeKey();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ private void appendInlineN1qlStatement(final StringBuilder sb) {
5252
}
5353

5454
@Override
55-
public String toN1qlString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
55+
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
5656
final StringBuilder statement = new StringBuilder();
5757
appendInlineN1qlStatement(statement); // apply the string statement
5858
// To use generated parameters for literals

src/main/java/org/springframework/data/couchbase/repository/CouchbaseRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.List;
2020

21+
import com.couchbase.client.java.query.QueryScanConsistency;
2122
import org.springframework.data.domain.Sort;
2223
import org.springframework.data.repository.NoRepositoryBean;
2324
import org.springframework.data.repository.PagingAndSortingRepository;
@@ -34,6 +35,8 @@ public interface CouchbaseRepository<T, ID> extends PagingAndSortingRepository<T
3435
@Override
3536
List<T> findAll(Sort sort);
3637

38+
List<T> findAll(QueryScanConsistency queryScanConsistency);
39+
3740
@Override
3841
List<T> findAll();
3942

src/main/java/org/springframework/data/couchbase/repository/Query.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
import org.springframework.data.annotation.QueryAnnotation;
2525
import org.springframework.data.couchbase.core.CouchbaseTemplate;
26-
import org.springframework.data.couchbase.repository.query.StringN1qlBasedQuery;
2726

2827
/**
2928
* Annotation to support the use of N1QL queries with Couchbase.

0 commit comments

Comments
 (0)