diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java index 86442c12a..884cc0b42 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java @@ -22,6 +22,7 @@ import java.util.Collection; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; +import org.springframework.data.couchbase.core.mapping.Document; import org.springframework.util.Assert; import com.couchbase.client.core.msg.kv.DurabilityLevel; diff --git a/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java b/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java index df4f6d3a6..07242522a 100644 --- a/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java +++ b/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * Copyright 2012-2021 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,13 +27,16 @@ import org.springframework.data.couchbase.core.convert.CouchbaseConverter; import org.springframework.lang.Nullable; +import static org.springframework.data.couchbase.core.query.N1QLExpression.*; + /** * @author Michael Nitschinger * @author Michael Reiche + * @author Mauro Monti */ public class QueryCriteria implements QueryCriteriaDefinition { - private final String key; + private final N1QLExpression key; /** * Holds the chain itself, the current operator being always the last one. */ @@ -46,15 +49,15 @@ public class QueryCriteria implements QueryCriteriaDefinition { private Object[] value; private String format; - QueryCriteria(List chain, String key, Object[] value, ChainOperator chainOperator) { + QueryCriteria(List chain, N1QLExpression key, Object[] value, ChainOperator chainOperator) { this(chain, key, value, chainOperator, null, null); } - QueryCriteria(List chain, String key, Object value, ChainOperator chainOperator) { + QueryCriteria(List chain, N1QLExpression key, Object value, ChainOperator chainOperator) { this(chain, key, new Object[] { value }, chainOperator, null, null); } - QueryCriteria(List chain, String key, Object[] value, ChainOperator chainOperator, String operator, + QueryCriteria(List chain, N1QLExpression key, Object[] value, ChainOperator chainOperator, String operator, String format) { this.criteriaChain = chain; criteriaChain.add(this); @@ -70,9 +73,16 @@ Object[] getValue() { } /** - * Static factory method to create a Criteria using the provided key. + * Static factory method to create a Criteria using the provided String key. */ public static QueryCriteria where(String key) { + return where(x(key)); + } + + /** + * Static factory method to create a Criteria using the provided N1QLExpression key. + */ + public static QueryCriteria where(N1QLExpression key) { return new QueryCriteria(new ArrayList<>(), key, null, null); } @@ -83,6 +93,10 @@ private static QueryCriteria wrap(QueryCriteria criteria) { } public QueryCriteria and(String key) { + return and(x(key)); + } + + public QueryCriteria and(N1QLExpression key) { return new QueryCriteria(this.criteriaChain, key, null, ChainOperator.AND); } @@ -90,14 +104,18 @@ public QueryCriteria and(QueryCriteria criteria) { return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.AND); } - public QueryCriteria or(QueryCriteria criteria) { - return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.OR); + public QueryCriteria or(String key) { + return or(x(key)); } - public QueryCriteria or(String key) { + public QueryCriteria or(N1QLExpression key) { return new QueryCriteria(this.criteriaChain, key, null, ChainOperator.OR); } + public QueryCriteria or(QueryCriteria criteria) { + return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.OR); + } + public QueryCriteria eq(@Nullable Object o) { return is(o); } @@ -343,7 +361,7 @@ public String export() { // used only by tests */ private StringBuilder exportSingle(StringBuilder sb, int[] paramIndexPtr, JsonValue parameters, CouchbaseConverter converter) { - String fieldName = maybeBackTic(key); + String fieldName = key == null ? null : key.toString(); // maybeBackTic(key); int valueLen = value == null ? 0 : value.length; Object[] v = new Object[valueLen + 2]; v[0] = fieldName; @@ -377,7 +395,7 @@ private StringBuilder exportSingle(StringBuilder sb, int[] paramIndexPtr, JsonVa * @param parameters - parameters of the query. If operands are parameterized, their values are added to parameters * @return string containing part of N1QL query */ - private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, JsonValue parameters, + private String maybeWrapValue(N1QLExpression key, Object value, int[] paramIndexPtr, JsonValue parameters, CouchbaseConverter converter) { if (paramIndexPtr != null) { if (paramIndexPtr[0] >= 0) { @@ -397,10 +415,10 @@ private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, Jso JsonObject params = (JsonObject) parameters; // from StringBasedN1qlQueryParser.getNamedPlaceholderValues() try { - params.put(key, convert(converter, value)); + params.put(key.toString(), convert(converter, value)); } catch (InvalidArgumentException iae) { if (value instanceof Object[]) { - params.put(key, JsonArray.from((Object[]) value)); + params.put(key.toString(), JsonArray.from((Object[]) value)); } else { throw iae; } @@ -416,7 +434,7 @@ private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, Jso } else if (value == null) { return "null"; } else if (value instanceof Object[]) { // convert array into sequence of comma-separated values - StringBuffer l = new StringBuffer(); + StringBuilder l = new StringBuilder(); l.append("["); Object[] array = (Object[]) value; for (int i = 0; i < array.length; i++) { diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQuery.java deleted file mode 100644 index c7fd87bd4..000000000 --- a/src/main/java/org/springframework/data/couchbase/repository/query/CouchbaseRepositoryQuery.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository.query; - -import org.springframework.data.couchbase.core.CouchbaseOperations; -import org.springframework.data.repository.core.NamedQueries; -import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.RepositoryQuery; - -/** - * @author Michael Nitschinger - * @author Michael Reiche - * @deprecated - */ -@Deprecated -public class CouchbaseRepositoryQuery implements RepositoryQuery { - - private final CouchbaseOperations operations; - private final CouchbaseQueryMethod queryMethod; - private final NamedQueries namedQueries; - private final QueryMethodEvaluationContextProvider evaluationContextProvider; - - public CouchbaseRepositoryQuery(final CouchbaseOperations operations, final CouchbaseQueryMethod queryMethod, - final NamedQueries namedQueries, final QueryMethodEvaluationContextProvider evaluationContextProvider) { - this.operations = operations; - this.queryMethod = queryMethod; - this.namedQueries = namedQueries; - this.evaluationContextProvider = evaluationContextProvider; - throw new RuntimeException("Deprecated"); - } - - @Override - public Object execute(final Object[] parameters) { - return new N1qlRepositoryQueryExecutor(operations, queryMethod, namedQueries, evaluationContextProvider) - .execute(parameters); - } - - @Override - public QueryMethod getQueryMethod() { - return queryMethod; - } - -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/N1qlCountQueryCreator.java b/src/main/java/org/springframework/data/couchbase/repository/query/N1qlCountQueryCreator.java index d78bd3406..04fd39093 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/N1qlCountQueryCreator.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/N1qlCountQueryCreator.java @@ -118,6 +118,11 @@ public Pageable first() { return delegate.first(); } + @Override + public Pageable withPage(int i) { + return new CountPageable(delegate.withPage(i)); + } + public boolean hasPrevious() { return delegate.hasPrevious(); } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java b/src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java index be4b21375..7c05282e9 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * Copyright 2012-2021 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,15 @@ package org.springframework.data.couchbase.repository.query; import static org.springframework.data.couchbase.core.query.QueryCriteria.where; +import static org.springframework.data.couchbase.core.query.N1QLExpression.*; +import static org.springframework.data.couchbase.core.query.QueryCriteria.*; import java.util.Iterator; import org.springframework.core.convert.converter.Converter; import org.springframework.data.couchbase.core.convert.CouchbaseConverter; import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; +import org.springframework.data.couchbase.core.query.N1QLExpression; import org.springframework.data.couchbase.core.query.Query; import org.springframework.data.couchbase.core.query.QueryCriteria; import org.springframework.data.domain.Sort; @@ -36,32 +39,38 @@ /** * @author Michael Nitschinger * @author Michael Reiche + * @author Mauro Monti */ public class N1qlQueryCreator extends AbstractQueryCreator { + private static final String META_ID_PROPERTY = "id"; + private static final String META_CAS_PROPERTY = "cas"; + private final ParameterAccessor accessor; private final MappingContext context; private final QueryMethod queryMethod; private final CouchbaseConverter converter; + private final String bucketName; - public N1qlQueryCreator(final PartTree tree, final ParameterAccessor accessor, QueryMethod queryMethod, - CouchbaseConverter converter) { + public N1qlQueryCreator(final PartTree tree, final ParameterAccessor accessor, final QueryMethod queryMethod, + final CouchbaseConverter converter, final String bucketName) { super(tree, accessor); this.accessor = accessor; this.context = converter.getMappingContext(); this.queryMethod = queryMethod; this.converter = converter; + this.bucketName = bucketName; } @Override protected QueryCriteria create(final Part part, final Iterator iterator) { PersistentPropertyPath path = context.getPersistentPropertyPath(part.getProperty()); CouchbasePersistentProperty property = path.getLeafProperty(); - return from(part, property, where(path.toDotPath(cvtr)), iterator); + return from(part, property, where(addMetaIfRequired(path, property)), iterator); } static Converter cvtr = ( - source) -> new StringBuilder(source.getName().length() + 2).append('`').append(source.getName()).append('`') + source) -> new StringBuilder(source.getFieldName().length() + 2).append('`').append(source.getFieldName()).append('`') .toString(); @Override @@ -73,7 +82,7 @@ protected QueryCriteria and(final Part part, final QueryCriteria base, final Ite PersistentPropertyPath path = context.getPersistentPropertyPath(part.getProperty()); CouchbasePersistentProperty property = path.getLeafProperty(); - return from(part, property, base.and(path.toDotPath()), iterator); + return from(part, property, base.and(addMetaIfRequired(path, property)), iterator); } @Override @@ -149,4 +158,15 @@ private QueryCriteria from(final Part part, final CouchbasePersistentProperty pr } } + private N1QLExpression addMetaIfRequired(final PersistentPropertyPath persistentPropertyPath, + final CouchbasePersistentProperty property) { + if (property.isIdProperty()) { + return path(meta(i(bucketName)), i(META_ID_PROPERTY)); + } + if (property.isVersionProperty()) { + return path(meta(i(bucketName)), i(META_CAS_PROPERTY)); + } + return x(persistentPropertyPath.toDotPath(cvtr)); + } + } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/N1qlRepositoryQueryExecutor.java b/src/main/java/org/springframework/data/couchbase/repository/query/N1qlRepositoryQueryExecutor.java deleted file mode 100644 index b1400bbb8..000000000 --- a/src/main/java/org/springframework/data/couchbase/repository/query/N1qlRepositoryQueryExecutor.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository.query; - -import org.springframework.data.couchbase.core.CouchbaseOperations; -import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation; -import org.springframework.data.couchbase.core.query.Query; -import org.springframework.data.domain.Pageable; -import org.springframework.data.repository.core.NamedQueries; -import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.data.repository.query.ParametersParameterAccessor; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.parser.PartTree; -import org.springframework.expression.spel.standard.SpelExpressionParser; - -import com.couchbase.client.java.query.QueryScanConsistency; - -/** - * @author Michael Nitschinger - * @author Michael Reiche - * @deprecated - */ -@Deprecated -public class N1qlRepositoryQueryExecutor { - - private final CouchbaseOperations operations; - private final CouchbaseQueryMethod queryMethod; - private final NamedQueries namedQueries; - private final QueryMethodEvaluationContextProvider evaluationContextProvider; - - public N1qlRepositoryQueryExecutor(final CouchbaseOperations operations, final CouchbaseQueryMethod queryMethod, - final NamedQueries namedQueries, final QueryMethodEvaluationContextProvider evaluationContextProvider) { - this.operations = operations; - this.queryMethod = queryMethod; - this.namedQueries = namedQueries; - this.evaluationContextProvider = evaluationContextProvider; - throw new RuntimeException("Deprecated"); - } - - private static final SpelExpressionParser SPEL_PARSER = new SpelExpressionParser(); - - /** - * see also {@link ReactiveN1qlRepositoryQueryExecutor#execute(Object[] parameters) execute } - * - * @param parameters - * @return - */ - public Object execute(final Object[] parameters) { - final Class domainClass = queryMethod.getResultProcessor().getReturnedType().getDomainType(); - final ParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters); - - // counterpart to ReactiveN1qlRespositoryQueryExecutor, - Query query; - ExecutableFindByQueryOperation.ExecutableFindByQuery q; - if (queryMethod.hasN1qlAnnotation()) { - query = new StringN1qlQueryCreator(accessor, queryMethod, operations.getConverter(), operations.getBucketName(), - SPEL_PARSER, evaluationContextProvider, namedQueries).createQuery(); - } else { - final PartTree tree = new PartTree(queryMethod.getName(), domainClass); - query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter()).createQuery(); - } - - ExecutableFindByQueryOperation.ExecutableFindByQuery operation = (ExecutableFindByQueryOperation.ExecutableFindByQuery) operations - .findByQuery(domainClass).withConsistency(buildQueryScanConsistency()); - if (queryMethod.isCountQuery()) { - return operation.matching(query).count(); - } else if (queryMethod.isCollectionQuery()) { - return operation.matching(query).all(); - } else if (queryMethod.isPageQuery()) { - Pageable p = accessor.getPageable(); - return new CouchbaseQueryExecution.PagedExecution(operation, p).execute(query, null, null); - } else { - return operation.matching(query).oneValue(); - } - - } - - private QueryScanConsistency buildQueryScanConsistency() { - QueryScanConsistency scanConsistency = QueryScanConsistency.NOT_BOUNDED; - if (queryMethod.hasConsistencyAnnotation()) { - scanConsistency = queryMethod.getConsistencyAnnotation().value(); - } else if (queryMethod.hasScanConsistencyAnnotation()) { - scanConsistency = queryMethod.getScanConsistencyAnnotation().query(); - } - return scanConsistency; - } - -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/PartTreeCouchbaseQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/PartTreeCouchbaseQuery.java index 006a4cfa1..8e6d8fd2e 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/PartTreeCouchbaseQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/PartTreeCouchbaseQuery.java @@ -72,7 +72,7 @@ public PartTree getTree() { @Override protected Query createQuery(ParametersParameterAccessor accessor) { - N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter); + N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter, getOperations().getBucketName()); Query query = creator.createQuery(); if (tree.isLimiting()) { @@ -88,7 +88,7 @@ protected Query createQuery(ParametersParameterAccessor accessor) { */ @Override protected Query createCountQuery(ParametersParameterAccessor accessor) { - return new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter).createQuery(); + return new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter, getOperations().getBucketName()).createQuery(); } /* diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQuery.java deleted file mode 100644 index 5e87ab56e..000000000 --- a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveCouchbaseRepositoryQuery.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository.query; - -import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; -import org.springframework.data.couchbase.core.query.Query; -import org.springframework.data.repository.core.NamedQueries; -import org.springframework.data.repository.query.ParametersParameterAccessor; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; - -/** - * @author Michael Nitschinger - * @author Michael Reiche - * @deprecated - */ -@Deprecated -public class ReactiveCouchbaseRepositoryQuery extends AbstractReactiveCouchbaseQuery { - - private final ReactiveCouchbaseOperations operations; - private final ReactiveCouchbaseQueryMethod queryMethod; - private final NamedQueries namedQueries; - private final QueryMethodEvaluationContextProvider evaluationContextProvider; - - public ReactiveCouchbaseRepositoryQuery(final ReactiveCouchbaseOperations operations, - final ReactiveCouchbaseQueryMethod queryMethod, final NamedQueries namedQueries, - final QueryMethodEvaluationContextProvider evaluationContextProvider) { - super(queryMethod, operations, new SpelExpressionParser(), evaluationContextProvider); - this.operations = operations; - this.queryMethod = queryMethod; - this.namedQueries = namedQueries; - this.evaluationContextProvider = evaluationContextProvider; - throw new RuntimeException("Deprecated"); - } - - @Override - public Object execute(final Object[] parameters) { - return new ReactiveN1qlRepositoryQueryExecutor(operations, queryMethod, namedQueries, evaluationContextProvider) - .execute(parameters); - } - - @Override - protected Query createCountQuery(ParametersParameterAccessor accessor) { - return null; - } - - @Override - protected Query createQuery(ParametersParameterAccessor accessor) { - return null; - } - - @Override - protected boolean isLimiting() { - // TODO - return false; - } - - @Override - public ReactiveCouchbaseQueryMethod getQueryMethod() { - return queryMethod; - } - -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveN1qlRepositoryQueryExecutor.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveN1qlRepositoryQueryExecutor.java deleted file mode 100644 index 084f1684d..000000000 --- a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveN1qlRepositoryQueryExecutor.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2012-2020 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository.query; - -import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; -import org.springframework.data.repository.core.NamedQueries; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.expression.spel.standard.SpelExpressionParser; - -/** - * @author Michael Nitschinger - * @author Michael Reiche - * @deprecated - */ -@Deprecated -public class ReactiveN1qlRepositoryQueryExecutor { - - private final ReactiveCouchbaseOperations operations; - private final ReactiveCouchbaseQueryMethod queryMethod; - private final NamedQueries namedQueries; - private final QueryMethodEvaluationContextProvider evaluationContextProvider; - - public ReactiveN1qlRepositoryQueryExecutor(final ReactiveCouchbaseOperations operations, - final ReactiveCouchbaseQueryMethod queryMethod, final NamedQueries namedQueries, - QueryMethodEvaluationContextProvider evaluationContextProvider) { - this.operations = operations; - this.queryMethod = queryMethod; - this.namedQueries = namedQueries; - this.evaluationContextProvider = evaluationContextProvider; - throw new RuntimeException("Deprecated"); - } - - /** - * see also {@link N1qlRepositoryQueryExecutor#execute(Object[] parameters) execute } - * - * @param parameters - * @return - */ - public Object execute(final Object[] parameters) { - // counterpart to N1qlRespositoryQueryExecutor, - - if (queryMethod.hasN1qlAnnotation()) { - return new ReactiveStringBasedCouchbaseQuery(queryMethod, operations, new SpelExpressionParser(), - evaluationContextProvider, namedQueries).execute(parameters); - } else { - return new ReactivePartTreeCouchbaseQuery(queryMethod, operations, new SpelExpressionParser(), - evaluationContextProvider).execute(parameters); - } - - } - -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactivePartTreeCouchbaseQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactivePartTreeCouchbaseQuery.java index fd60ad402..b787bd1ce 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/ReactivePartTreeCouchbaseQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/ReactivePartTreeCouchbaseQuery.java @@ -73,7 +73,7 @@ public PartTree getTree() { @Override protected Query createQuery(ParametersParameterAccessor accessor) { - N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter); + N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter, getOperations().getBucketName()); Query query = creator.createQuery(); if (tree.isLimiting()) { @@ -91,7 +91,7 @@ protected Query createQuery(ParametersParameterAccessor accessor) { */ @Override protected Query createCountQuery(ParametersParameterAccessor accessor) { - Query query = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter).createQuery(); + Query query = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter, getOperations().getBucketName()).createQuery(); if (LOG.isDebugEnabled()) { LOG.debug("Created query {} for * fields.", query.export()); } diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactivePartTreeN1qlBasedQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactivePartTreeN1qlBasedQuery.java deleted file mode 100644 index a452eaec6..000000000 --- a/src/main/java/org/springframework/data/couchbase/repository/query/ReactivePartTreeN1qlBasedQuery.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2017-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.repository.query; - -import static org.springframework.data.couchbase.core.query.N1QLExpression.count; -import static org.springframework.data.couchbase.core.query.N1QLExpression.select; -import static org.springframework.data.couchbase.core.query.N1QLExpression.x; - -import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; -import org.springframework.data.couchbase.core.query.N1QLExpression; -import org.springframework.data.couchbase.repository.query.support.N1qlUtils; -import org.springframework.data.repository.query.ParameterAccessor; -import org.springframework.data.repository.query.RepositoryQuery; -import org.springframework.data.repository.query.ReturnedType; -import org.springframework.data.repository.query.parser.PartTree; - -import com.couchbase.client.java.json.JsonValue; - -/** - * A reactive {@link RepositoryQuery} for Couchbase, based on query derivation - * - * @author Subhashni Balakrishnan - * @author Michael Reiche - * @since 3.0 - * @deprecated - */ -@Deprecated -public class ReactivePartTreeN1qlBasedQuery extends ReactiveAbstractN1qlBasedQuery { - - private final PartTree partTree; - private JsonValue placeHolderValues; - - public ReactivePartTreeN1qlBasedQuery(CouchbaseQueryMethod queryMethod, ReactiveCouchbaseOperations operations) { - super(queryMethod, operations); - this.partTree = new PartTree(queryMethod.getName(), queryMethod.getEntityInformation().getJavaType()); - throw new RuntimeException("deprecated"); - } - - @Override - protected JsonValue getPlaceholderValues(ParameterAccessor accessor) { - return this.placeHolderValues; - } - - @Override - protected N1QLExpression getExpression(ParameterAccessor accessor, Object[] runtimeParameters, - ReturnedType returnedType) { - String bucketName = getCouchbaseOperations().getBucketName(); - N1QLExpression bucket = N1qlUtils.escapedBucket(bucketName); - - N1QLExpression select; - if (partTree.isCountProjection()) { - select = select(count(x("*"))); - } else { - select = N1qlUtils.createSelectClauseForEntity(bucketName, returnedType, - this.getCouchbaseOperations().getConverter()); - } - N1QLExpression selectFrom = select.from(bucket); - - OldN1qlQueryCreator queryCreator = new OldN1qlQueryCreator(partTree, accessor, selectFrom, - getCouchbaseOperations().getConverter(), getQueryMethod()); - N1QLExpression selectFromWhereOrderBy = queryCreator.createQuery(); - this.placeHolderValues = queryCreator.getPlaceHolderValues(); - if (partTree.isLimiting()) { - return selectFromWhereOrderBy.limit(partTree.getMaxResults()); - } else { - return selectFromWhereOrderBy; - } - } -} diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java index 9626e4644..3296397a8 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/ReactiveStringBasedCouchbaseQuery.java @@ -79,6 +79,7 @@ protected Query createQuery(ParametersParameterAccessor accessor) { StringN1qlQueryCreator creator = new StringN1qlQueryCreator(accessor, getQueryMethod(), getOperations().getConverter(), getOperations().getBucketName(), expressionParser, evaluationContextProvider, namedQueries); + Query query = creator.createQuery(); if (LOG.isDebugEnabled()) { diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreator.java b/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreator.java index d19f185f9..63aa27f5d 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreator.java +++ b/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlQueryCreator.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 the original author or authors + * Copyright 2021 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,11 @@ */ package org.springframework.data.couchbase.repository.query; +import static org.springframework.data.couchbase.core.query.N1QLExpression.x; +import static org.springframework.data.couchbase.core.query.QueryCriteria.*; + +import java.util.Iterator; + import com.couchbase.client.java.json.JsonArray; import com.couchbase.client.java.json.JsonObject; import com.couchbase.client.java.json.JsonValue; @@ -28,9 +33,7 @@ import org.springframework.data.mapping.PersistentPropertyPath; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.core.NamedQueries; -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.*; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.PartTree; @@ -42,6 +45,7 @@ /** * @author Michael Reiche + * @author Mauro Monti */ public class StringN1qlQueryCreator extends AbstractQueryCreator { @@ -52,10 +56,12 @@ public class StringN1qlQueryCreator extends AbstractQueryCreator iterator) PersistentPropertyPath path = context.getPersistentPropertyPath( part.getProperty()); CouchbasePersistentProperty property = path.getLeafProperty(); - return from(part, property, where(path.toDotPath()), iterator); + return from(part, property, where(x(path.toDotPath())), iterator); } @Override @@ -114,7 +120,7 @@ protected QueryCriteria and(final Part part, final QueryCriteria base, final Ite part.getProperty()); CouchbasePersistentProperty property = path.getLeafProperty(); - return from(part, property, base.and(path.toDotPath()), iterator); + return from(part, property, base.and(x(path.toDotPath())), iterator); } @Override diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java index 9d4940d8a..626916e44 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java @@ -23,6 +23,8 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE; +import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -31,9 +33,14 @@ import java.util.Set; import java.util.UUID; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.DuplicateKeyException; import org.springframework.data.couchbase.core.ExecutableRemoveByIdOperation.ExecutableRemoveById; import org.springframework.data.couchbase.core.ExecutableReplaceByIdOperation.ExecutableReplaceById; diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java index 30a0a96e1..25c3b4d29 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors + * Copyright 2012-2021 the original author or authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE; import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE; +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.data.couchbase.config.BeanNames.*; +import static org.springframework.data.couchbase.core.query.N1QLExpression.*; import java.time.Instant; import java.time.temporal.TemporalAccessor; @@ -60,6 +63,7 @@ * @author Michael Nitschinger * @author Michael Reiche * @author Haris Alesevic + * @author Mauro Monti */ @IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED) class CouchbaseTemplateQueryIntegrationTests extends JavaIntegrationTests { @@ -124,7 +128,7 @@ void findByMatchingQuery() { couchbaseTemplate.upsertById(User.class).all(Arrays.asList(user1, user2, specialUser)); - Query specialUsers = new Query(QueryCriteria.where("firstname").like("special")); + Query specialUsers = new Query(QueryCriteria.where(i("firstname")).like("special")); final List foundUsers = couchbaseTemplate.findByQuery(User.class) .withConsistency(QueryScanConsistency.REQUEST_PLUS).matching(specialUsers).all(); @@ -206,7 +210,7 @@ void removeByMatchingQuery() { assertTrue(couchbaseTemplate.existsById().one(user2.getId())); assertTrue(couchbaseTemplate.existsById().one(specialUser.getId())); - Query nonSpecialUsers = new Query(QueryCriteria.where("firstname").notLike("special")); + Query nonSpecialUsers = new Query(QueryCriteria.where(i("firstname")).notLike("special")); couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS) .matching(nonSpecialUsers).all(); diff --git a/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java b/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java index 0dcbd991c..185f4892e 100644 --- a/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 the original author or authors. + * Copyright 2017-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,56 +17,51 @@ package org.springframework.data.couchbase.core.query; import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.data.couchbase.core.query.N1QLExpression.*; import static org.springframework.data.couchbase.core.query.QueryCriteria.*; +import static org.springframework.data.couchbase.repository.query.support.N1qlUtils.*; import com.couchbase.client.java.json.JsonArray; -import com.couchbase.client.java.json.JsonObject; import org.junit.jupiter.api.Test; -import org.springframework.data.couchbase.domain.User; -import org.springframework.data.couchbase.domain.UserRepository; -import org.springframework.data.couchbase.repository.query.N1qlQueryCreator; -import org.springframework.data.repository.query.parser.PartTree; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; +/** + * @author Mauro Monti + */ class QueryCriteriaTests { @Test void testSimpleCriteria() { - QueryCriteria c = where("name").is("Bubba"); + QueryCriteria c = where(i("name")).is("Bubba"); assertEquals("`name` = \"Bubba\"", c.export()); } @Test public void testNullValue() { - QueryCriteria c = where("name").is(null); + QueryCriteria c = where(i("name")).is(null); assertEquals("`name` = null", c.export()); } @Test void testSimpleNumber() { - QueryCriteria c = where("name").is(5); + QueryCriteria c = where(i("name")).is(5); assertEquals("`name` = 5", c.export()); } @Test void testNotEqualCriteria() { - QueryCriteria c = where("name").ne("Bubba"); + QueryCriteria c = where(i("name")).ne("Bubba"); assertEquals("`name` != \"Bubba\"", c.export()); } @Test void testChainedCriteria() { - QueryCriteria c = where("name").is("Bubba").and("age").lt(21).or("country").is("Austria"); + QueryCriteria c = where(i("name")).is("Bubba").and(i("age")).lt(21).or(i("country")).is("Austria"); assertEquals("`name` = \"Bubba\" and `age` < 21 or `country` = \"Austria\"", c.export()); } @Test void testNestedAndCriteria() { - QueryCriteria c = where("name").is("Bubba").and(where("age").gt(12).or("country").is("Austria")); + QueryCriteria c = where(i("name")).is("Bubba").and(where(i("age")).gt(12).or(i("country")).is("Austria")); JsonArray parameters = JsonArray.create(); assertEquals("`name` = $1 and (`age` > $2 or `country` = $3)", c.export(new int[1], parameters, null)); assertEquals("[\"Bubba\",12,\"Austria\"]", parameters.toString()); @@ -74,7 +69,7 @@ void testNestedAndCriteria() { @Test void testNestedOrCriteria() { - QueryCriteria c = where("name").is("Bubba").or(where("age").gt(12).or("country").is("Austria")); + QueryCriteria c = where(i("name")).is("Bubba").or(where(i("age")).gt(12).or(i("country")).is("Austria")); JsonArray parameters = JsonArray.create(); assertEquals("`name` = $1 or (`age` > $2 or `country` = $3)", c.export(new int[1], parameters, null)); assertEquals("[\"Bubba\",12,\"Austria\"]", parameters.toString()); @@ -82,45 +77,45 @@ void testNestedOrCriteria() { @Test void testNestedNotIn() { - QueryCriteria c = where("name").is("Bubba").or(where("age").gt(12).or("country").is("Austria")) - .and(where("state").notIn(new String[] { "Alabama", "Florida" })); + QueryCriteria c = where(i("name")).is("Bubba").or(where(i("age")).gt(12).or(i("country")).is("Austria")) + .and(where(i("state")).notIn(new String[] { "Alabama", "Florida" })); assertEquals("`name` = \"Bubba\" or (`age` > 12 or `country` = \"Austria\") and " + "(not( (`state` in ( [\"Alabama\",\"Florida\"] )) ))", c.export()); } @Test void testLt() { - QueryCriteria c = where("name").lt("Couch"); + QueryCriteria c = where(i("name")).lt("Couch"); assertEquals("`name` < \"Couch\"", c.export()); } @Test void testLte() { - QueryCriteria c = where("name").lte("Couch"); + QueryCriteria c = where(i("name")).lte("Couch"); assertEquals("`name` <= \"Couch\"", c.export()); } @Test void testGt() { - QueryCriteria c = where("name").gt("Couch"); + QueryCriteria c = where(i("name")).gt("Couch"); assertEquals("`name` > \"Couch\"", c.export()); } @Test void testGte() { - QueryCriteria c = where("name").gte("Couch"); + QueryCriteria c = where(i("name")).gte("Couch"); assertEquals("`name` >= \"Couch\"", c.export()); } @Test void testNe() { - QueryCriteria c = where("name").ne("Couch"); + QueryCriteria c = where(i("name")).ne("Couch"); assertEquals("`name` != \"Couch\"", c.export()); } @Test void testStartingWith() { - QueryCriteria c = where("name").startingWith("Cou"); + QueryCriteria c = where(i("name")).startingWith("Cou"); assertEquals("`name` like (\"Cou\"||\"%\")", c.export()); } @@ -128,99 +123,99 @@ void testStartingWith() { * startingWith() cannot be a QueryCriteria @Test void testStartingWithExpr() { - QueryCriteria c = where("name").startingWith(where("name").plus("xxx")); + QueryCriteria c = where(i("name")).startingWith(where(i("name")).plus("xxx")); assertEquals("`name` like (((`name` || "xxx") || ""%""))", c.export()); } */ @Test void testEndingWith() { - QueryCriteria c = where("name").endingWith("ouch"); + QueryCriteria c = where(i("name")).endingWith("ouch"); assertEquals("`name` like (\"%\"||\"ouch\")", c.export()); } @Test void testEndingWithExpr() { - QueryCriteria c = where("name").endingWith(where("name").plus("xxx")); + QueryCriteria c = where(i("name")).endingWith(where(i("name")).plus("xxx")); assertEquals("`name` like (\"%\"||((`name` || \"xxx\")))", c.export()); } @Test void testRegex() { - QueryCriteria c = where("name").regex("C.*h"); + QueryCriteria c = where(i("name")).regex("C.*h"); assertEquals("regexp_like(`name`, \"C.*h\")", c.export()); } @Test void testContaining() { - QueryCriteria c = where("name").containing("ouch"); + QueryCriteria c = where(i("name")).containing("ouch"); assertEquals("contains(`name`, \"ouch\")", c.export()); } @Test void testNotContaining() { - QueryCriteria c = where("name").notContaining("Elvis"); + QueryCriteria c = where(i("name")).notContaining("Elvis"); assertEquals("not( (contains(`name`, \"Elvis\")) )", c.export()); } @Test void testLike() { - QueryCriteria c = where("name").like("%ouch%"); + QueryCriteria c = where(i("name")).like("%ouch%"); assertEquals("`name` like \"%ouch%\"", c.export()); } @Test void testNotLike() { - QueryCriteria c = where("name").notLike("%Elvis%"); + QueryCriteria c = where(i("name")).notLike("%Elvis%"); assertEquals("not(`name` like \"%Elvis%\")", c.export()); } @Test void testIsNull() { - QueryCriteria c = where("name").isNull(); + QueryCriteria c = where(i("name")).isNull(); assertEquals("`name` is null", c.export()); } @Test void testIsNotNull() { - QueryCriteria c = where("name").isNotNull(); + QueryCriteria c = where(i("name")).isNotNull(); assertEquals("`name` is not null", c.export()); } @Test void testIsMissing() { - QueryCriteria c = where("name").isMissing(); + QueryCriteria c = where(i("name")).isMissing(); assertEquals("`name` is missing", c.export()); } @Test void testIsNotMissing() { - QueryCriteria c = where("name").isNotMissing(); + QueryCriteria c = where(i("name")).isNotMissing(); assertEquals("`name` is not missing", c.export()); } @Test void testIsValued() { - QueryCriteria c = where("name").isValued(); + QueryCriteria c = where(i("name")).isValued(); assertEquals("`name` is valued", c.export()); } @Test void testIsNotValued() { - QueryCriteria c = where("name").isNotValued(); + QueryCriteria c = where(i("name")).isNotValued(); assertEquals("`name` is not valued", c.export()); } @Test void testBetween() { - QueryCriteria c = where("name").between("Davis", "Gump"); + QueryCriteria c = where(i("name")).between("Davis", "Gump"); assertEquals("`name` between \"Davis\" and \"Gump\"", c.export()); } @Test void testIn() { String[] args = new String[] { "gump", "davis" }; - QueryCriteria c = where("name").in(args); + QueryCriteria c = where(i("name")).in(args); assertEquals("`name` in ( [\"gump\",\"davis\"] )", c.export()); JsonArray parameters = JsonArray.create(); assertEquals("`name` in ( $1 )", c.export(new int[1], parameters, null)); @@ -230,7 +225,7 @@ void testIn() { @Test void testNotIn() { String[] args = new String[] { "gump", "davis" }; - QueryCriteria c = where("name").notIn(args); + QueryCriteria c = where(i("name")).notIn(args); assertEquals("not( (`name` in ( [\"gump\",\"davis\"] )) )", c.export()); JsonArray parameters = JsonArray.create(); assertEquals("not( (`name` in ( $1 )) )", c.export(new int[1], parameters, null)); @@ -239,16 +234,32 @@ void testNotIn() { @Test void testTrue() { - QueryCriteria c = where("name").TRUE(); + QueryCriteria c = where(i("name")).TRUE(); assertEquals("`name`", c.export()); } @Test void testFalse() { - QueryCriteria c = where("name").FALSE(); + QueryCriteria c = where(i("name")).FALSE(); assertEquals("not( (`name`) )", c.export()); } + @Test // https://github.com/spring-projects/spring-data-couchbase/issues/1066 + void testCriteriaCorrectlyEscapedWhenUsingMetaOnLHS() { + final String bucketName = "sample-bucket"; + final String version = "1611287177404088320"; + QueryCriteria criteria = QueryCriteria.where(path(meta(escapedBucket(bucketName)), "cas")).eq(x(version)); + assertEquals("META(`" + bucketName + "`).cas = " + x(version), criteria.export()); + } + + @Test // https://github.com/spring-projects/spring-data-couchbase/issues/1066 + void testCriteriaCorrectlyEscapedWhenUsingMetaOnRHS() { + final String bucketName = "sample-bucket"; + final String version = "1611287177404088320"; + QueryCriteria criteria = QueryCriteria.where(x(version)).eq(path(meta(escapedBucket(bucketName)), "cas")); + assertEquals(x(version) + " = META(`" + bucketName + "`).cas", criteria.export()); + } + private String arrayToString(Object[] array) { StringBuilder sb = new StringBuilder(); if (array != null) { diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java b/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java index 023afa5b1..e00081225 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/UserRepository.java @@ -47,4 +47,8 @@ public interface UserRepository extends CouchbaseRepository { @Query("#{#n1ql.selectEntity} where #{#n1ql.filter} and (firstname = $first or lastname = $last)") List getByFirstnameOrLastname(@Param("first") String firstname, @Param("last") String lastname); + + List findByIdIsNotNullAndFirstnameEquals(String firstname); + + List findByVersionEqualsAndFirstnameEquals(Long version, String firstname); } diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java index a71a4fbe5..2a694c7c9 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.dao.DataIntegrityViolationException; @@ -306,7 +307,6 @@ private void sleep(int millis) { try { Thread.sleep(millis); // so they are executed out-of-order } catch (InterruptedException ie) { - ; } } diff --git a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java index d6b582f7a..b2c495f99 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java @@ -34,6 +34,7 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Test; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.dao.DataIntegrityViolationException; diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTests.java b/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTests.java index f3bbd618d..31162786d 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2019 the original author or authors. + * Copyright 2017-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,10 @@ package org.springframework.data.couchbase.repository.query; import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.data.couchbase.core.query.N1QLExpression.*; import static org.springframework.data.couchbase.core.query.QueryCriteria.*; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -43,16 +42,19 @@ /** * @author Michael Nitschinger * @author Michael Reiche + * @author Mauro Monti */ class N1qlQueryCreatorTests { MappingContext, CouchbasePersistentProperty> context; CouchbaseConverter converter; + String bucketName; @BeforeEach public void beforeEach() { context = new CouchbaseMappingContext(); converter = new MappingCouchbaseConverter(context); + bucketName = "sample-bucket"; } @Test @@ -62,10 +64,10 @@ void createsQueryCorrectly() throws Exception { Method method = UserRepository.class.getMethod(input, String.class); N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), null, - converter); + converter, bucketName); Query query = creator.createQuery(); - assertEquals(query.export(), " WHERE " + where("firstname").is("Oliver").export()); + assertEquals(query.export(), " WHERE " + where(i("firstname")).is("Oliver").export()); } @Test @@ -73,9 +75,9 @@ void queryParametersArray() throws Exception { String input = "findByFirstnameIn"; PartTree tree = new PartTree(input, User.class); Method method = UserRepository.class.getMethod(input, String[].class); - Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles")); + Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles")); N1qlQueryCreator creator = new N1qlQueryCreator(tree, - getAccessor(getParameters(method), new Object[] { new Object[] { "Oliver", "Charles" } }), null, converter); + getAccessor(getParameters(method), new Object[] { new Object[] { "Oliver", "Charles" } }), null, converter, bucketName); Query query = creator.createQuery(); // Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles")); @@ -97,10 +99,10 @@ void queryParametersJsonArray() throws Exception { jsonArray.add("Oliver"); jsonArray.add("Charles"); N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), jsonArray), null, - converter); + converter, bucketName); Query query = creator.createQuery(); - Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles")); + Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles")); assertEquals(expected.export(new int[1]), query.export(new int[1])); JsonObject expectedOptions = JsonObject.create(); expected.buildQueryOptions(null).build().injectParams(expectedOptions); @@ -118,10 +120,10 @@ void queryParametersList() throws Exception { list.add("Oliver"); list.add("Charles"); N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), new Object[] { list }), - null, converter); + null, converter, bucketName); Query query = creator.createQuery(); - Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles")); + Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles")); assertEquals(expected.export(new int[1]), query.export(new int[1])); JsonObject expectedOptions = JsonObject.create(); @@ -137,10 +139,36 @@ void createsAndQueryCorrectly() throws Exception { PartTree tree = new PartTree(input, User.class); Method method = UserRepository.class.getMethod(input, String.class, String.class); N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "John", "Doe"), null, - converter); + converter, bucketName); Query query = creator.createQuery(); - assertEquals(" WHERE " + where("firstname").is("John").and("lastname").is("Doe").export(), query.export()); + assertEquals(" WHERE " + where(i("firstname")).is("John").and(i("lastname")).is("Doe").export(), query.export()); + } + + @Test // https://github.com/spring-projects/spring-data-couchbase/issues/1072 + void createsQueryFindByIdIsNotNullAndFirstname() throws Exception { + String input = "findByIdIsNotNullAndFirstnameEquals"; + PartTree tree = new PartTree(input, User.class); + Method method = UserRepository.class.getMethod(input, String.class); + + N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), null, converter, + bucketName); + Query query = creator.createQuery(); + + assertEquals(query.export()," WHERE " + where(x("META(`"+bucketName+"`).`id`")).isNotNull().and(i("firstname")).is("Oliver").export()); + } + + @Test // https://github.com/spring-projects/spring-data-couchbase/issues/1072 + void createsQueryFindByVersionEqualsAndAndFirstname() throws Exception { + String input = "findByVersionEqualsAndFirstnameEquals"; + PartTree tree = new PartTree(input, User.class); + Method method = UserRepository.class.getMethod(input, Long.class, String.class); + + N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), 1611287177404088320L, "Oliver"), null, converter, + bucketName); + Query query = creator.createQuery(); + + assertEquals(query.export()," WHERE " + where(x("META(`"+bucketName+"`).`cas`")).is(1611287177404088320L).and(i("firstname")).is("Oliver").export()); } private ParameterAccessor getAccessor(Parameters params, Object... values) {