|
32 | 32 | import org.springframework.data.couchbase.core.query.N1QLExpression; |
33 | 33 | import org.springframework.data.couchbase.repository.Query; |
34 | 34 | import org.springframework.data.couchbase.repository.query.support.N1qlUtils; |
| 35 | +import org.springframework.data.domain.Pageable; |
| 36 | +import org.springframework.data.domain.Sort; |
35 | 37 | import org.springframework.data.repository.query.Parameter; |
36 | 38 | import org.springframework.data.repository.query.ParameterAccessor; |
37 | 39 | import org.springframework.data.repository.query.QueryMethod; |
| 40 | +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; |
| 41 | +import org.springframework.data.repository.query.ReturnedType; |
38 | 42 | import org.springframework.expression.EvaluationContext; |
39 | 43 | import org.springframework.expression.common.TemplateParserContext; |
40 | 44 | import org.springframework.expression.spel.standard.SpelExpressionParser; |
41 | 45 |
|
42 | 46 | import com.couchbase.client.java.json.JsonArray; |
43 | 47 | import com.couchbase.client.java.json.JsonObject; |
44 | 48 | import com.couchbase.client.java.json.JsonValue; |
| 49 | +import org.springframework.util.Assert; |
45 | 50 |
|
46 | 51 | /** |
47 | 52 | * @author Subhashni Balakrishnan |
@@ -103,20 +108,23 @@ public class StringBasedN1qlQueryParser { |
103 | 108 | private static final Logger LOGGER = LoggerFactory.getLogger(StringBasedN1qlQueryParser.class); |
104 | 109 | private final String statement; |
105 | 110 | private final QueryMethod queryMethod; |
106 | | - private final PlaceholderType placeHolderType; |
| 111 | + private PlaceholderType placeHolderType; |
107 | 112 | private final N1qlSpelValues statementContext; |
108 | 113 | private final N1qlSpelValues countContext; |
109 | 114 | private final CouchbaseConverter couchbaseConverter; |
110 | 115 | private final Collection<String> parameterNames = new HashSet<String>(); |
| 116 | + public final N1QLExpression parsedExpression; |
111 | 117 |
|
112 | 118 | public StringBasedN1qlQueryParser(String statement, QueryMethod queryMethod, String bucketName, |
113 | | - CouchbaseConverter couchbaseConverter, String typeField, String typeValue) { |
| 119 | + CouchbaseConverter couchbaseConverter, String typeField, String typeValue, ParameterAccessor accessor, |
| 120 | + SpelExpressionParser parser, QueryMethodEvaluationContextProvider evaluationContextProvider) { |
114 | 121 | this.statement = statement; |
115 | 122 | this.queryMethod = queryMethod; |
116 | | - this.placeHolderType = checkPlaceholders(statement); |
117 | 123 | this.statementContext = createN1qlSpelValues(bucketName, typeField, typeValue, false); |
118 | 124 | this.countContext = createN1qlSpelValues(bucketName, typeField, typeValue, true); |
119 | 125 | this.couchbaseConverter = couchbaseConverter; |
| 126 | + this.parsedExpression = getExpression(accessor, getParameters(accessor), null, parser, evaluationContextProvider); |
| 127 | + checkPlaceholders( this.parsedExpression.toString() ); |
120 | 128 | } |
121 | 129 |
|
122 | 130 | public static N1qlSpelValues createN1qlSpelValues(String bucketName, String typeField, String typeValue, |
@@ -151,7 +159,7 @@ public String doParse(SpelExpressionParser parser, EvaluationContext evaluationC |
151 | 159 | return parsedExpression.getValue(evaluationContext, String.class); |
152 | 160 | } |
153 | 161 |
|
154 | | - private PlaceholderType checkPlaceholders(String statement) { |
| 162 | + private void checkPlaceholders(String statement) { |
155 | 163 |
|
156 | 164 | Matcher quoteMatcher = QUOTE_DETECTION_PATTERN.matcher(statement); |
157 | 165 | Matcher positionMatcher = POSITIONAL_PLACEHOLDER_PATTERN.matcher(statement); |
@@ -192,11 +200,11 @@ private PlaceholderType checkPlaceholders(String statement) { |
192 | 200 | } |
193 | 201 |
|
194 | 202 | if (posCount > 0) { |
195 | | - return PlaceholderType.POSITIONAL; |
| 203 | + placeHolderType = PlaceholderType.POSITIONAL; |
196 | 204 | } else if (namedCount > 0) { |
197 | | - return PlaceholderType.NAMED; |
| 205 | + placeHolderType = PlaceholderType.NAMED; |
198 | 206 | } else { |
199 | | - return PlaceholderType.NONE; |
| 207 | + placeHolderType = PlaceholderType.NONE; |
200 | 208 | } |
201 | 209 | } |
202 | 210 |
|
@@ -402,5 +410,39 @@ public N1qlSpelValues(String selectClause, String entityFields, String bucket, S |
402 | 410 | this.returning = returning; |
403 | 411 | } |
404 | 412 | } |
| 413 | + // copied from StringN1qlBasedQuery |
| 414 | + private N1QLExpression getExpression(ParameterAccessor accessor, Object[] runtimeParameters, |
| 415 | + ReturnedType returnedType, SpelExpressionParser parser, QueryMethodEvaluationContextProvider evaluationContextProvider) { |
| 416 | + boolean isCountQuery = queryMethod.getName().toLowerCase().startsWith("count"); // should be queryMethod.isCountQuery() |
| 417 | + EvaluationContext evaluationContext = evaluationContextProvider |
| 418 | + .getEvaluationContext(queryMethod.getParameters(), runtimeParameters); |
| 419 | + N1QLExpression parsedStatement = x(this.doParse(parser, evaluationContext, isCountQuery)); |
| 420 | + |
| 421 | + Sort sort = accessor.getSort(); |
| 422 | + if (sort.isSorted()) { |
| 423 | + N1QLExpression[] cbSorts = N1qlUtils.createSort(sort); |
| 424 | + parsedStatement = parsedStatement.orderBy(cbSorts); |
| 425 | + } |
| 426 | + if (queryMethod.isPageQuery()) { |
| 427 | + Pageable pageable = accessor.getPageable(); |
| 428 | + Assert.notNull(pageable, "Pageable must not be null!"); |
| 429 | + parsedStatement = parsedStatement.limit(pageable.getPageSize()).offset( |
| 430 | + Math.toIntExact(pageable.getOffset())); |
| 431 | + } else if (queryMethod.isSliceQuery()) { |
| 432 | + Pageable pageable = accessor.getPageable(); |
| 433 | + Assert.notNull(pageable, "Pageable must not be null!"); |
| 434 | + parsedStatement = parsedStatement.limit(pageable.getPageSize() + 1).offset( |
| 435 | + Math.toIntExact(pageable.getOffset())); |
| 436 | + } |
| 437 | + return parsedStatement; |
| 438 | + } |
405 | 439 |
|
| 440 | + // getExpression() could do this itself, but pass as an arg to be consistent with StringN1qlBasedQuery |
| 441 | + private static Object[] getParameters(ParameterAccessor accessor) { |
| 442 | + ArrayList<Object> params = new ArrayList<>(); |
| 443 | + for (Object o : accessor) { |
| 444 | + params.add(o); |
| 445 | + } |
| 446 | + return params.toArray(); |
| 447 | + } |
406 | 448 | } |
0 commit comments