Skip to content

DATACOUCH-616 - Fix parsing of String Query containing conditionals i… #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ private UpsertOptions buildUpsertOptions() {
} else if (durabilityLevel != DurabilityLevel.NONE) {
options.durability(durabilityLevel);
}
if (expiry != null && !expiry.isZero()) {
if (expiry != null && ! expiry.isZero()) {
options.expiry(expiry);
} else if (domainType.isAnnotationPresent(Document.class)) {
Document documentAnn = domainType.getAnnotation(Document.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import org.springframework.data.annotation.QueryAnnotation;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.repository.query.StringN1qlBasedQuery;

/**
* Annotation to support the use of N1QL queries with Couchbase.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,21 @@
import org.springframework.data.couchbase.core.query.N1QLExpression;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.couchbase.repository.query.support.N1qlUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReturnedType;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.json.JsonValue;
import org.springframework.util.Assert;

/**
* @author Subhashni Balakrishnan
Expand Down Expand Up @@ -103,20 +108,23 @@ public class StringBasedN1qlQueryParser {
private static final Logger LOGGER = LoggerFactory.getLogger(StringBasedN1qlQueryParser.class);
private final String statement;
private final QueryMethod queryMethod;
private final PlaceholderType placeHolderType;
private PlaceholderType placeHolderType;
private final N1qlSpelValues statementContext;
private final N1qlSpelValues countContext;
private final CouchbaseConverter couchbaseConverter;
private final Collection<String> parameterNames = new HashSet<String>();
public final N1QLExpression parsedExpression;

public StringBasedN1qlQueryParser(String statement, QueryMethod queryMethod, String bucketName,
CouchbaseConverter couchbaseConverter, String typeField, String typeValue) {
CouchbaseConverter couchbaseConverter, String typeField, String typeValue, ParameterAccessor accessor,
SpelExpressionParser parser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
this.statement = statement;
this.queryMethod = queryMethod;
this.placeHolderType = checkPlaceholders(statement);
this.statementContext = createN1qlSpelValues(bucketName, typeField, typeValue, false);
this.countContext = createN1qlSpelValues(bucketName, typeField, typeValue, true);
this.couchbaseConverter = couchbaseConverter;
this.parsedExpression = getExpression(accessor, getParameters(accessor), null, parser, evaluationContextProvider);
checkPlaceholders( this.parsedExpression.toString() );
}

public static N1qlSpelValues createN1qlSpelValues(String bucketName, String typeField, String typeValue,
Expand Down Expand Up @@ -151,7 +159,7 @@ public String doParse(SpelExpressionParser parser, EvaluationContext evaluationC
return parsedExpression.getValue(evaluationContext, String.class);
}

private PlaceholderType checkPlaceholders(String statement) {
private void checkPlaceholders(String statement) {

Matcher quoteMatcher = QUOTE_DETECTION_PATTERN.matcher(statement);
Matcher positionMatcher = POSITIONAL_PLACEHOLDER_PATTERN.matcher(statement);
Expand Down Expand Up @@ -192,11 +200,11 @@ private PlaceholderType checkPlaceholders(String statement) {
}

if (posCount > 0) {
return PlaceholderType.POSITIONAL;
placeHolderType = PlaceholderType.POSITIONAL;
} else if (namedCount > 0) {
return PlaceholderType.NAMED;
placeHolderType = PlaceholderType.NAMED;
} else {
return PlaceholderType.NONE;
placeHolderType = PlaceholderType.NONE;
}
}

Expand Down Expand Up @@ -402,5 +410,39 @@ public N1qlSpelValues(String selectClause, String entityFields, String bucket, S
this.returning = returning;
}
}
// copied from StringN1qlBasedQuery
private N1QLExpression getExpression(ParameterAccessor accessor, Object[] runtimeParameters,
ReturnedType returnedType, SpelExpressionParser parser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
boolean isCountQuery = queryMethod.getName().toLowerCase().startsWith("count"); // should be queryMethod.isCountQuery()
EvaluationContext evaluationContext = evaluationContextProvider
.getEvaluationContext(queryMethod.getParameters(), runtimeParameters);
N1QLExpression parsedStatement = x(this.doParse(parser, evaluationContext, isCountQuery));

Sort sort = accessor.getSort();
if (sort.isSorted()) {
N1QLExpression[] cbSorts = N1qlUtils.createSort(sort);
parsedStatement = parsedStatement.orderBy(cbSorts);
}
if (queryMethod.isPageQuery()) {
Pageable pageable = accessor.getPageable();
Assert.notNull(pageable, "Pageable must not be null!");
parsedStatement = parsedStatement.limit(pageable.getPageSize()).offset(
Math.toIntExact(pageable.getOffset()));
} else if (queryMethod.isSliceQuery()) {
Pageable pageable = accessor.getPageable();
Assert.notNull(pageable, "Pageable must not be null!");
parsedStatement = parsedStatement.limit(pageable.getPageSize() + 1).offset(
Math.toIntExact(pageable.getOffset()));
}
return parsedStatement;
}

// getExpression() could do this itself, but pass as an arg to be consistent with StringN1qlBasedQuery
private static Object[] getParameters(ParameterAccessor accessor) {
ArrayList<Object> params = new ArrayList<>();
for (Object o : accessor) {
params.add(o);
}
return params.toArray();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class StringN1qlQueryCreator extends AbstractQueryCreator<Query, QueryCri
private final QueryMethod queryMethod;
private final CouchbaseConverter couchbaseConverter;
private static final SpelExpressionParser SPEL_PARSER = new SpelExpressionParser();
private final N1QLExpression parsedExpression;

public StringN1qlQueryCreator(final ParameterAccessor accessor, CouchbaseQueryMethod queryMethod,
CouchbaseConverter couchbaseConverter, String bucketName,
Expand All @@ -83,8 +84,9 @@ public StringN1qlQueryCreator(final ParameterAccessor accessor, CouchbaseQueryMe
throw new IllegalArgumentException("query has no inline Query or named Query not found");
}
this.queryParser = new StringBasedN1qlQueryParser(queryString, queryMethod, bucketName, couchbaseConverter,
getTypeField(), getTypeValue());
getTypeField(), getTypeValue(), accessor, SPEL_PARSER, evaluationContextProvider);
this.parser = SPEL_PARSER;
this.parsedExpression = this.queryParser.parsedExpression;
}

protected QueryMethod getQueryMethod() {
Expand Down Expand Up @@ -127,7 +129,6 @@ protected QueryCriteria or(QueryCriteria base, QueryCriteria criteria) {

@Override
protected Query complete(QueryCriteria criteria, Sort sort) {
N1QLExpression parsedExpression = getExpression(accessor, getParameters(accessor), null /* returnedType */);
Query q = new StringQuery(parsedExpression.toString()).with(sort);
JsonValue params = queryParser.getPlaceholderValues(accessor);
if (params instanceof JsonArray) {
Expand All @@ -150,40 +151,4 @@ private QueryCriteria from(final Part part, final CouchbasePersistentProperty pr
}
}

// copied from StringN1qlBasedQuery
private N1QLExpression getExpression(ParameterAccessor accessor, Object[] runtimeParameters,
ReturnedType returnedType) {
EvaluationContext evaluationContext = evaluationContextProvider.getEvaluationContext(
getQueryMethod().getParameters(), runtimeParameters);
N1QLExpression parsedStatement = x(this.queryParser.doParse(parser, evaluationContext, false));

Sort sort = accessor.getSort();
if (sort.isSorted()) {
N1QLExpression[] cbSorts = N1qlUtils.createSort(sort);
parsedStatement = parsedStatement.orderBy(cbSorts);
}
if (queryMethod.isPageQuery()) {
Pageable pageable = accessor.getPageable();
Assert.notNull(pageable, "Pageable must not be null!");
parsedStatement = parsedStatement.limit(pageable.getPageSize()).offset(
Math.toIntExact(pageable.getOffset()));
} else if (queryMethod.isSliceQuery()) {
Pageable pageable = accessor.getPageable();
Assert.notNull(pageable, "Pageable must not be null!");
parsedStatement = parsedStatement.limit(pageable.getPageSize() + 1).offset(
Math.toIntExact(pageable.getOffset()));
}
return parsedStatement;
}

// getExpression() could do this itself, but pass as an arg to be consistent with StringN1qlBasedQuery
private static Object[] getParameters(ParameterAccessor accessor) {
ArrayList<Object> params = new ArrayList<>();
for (Object o : accessor) {
params.add(o);
}
return params.toArray();
}
}


Loading