Skip to content

Commit 41ee1f9

Browse files
authored
DATACOUCH-1066 - Make QueryCriteria key a N1qlExpression. (#1082)
This is a merge of mmonti's changeset into master. The changeset was made on top of 4.1.x instead of master so it has a little catching up to do. Co-authored-by: mikereiche <[email protected]>
1 parent 9356e43 commit 41ee1f9

13 files changed

+239
-128
lines changed

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

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors
2+
* Copyright 2012-2021 the original author or authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,25 +15,29 @@
1515
*/
1616
package org.springframework.data.couchbase.core.query;
1717

18+
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
19+
1820
import java.util.ArrayList;
1921
import java.util.Formatter;
2022
import java.util.LinkedList;
2123
import java.util.List;
2224

25+
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
26+
import org.springframework.lang.Nullable;
27+
2328
import com.couchbase.client.core.error.InvalidArgumentException;
2429
import com.couchbase.client.java.json.JsonArray;
2530
import com.couchbase.client.java.json.JsonObject;
2631
import com.couchbase.client.java.json.JsonValue;
27-
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
28-
import org.springframework.lang.Nullable;
2932

3033
/**
3134
* @author Michael Nitschinger
3235
* @author Michael Reiche
36+
* @author Mauro Monti
3337
*/
3438
public class QueryCriteria implements QueryCriteriaDefinition {
3539

36-
private final String key;
40+
private final N1QLExpression key;
3741
/**
3842
* Holds the chain itself, the current operator being always the last one.
3943
*/
@@ -46,16 +50,16 @@ public class QueryCriteria implements QueryCriteriaDefinition {
4650
private Object[] value;
4751
private String format;
4852

49-
QueryCriteria(List<QueryCriteria> chain, String key, Object[] value, ChainOperator chainOperator) {
53+
QueryCriteria(List<QueryCriteria> chain, N1QLExpression key, Object[] value, ChainOperator chainOperator) {
5054
this(chain, key, value, chainOperator, null, null);
5155
}
5256

53-
QueryCriteria(List<QueryCriteria> chain, String key, Object value, ChainOperator chainOperator) {
57+
QueryCriteria(List<QueryCriteria> chain, N1QLExpression key, Object value, ChainOperator chainOperator) {
5458
this(chain, key, new Object[] { value }, chainOperator, null, null);
5559
}
5660

57-
QueryCriteria(List<QueryCriteria> chain, String key, Object[] value, ChainOperator chainOperator, String operator,
58-
String format) {
61+
QueryCriteria(List<QueryCriteria> chain, N1QLExpression key, Object[] value, ChainOperator chainOperator,
62+
String operator, String format) {
5963
this.criteriaChain = chain;
6064
criteriaChain.add(this);
6165
this.key = key;
@@ -70,9 +74,16 @@ Object[] getValue() {
7074
}
7175

7276
/**
73-
* Static factory method to create a Criteria using the provided key.
77+
* Static factory method to create a Criteria using the provided String key.
7478
*/
7579
public static QueryCriteria where(String key) {
80+
return where(x(key));
81+
}
82+
83+
/**
84+
* Static factory method to create a Criteria using the provided N1QLExpression key.
85+
*/
86+
public static QueryCriteria where(N1QLExpression key) {
7687
return new QueryCriteria(new ArrayList<>(), key, null, null);
7788
}
7889

@@ -83,21 +94,29 @@ private static QueryCriteria wrap(QueryCriteria criteria) {
8394
}
8495

8596
public QueryCriteria and(String key) {
97+
return and(x(key));
98+
}
99+
100+
public QueryCriteria and(N1QLExpression key) {
86101
return new QueryCriteria(this.criteriaChain, key, null, ChainOperator.AND);
87102
}
88103

89104
public QueryCriteria and(QueryCriteria criteria) {
90105
return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.AND);
91106
}
92107

93-
public QueryCriteria or(QueryCriteria criteria) {
94-
return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.OR);
108+
public QueryCriteria or(String key) {
109+
return or(x(key));
95110
}
96111

97-
public QueryCriteria or(String key) {
112+
public QueryCriteria or(N1QLExpression key) {
98113
return new QueryCriteria(this.criteriaChain, key, null, ChainOperator.OR);
99114
}
100115

116+
public QueryCriteria or(QueryCriteria criteria) {
117+
return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.OR);
118+
}
119+
101120
public QueryCriteria eq(@Nullable Object o) {
102121
return is(o);
103122
}
@@ -343,7 +362,7 @@ public String export() { // used only by tests
343362
*/
344363
private StringBuilder exportSingle(StringBuilder sb, int[] paramIndexPtr, JsonValue parameters,
345364
CouchbaseConverter converter) {
346-
String fieldName = maybeBackTic(key);
365+
String fieldName = key == null ? null : key.toString(); // maybeBackTic(key);
347366
int valueLen = value == null ? 0 : value.length;
348367
Object[] v = new Object[valueLen + 2];
349368
v[0] = fieldName;
@@ -377,7 +396,7 @@ private StringBuilder exportSingle(StringBuilder sb, int[] paramIndexPtr, JsonVa
377396
* @param parameters - parameters of the query. If operands are parameterized, their values are added to parameters
378397
* @return string containing part of N1QL query
379398
*/
380-
private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, JsonValue parameters,
399+
private String maybeWrapValue(N1QLExpression key, Object value, int[] paramIndexPtr, JsonValue parameters,
381400
CouchbaseConverter converter) {
382401
if (paramIndexPtr != null) {
383402
if (paramIndexPtr[0] >= 0) {
@@ -397,10 +416,10 @@ private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, Jso
397416
JsonObject params = (JsonObject) parameters;
398417
// from StringBasedN1qlQueryParser.getNamedPlaceholderValues()
399418
try {
400-
params.put(key, convert(converter, value));
419+
params.put(key.toString(), convert(converter, value));
401420
} catch (InvalidArgumentException iae) {
402421
if (value instanceof Object[]) {
403-
params.put(key, JsonArray.from((Object[]) value));
422+
params.put(key.toString(), JsonArray.from((Object[]) value));
404423
} else {
405424
throw iae;
406425
}
@@ -416,7 +435,7 @@ private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, Jso
416435
} else if (value == null) {
417436
return "null";
418437
} else if (value instanceof Object[]) { // convert array into sequence of comma-separated values
419-
StringBuffer l = new StringBuffer();
438+
StringBuilder l = new StringBuilder();
420439
l.append("[");
421440
Object[] array = (Object[]) value;
422441
for (int i = 0; i < array.length; i++) {

src/main/java/org/springframework/data/couchbase/repository/query/N1qlCountQueryCreator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ public Pageable first() {
118118
return delegate.first();
119119
}
120120

121+
@Override
122+
public Pageable withPage(int i) {
123+
return new CountPageable(delegate.withPage(i));
124+
}
125+
121126
public boolean hasPrevious() {
122127
return delegate.hasPrevious();
123128
}

src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors
2+
* Copyright 2012-2021 the original author or authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,13 +15,18 @@
1515
*/
1616
package org.springframework.data.couchbase.repository.query;
1717

18+
import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
19+
import static org.springframework.data.couchbase.core.query.N1QLExpression.meta;
20+
import static org.springframework.data.couchbase.core.query.N1QLExpression.path;
21+
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
1822
import static org.springframework.data.couchbase.core.query.QueryCriteria.where;
1923

2024
import java.util.Iterator;
2125

2226
import org.springframework.core.convert.converter.Converter;
2327
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
2428
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
29+
import org.springframework.data.couchbase.core.query.N1QLExpression;
2530
import org.springframework.data.couchbase.core.query.Query;
2631
import org.springframework.data.couchbase.core.query.QueryCriteria;
2732
import org.springframework.data.domain.Sort;
@@ -36,33 +41,39 @@
3641
/**
3742
* @author Michael Nitschinger
3843
* @author Michael Reiche
44+
* @author Mauro Monti
3945
*/
4046
public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria> {
4147

48+
private static final String META_ID_PROPERTY = "id";
49+
private static final String META_CAS_PROPERTY = "cas";
50+
4251
private final ParameterAccessor accessor;
4352
private final MappingContext<?, CouchbasePersistentProperty> context;
4453
private final QueryMethod queryMethod;
4554
private final CouchbaseConverter converter;
55+
private final String bucketName;
4656

47-
public N1qlQueryCreator(final PartTree tree, final ParameterAccessor accessor, QueryMethod queryMethod,
48-
CouchbaseConverter converter) {
57+
public N1qlQueryCreator(final PartTree tree, final ParameterAccessor accessor, final QueryMethod queryMethod,
58+
final CouchbaseConverter converter, final String bucketName) {
4959
super(tree, accessor);
5060
this.accessor = accessor;
5161
this.context = converter.getMappingContext();
5262
this.queryMethod = queryMethod;
5363
this.converter = converter;
64+
this.bucketName = bucketName;
5465
}
5566

5667
@Override
5768
protected QueryCriteria create(final Part part, final Iterator<Object> iterator) {
5869
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
5970
CouchbasePersistentProperty property = path.getLeafProperty();
60-
return from(part, property, where(path.toDotPath(cvtr)), iterator);
71+
return from(part, property, where(addMetaIfRequired(path, property)), iterator);
6172
}
6273

6374
static Converter<? super CouchbasePersistentProperty, String> cvtr = (
64-
source) -> new StringBuilder(source.getName().length() + 2).append('`').append(source.getName()).append('`')
65-
.toString();
75+
source) -> new StringBuilder(source.getFieldName().length() + 2).append('`').append(source.getFieldName())
76+
.append('`').toString();
6677

6778
@Override
6879
protected QueryCriteria and(final Part part, final QueryCriteria base, final Iterator<Object> iterator) {
@@ -73,7 +84,7 @@ protected QueryCriteria and(final Part part, final QueryCriteria base, final Ite
7384
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
7485
CouchbasePersistentProperty property = path.getLeafProperty();
7586

76-
return from(part, property, base.and(path.toDotPath()), iterator);
87+
return from(part, property, base.and(addMetaIfRequired(path, property)), iterator);
7788
}
7889

7990
@Override
@@ -149,4 +160,16 @@ private QueryCriteria from(final Part part, final CouchbasePersistentProperty pr
149160
}
150161
}
151162

163+
private N1QLExpression addMetaIfRequired(
164+
final PersistentPropertyPath<CouchbasePersistentProperty> persistentPropertyPath,
165+
final CouchbasePersistentProperty property) {
166+
if (property.isIdProperty()) {
167+
return path(meta(i(bucketName)), i(META_ID_PROPERTY));
168+
}
169+
if (property.isVersionProperty()) {
170+
return path(meta(i(bucketName)), i(META_CAS_PROPERTY));
171+
}
172+
return x(persistentPropertyPath.toDotPath(cvtr));
173+
}
174+
152175
}

src/main/java/org/springframework/data/couchbase/repository/query/N1qlRepositoryQueryExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public Object execute(final Object[] parameters) {
7070
SPEL_PARSER, evaluationContextProvider, namedQueries).createQuery();
7171
} else {
7272
final PartTree tree = new PartTree(queryMethod.getName(), domainClass);
73-
query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter()).createQuery();
73+
query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter(), operations.getBucketName()).createQuery();
7474
}
7575

7676
ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation = (ExecutableFindByQueryOperation.ExecutableFindByQuery<?>) operations

src/main/java/org/springframework/data/couchbase/repository/query/PartTreeCouchbaseQuery.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ public PartTree getTree() {
7272
@Override
7373
protected Query createQuery(ParametersParameterAccessor accessor) {
7474

75-
N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter);
75+
N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter,
76+
getOperations().getBucketName());
7677
Query query = creator.createQuery();
7778

7879
if (tree.isLimiting()) {
@@ -88,7 +89,8 @@ protected Query createQuery(ParametersParameterAccessor accessor) {
8889
*/
8990
@Override
9091
protected Query createCountQuery(ParametersParameterAccessor accessor) {
91-
return new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter).createQuery();
92+
return new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter, getOperations().getBucketName())
93+
.createQuery();
9294
}
9395

9496
/*

src/main/java/org/springframework/data/couchbase/repository/query/ReactivePartTreeCouchbaseQuery.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ public PartTree getTree() {
7373
@Override
7474
protected Query createQuery(ParametersParameterAccessor accessor) {
7575

76-
N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter);
76+
N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter,
77+
getOperations().getBucketName());
7778
Query query = creator.createQuery();
7879

7980
if (tree.isLimiting()) {
@@ -91,7 +92,8 @@ protected Query createQuery(ParametersParameterAccessor accessor) {
9192
*/
9293
@Override
9394
protected Query createCountQuery(ParametersParameterAccessor accessor) {
94-
Query query = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter).createQuery();
95+
Query query = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter, getOperations().getBucketName())
96+
.createQuery();
9597
if (LOG.isDebugEnabled()) {
9698
LOG.debug("Created query {} for * fields.", query.export());
9799
}

src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ protected Query createQuery(ParametersParameterAccessor accessor) {
7979
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(accessor, getQueryMethod(),
8080
getOperations().getConverter(), getOperations().getBucketName(), expressionParser, evaluationContextProvider,
8181
namedQueries);
82+
8283
Query query = creator.createQuery();
8384

8485
if (LOG.isDebugEnabled()) {

src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreator.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020 the original author or authors
2+
* Copyright 2021 the original author or authors
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,9 +15,11 @@
1515
*/
1616
package org.springframework.data.couchbase.repository.query;
1717

18-
import com.couchbase.client.java.json.JsonArray;
19-
import com.couchbase.client.java.json.JsonObject;
20-
import com.couchbase.client.java.json.JsonValue;
18+
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
19+
import static org.springframework.data.couchbase.core.query.QueryCriteria.where;
20+
21+
import java.util.Iterator;
22+
2123
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
2224
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
2325
import org.springframework.data.couchbase.core.query.N1QLExpression;
@@ -36,12 +38,13 @@
3638
import org.springframework.data.repository.query.parser.PartTree;
3739
import org.springframework.expression.spel.standard.SpelExpressionParser;
3840

39-
import java.util.Iterator;
40-
41-
import static org.springframework.data.couchbase.core.query.QueryCriteria.where;
41+
import com.couchbase.client.java.json.JsonArray;
42+
import com.couchbase.client.java.json.JsonObject;
43+
import com.couchbase.client.java.json.JsonValue;
4244

4345
/**
4446
* @author Michael Reiche
47+
* @author Mauro Monti
4548
*/
4649
public class StringN1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria> {
4750

@@ -98,10 +101,9 @@ protected String getTypeValue() {
98101

99102
@Override
100103
protected QueryCriteria create(final Part part, final Iterator<Object> iterator) {
101-
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(
102-
part.getProperty());
104+
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
103105
CouchbasePersistentProperty property = path.getLeafProperty();
104-
return from(part, property, where(path.toDotPath()), iterator);
106+
return from(part, property, where(x(path.toDotPath())), iterator);
105107
}
106108

107109
@Override
@@ -110,11 +112,10 @@ protected QueryCriteria and(final Part part, final QueryCriteria base, final Ite
110112
return create(part, iterator);
111113
}
112114

113-
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(
114-
part.getProperty());
115+
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
115116
CouchbasePersistentProperty property = path.getLeafProperty();
116117

117-
return from(part, property, base.and(path.toDotPath()), iterator);
118+
return from(part, property, base.and(x(path.toDotPath())), iterator);
118119
}
119120

120121
@Override
@@ -139,10 +140,10 @@ private QueryCriteria from(final Part part, final CouchbasePersistentProperty pr
139140

140141
final Part.Type type = part.getType();
141142
switch (type) {
142-
case SIMPLE_PROPERTY:
143-
return criteria; // this will be the dummy from PartTree
144-
default:
145-
throw new IllegalArgumentException("Unsupported keyword!");
143+
case SIMPLE_PROPERTY:
144+
return criteria; // this will be the dummy from PartTree
145+
default:
146+
throw new IllegalArgumentException("Unsupported keyword!");
146147
}
147148
}
148149

0 commit comments

Comments
 (0)