Add project(fields) to findByQuery similar to same method on findById. (#1212)
Closes #1208, #1213.
This commit is contained in:
@@ -221,7 +221,7 @@ public interface ExecutableFindByQueryOperation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
|
||||
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
@@ -254,12 +254,30 @@ public interface ExecutableFindByQueryOperation {
|
||||
<R> FindByQueryWithConsistency<R> as(Class<R> returnType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify fields to project.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {
|
||||
|
||||
/**
|
||||
* Define the target type fields should be mapped to. <br />
|
||||
* Skip this step if you are anyway only interested in the original domain type.
|
||||
*
|
||||
* @param fields to project
|
||||
* @return new instance of {@link ReactiveFindByQueryOperation.FindByQueryWithConsistency}.
|
||||
* @throws IllegalArgumentException if returnType is {@literal null}.
|
||||
*/
|
||||
FindByQueryWithProjection<T> project(String[] fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify DISTINCT fields
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjection<T>, WithDistinct<T> {
|
||||
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithDistinct<T> {
|
||||
|
||||
/**
|
||||
* Finds the distinct values for a specified {@literal field} across a single collection
|
||||
|
||||
@@ -19,7 +19,6 @@ import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport;
|
||||
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -45,7 +44,7 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
@Override
|
||||
public <T> ExecutableFindByQuery<T> findByQuery(final Class<T> domainType) {
|
||||
return new ExecutableFindByQuerySupport<T>(template, domainType, domainType, ALL_QUERY, null, null, null, null,
|
||||
null);
|
||||
null, null);
|
||||
}
|
||||
|
||||
static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T> {
|
||||
@@ -60,22 +59,24 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
private final String collection;
|
||||
private final QueryOptions options;
|
||||
private final String[] distinctFields;
|
||||
private final String[] fields;
|
||||
|
||||
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<?> domainType, final Class<T> returnType,
|
||||
final Query query, final QueryScanConsistency scanConsistency, final String scope, final String collection,
|
||||
final QueryOptions options, final String[] distinctFields) {
|
||||
final QueryOptions options, final String[] distinctFields, final String[] fields) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.returnType = returnType;
|
||||
this.query = query;
|
||||
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, returnType, query,
|
||||
scanConsistency, scope, collection, options, distinctFields,
|
||||
scanConsistency, scope, collection, options, distinctFields, fields,
|
||||
new NonReactiveSupportWrapper(template.support()));
|
||||
this.scanConsistency = scanConsistency;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.distinctFields = distinctFields;
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,38 +103,47 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
scanCons = scanConsistency;
|
||||
}
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
|
||||
options, distinctFields);
|
||||
options, distinctFields, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public FindByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
collection, options, distinctFields, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
collection, options, distinctFields, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> FindByQueryWithConsistency<R> as(final Class<R> returnType) {
|
||||
Assert.notNull(returnType, "returnType must not be null!");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
collection, options, distinctFields, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithProjection<T> project(String[] fields) {
|
||||
Assert.notNull(fields, "Fields must not be null");
|
||||
Assert.isNull(distinctFields, "only one of project(fields) and distinct(distinctFields) can be specified");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithProjection<T> distinct(final String[] distinctFields) {
|
||||
Assert.notNull(distinctFields, "distinctFields must not be null!");
|
||||
Assert.notNull(distinctFields, "distinctFields must not be null");
|
||||
Assert.isNull(fields, "only one of project(fields) and distinct(distinctFields) can be specified");
|
||||
// Coming from an annotation, this cannot be null.
|
||||
// But a non-null but empty distinctFields means distinct on all fields
|
||||
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
|
||||
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, dFields);
|
||||
collection, options, dFields, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -144,8 +154,8 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
@Override
|
||||
public long count() {
|
||||
Long l = reactiveSupport.count().block();
|
||||
if ( l == null ){
|
||||
throw new CouchbaseQueryExecutionException("count query did not return a count : "+query.export());
|
||||
if (l == null) {
|
||||
throw new CouchbaseQueryExecutionException("count query did not return a count : " + query.export());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
@@ -159,19 +169,19 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
collection, options, distinctFields, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryInCollection<T> inScope(final String scope) {
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
collection, options, distinctFields, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithConsistency<T> inCollection(final String collection) {
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
collection, options, distinctFields, fields);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -201,12 +201,30 @@ public interface ReactiveFindByQueryOperation {
|
||||
<R> FindByQueryWithConsistency<R> as(Class<R> returnType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify fields to project.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {
|
||||
|
||||
/**
|
||||
* Define the target type fields should be mapped to. <br />
|
||||
* Skip this step if you are anyway only interested in the original domain type.
|
||||
*
|
||||
* @param fields to project
|
||||
* @return new instance of {@link FindByQueryWithConsistency}.
|
||||
* @throws IllegalArgumentException if returnType is {@literal null}.
|
||||
*/
|
||||
FindByQueryWithProjection<T> project(String[] fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify DISTINCT fields
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjection<T>, WithDistinct<T> {
|
||||
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithDistinct<T> {
|
||||
|
||||
/**
|
||||
* Finds the distinct values for a specified {@literal field} across a single {@link } or view.
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
@Override
|
||||
public <T> ReactiveFindByQuery<T> findByQuery(final Class<T> domainType) {
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, domainType, ALL_QUERY, null, null, null, null, null,
|
||||
template.support());
|
||||
null, template.support());
|
||||
}
|
||||
|
||||
static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
|
||||
@@ -63,12 +63,13 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
private final String collection;
|
||||
private final String scope;
|
||||
private final String[] distinctFields;
|
||||
private final String[] fields;
|
||||
private final QueryOptions options;
|
||||
private final ReactiveTemplateSupport support;
|
||||
|
||||
ReactiveFindByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType,
|
||||
final Class<T> returnType, final Query query, final QueryScanConsistency scanConsistency, final String scope,
|
||||
final String collection, final QueryOptions options, final String[] distinctFields,
|
||||
final String collection, final QueryOptions options, final String[] distinctFields, final String[] fields,
|
||||
final ReactiveTemplateSupport support) {
|
||||
Assert.notNull(domainType, "domainType must not be null!");
|
||||
Assert.notNull(returnType, "returnType must not be null!");
|
||||
@@ -81,6 +82,7 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.distinctFields = distinctFields;
|
||||
this.fields = fields;
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
@@ -94,57 +96,65 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
scanCons = scanConsistency;
|
||||
}
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
|
||||
options, distinctFields, support);
|
||||
options, distinctFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
collection, options, distinctFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryInCollection<T> inScope(final String scope) {
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
collection, options, distinctFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithConsistency<T> inCollection(final String collection) {
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
collection, options, distinctFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency) {
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
collection, options, distinctFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithConsistency<T> withConsistency(QueryScanConsistency scanConsistency) {
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
collection, options, distinctFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> FindByQueryWithConsistency<R> as(Class<R> returnType) {
|
||||
Assert.notNull(returnType, "returnType must not be null!");
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
collection, options, distinctFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithProjection<T> project(String[] fields) {
|
||||
Assert.notNull(fields, "Fields must not be null");
|
||||
Assert.isNull(distinctFields, "only one of project(fields) and distinct(distinctFields) can be specified");
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithDistinct<T> distinct(final String[] distinctFields) {
|
||||
Assert.notNull(distinctFields, "distinctFields must not be null!");
|
||||
Assert.notNull(distinctFields, "distinctFields must not be null");
|
||||
Assert.isNull(fields, "only one of project(fields) and distinct(distinctFields) can be specified");
|
||||
// Coming from an annotation, this cannot be null.
|
||||
// But a non-null but empty distinctFields means distinct on all fields
|
||||
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
|
||||
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, dFields, support);
|
||||
collection, options, dFields, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -228,7 +238,7 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
|
||||
private String assembleEntityQuery(final boolean count, String[] distinctFields, String collection) {
|
||||
return query.toN1qlSelectString(template, collection, this.domainType, this.returnType, count,
|
||||
query.getDistinctFields() != null ? query.getDistinctFields() : distinctFields);
|
||||
query.getDistinctFields() != null ? query.getDistinctFields() : distinctFields, fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,13 +320,13 @@ public class Query {
|
||||
}
|
||||
|
||||
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
|
||||
return toN1qlSelectString(template, null, domainClass, null, isCount, null);
|
||||
return toN1qlSelectString(template, null, domainClass, null, isCount, null, null);
|
||||
}
|
||||
|
||||
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass,
|
||||
Class returnClass, boolean isCount, String[] distinctFields) {
|
||||
Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, collectionName, domainClass,
|
||||
returnClass, isCount, distinctFields);
|
||||
returnClass, isCount, distinctFields, fields);
|
||||
final StringBuilder statement = new StringBuilder();
|
||||
appendString(statement, n1ql.selectEntity); // select ...
|
||||
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
|
||||
@@ -340,7 +340,7 @@ public class Query {
|
||||
|
||||
public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass) {
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, collectionName, domainClass, null,
|
||||
false, null);
|
||||
false, null, null);
|
||||
final StringBuilder statement = new StringBuilder();
|
||||
appendString(statement, n1ql.delete); // delete ...
|
||||
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
|
||||
@@ -350,7 +350,7 @@ public class Query {
|
||||
}
|
||||
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTemplate template, String collectionName,
|
||||
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields) {
|
||||
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
|
||||
String typeKey = template.getConverter().getTypeKey();
|
||||
final CouchbasePersistentEntity<?> persistentEntity = template.getConverter().getMappingContext()
|
||||
.getRequiredPersistentEntity(domainClass);
|
||||
@@ -363,7 +363,7 @@ public class Query {
|
||||
}
|
||||
|
||||
StringBasedN1qlQueryParser sbnqp = new StringBasedN1qlQueryParser(template.getBucketName(), collectionName,
|
||||
template.getConverter(), domainClass, returnClass, typeKey, typeValue, distinctFields);
|
||||
template.getConverter(), domainClass, returnClass, typeKey, typeValue, isCount, distinctFields, fields);
|
||||
return isCount ? sbnqp.getCountContext() : sbnqp.getStatementContext();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.support.TemplateUtils;
|
||||
|
||||
import com.couchbase.client.java.json.JsonArray;
|
||||
import com.couchbase.client.java.json.JsonValue;
|
||||
import org.springframework.data.couchbase.core.support.TemplateUtils;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Query created from the string in @Query annotation in the repository interface.
|
||||
@@ -56,7 +56,7 @@ public class StringQuery extends Query {
|
||||
|
||||
@Override
|
||||
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collection, Class domainClass,
|
||||
Class resultClass, boolean isCount, String[] distinctFields) {
|
||||
Class resultClass, boolean isCount, String[] distinctFields, String[] fields) {
|
||||
final StringBuilder statement = new StringBuilder();
|
||||
boolean makeCount = isCount && inlineN1qlQuery != null
|
||||
&& !inlineN1qlQuery.toLowerCase(Locale.ROOT).contains("count(");
|
||||
@@ -95,6 +95,6 @@ public class StringQuery extends Query {
|
||||
*/
|
||||
@Override
|
||||
public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass) {
|
||||
return toN1qlSelectString(template, collectionName, domainClass, domainClass, false, null);
|
||||
return toN1qlSelectString(template, collectionName, domainClass, domainClass, false, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2020-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.
|
||||
* 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.core.support;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take Projection
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <R> - the entity class
|
||||
*/
|
||||
public interface WithProjecting<R> {
|
||||
Object project(String[] fields);
|
||||
|
||||
}
|
||||
@@ -87,7 +87,7 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
|
||||
|
||||
CouchbaseQueryExecution execution = getExecution(accessor,
|
||||
new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), null);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), typeToRead, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,20 +115,21 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
|
||||
if (isDeleteQuery()) {
|
||||
return new DeleteExecution(getOperations(), getQueryMethod());
|
||||
} else if (isTailable(getQueryMethod())) {
|
||||
return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all(); // s/b tail() instead of all()
|
||||
return (q, t, r, c) -> operation.as(r).matching(q.with(accessor.getPageable())).all(); // s/b tail() instead of
|
||||
// all()
|
||||
} else if (getQueryMethod().isCollectionQuery()) {
|
||||
return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all();
|
||||
return (q, t, r, c) -> operation.as(r).matching(q.with(accessor.getPageable())).all();
|
||||
} else if (getQueryMethod().isStreamQuery()) {
|
||||
return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).stream();
|
||||
return (q, t, r, c) -> operation.as(r).matching(q.with(accessor.getPageable())).stream();
|
||||
} else if (isCountQuery()) {
|
||||
return (q, t, c) -> operation.matching(q).count();
|
||||
return (q, t, r, c) -> operation.as(r).matching(q).count();
|
||||
} else if (isExistsQuery()) {
|
||||
return (q, t, c) -> operation.matching(q).exists();
|
||||
return (q, t, r, c) -> operation.as(r).matching(q).exists();
|
||||
} else if (getQueryMethod().isPageQuery()) {
|
||||
return new PagedExecution(operation, accessor.getPageable());
|
||||
} else {
|
||||
return (q, t, c) -> {
|
||||
TerminatingFindByQuery<?> find = operation.matching(q);
|
||||
return (q, t, r, c) -> {
|
||||
TerminatingFindByQuery<?> find = operation.as(r).matching(q);
|
||||
if (isCountQuery()) {
|
||||
return find.count();
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu
|
||||
|
||||
ReactiveCouchbaseQueryExecution execution = getExecution(accessor,
|
||||
new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), null);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), typeToRead, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +98,7 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu
|
||||
* @return
|
||||
*/
|
||||
private ReactiveCouchbaseQueryExecution getExecution(ParameterAccessor accessor,
|
||||
Converter<Object, Object> resultProcessing, ReactiveFindByQueryOperation.FindByQueryWithQuery<?> operation) {
|
||||
Converter<Object, Object> resultProcessing, ReactiveFindByQuery<?> operation) {
|
||||
return new ResultProcessingExecution(getExecutionToWrap(accessor, operation), resultProcessing);
|
||||
}
|
||||
|
||||
@@ -110,23 +110,24 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu
|
||||
* @return
|
||||
*/
|
||||
private ReactiveCouchbaseQueryExecution getExecutionToWrap(ParameterAccessor accessor,
|
||||
ReactiveFindByQueryOperation.FindByQueryWithQuery<?> operation) {
|
||||
ReactiveFindByQuery<?> operation) {
|
||||
|
||||
if (isDeleteQuery()) {
|
||||
return new DeleteExecution(getOperations(), getQueryMethod());
|
||||
} else if (isTailable(getQueryMethod())) {
|
||||
return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all(); // s/b tail() instead of all()
|
||||
return (q, t, r, c) -> operation.as(r).matching(q.with(accessor.getPageable())).all(); // s/b tail() instead of
|
||||
// all()
|
||||
} else if (getQueryMethod().isCollectionQuery()) {
|
||||
return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all();
|
||||
return (q, t, r, c) -> operation.as(r).matching(q.with(accessor.getPageable())).all();
|
||||
// } else if (getQueryMethod().isStreamQuery()) {
|
||||
// return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all().toStream();
|
||||
} else if (isCountQuery()) {
|
||||
return (q, t, c) -> operation.matching(q).count();
|
||||
return (q, t, r, c) -> operation.as(r).matching(q).count();
|
||||
} else if (isExistsQuery()) {
|
||||
return (q, t, c) -> operation.matching(q).exists();
|
||||
return (q, t, r, c) -> operation.as(r).matching(q).exists();
|
||||
} else {
|
||||
return (q, t, c) -> {
|
||||
ReactiveFindByQueryOperation.TerminatingFindByQuery<?> find = operation.matching(q);
|
||||
return (q, t, r, c) -> {
|
||||
ReactiveFindByQueryOperation.TerminatingFindByQuery<?> find = operation.as(r).matching(q);
|
||||
return isLimiting() ? find.first() : find.one();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.springframework.util.Assert;
|
||||
@FunctionalInterface
|
||||
interface CouchbaseQueryExecution {
|
||||
|
||||
Object execute(Query query, Class<?> type, String collection);
|
||||
Object execute(Query query, Class<?> type, Class<?> returnType, String collection);
|
||||
|
||||
/**
|
||||
* {@link CouchbaseQueryExecution} removing documents matching the query.
|
||||
@@ -60,7 +60,7 @@ interface CouchbaseQueryExecution {
|
||||
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery.Execution#execute(org.springframework.data.couchbase.core.query.Query, java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
public Object execute(Query query, Class<?> type, Class<?> returnType, String collection) {
|
||||
return operations.removeByQuery(type).matching(query).all();
|
||||
}
|
||||
|
||||
@@ -83,8 +83,8 @@ interface CouchbaseQueryExecution {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
return converter.convert(delegate.execute(query, type, collection));
|
||||
public Object execute(Query query, Class<?> type, Class<?> returnType, String collection) {
|
||||
return converter.convert(delegate.execute(query, type, returnType, collection));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,11 +109,11 @@ interface CouchbaseQueryExecution {
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
public Object execute(Query query, Class<?> type, Class<?> returnType, String collection) {
|
||||
int pageSize = pageable.getPageSize();
|
||||
// Apply Pageable but tweak limit to peek into next page
|
||||
Query modifiedQuery = query.skip(pageable.getOffset()).limit(pageSize + 1);
|
||||
List result = find.matching(modifiedQuery).all();
|
||||
List result = find.as(returnType).matching(modifiedQuery).all();
|
||||
boolean hasNext = result.size() > pageSize;
|
||||
return new SliceImpl<Object>(hasNext ? result.subList(0, pageSize) : result, pageable, hasNext);
|
||||
}
|
||||
@@ -139,9 +139,9 @@ interface CouchbaseQueryExecution {
|
||||
* @see org.springframework.data.couchbase.repository.query.CouchbaseQueryExecution#execute(org.springframework.data.couchbase.core.query.Query)
|
||||
*/
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
public Object execute(Query query, Class<?> type, Class<?> returnType, String collection) {
|
||||
int overallLimit = 0; // query.getLimit();
|
||||
TerminatingFindByQuery<?> matching = operation.matching(query);
|
||||
TerminatingFindByQuery<?> matching = operation.as(returnType).matching(query);
|
||||
// Adjust limit if page would exceed the overall limit
|
||||
if (overallLimit != 0 && pageable.getOffset() + pageable.getPageSize() > overallLimit) {
|
||||
query.limit((int) (overallLimit - pageable.getOffset()));
|
||||
|
||||
@@ -80,7 +80,7 @@ public class N1qlRepositoryQueryExecutor {
|
||||
return operation.matching(query).all();
|
||||
} else if (queryMethod.isPageQuery()) {
|
||||
Pageable p = accessor.getPageable();
|
||||
return new CouchbaseQueryExecution.PagedExecution(operation, p).execute(query, null, null);
|
||||
return new CouchbaseQueryExecution.PagedExecution(operation, p).execute(query, null, null, null);
|
||||
} else {
|
||||
return operation.matching(query).oneValue();
|
||||
}
|
||||
|
||||
@@ -28,9 +28,10 @@ import org.springframework.util.Assert;
|
||||
* @author Michael Reiche
|
||||
* @since 4.1
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface ReactiveCouchbaseQueryExecution {
|
||||
|
||||
Object execute(Query query, Class<?> type, String collection);
|
||||
Object execute(Query query, Class<?> type, Class<?> returnType, String collection);
|
||||
|
||||
/**
|
||||
* {@link ReactiveCouchbaseQueryExecution} removing documents matching the query.
|
||||
@@ -51,7 +52,7 @@ interface ReactiveCouchbaseQueryExecution {
|
||||
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery.Execution#execute(org.springframework.data.couchbase.core.query.Query, java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
public Object execute(Query query, Class<?> type, Class<?> returnType, String collection) {
|
||||
return operations.removeByQuery(type)/*.inCollection(collection)*/.matching(query).all();
|
||||
}
|
||||
|
||||
@@ -74,8 +75,8 @@ interface ReactiveCouchbaseQueryExecution {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
return converter.convert(delegate.execute(query, type, collection));
|
||||
public Object execute(Query query, Class<?> type, Class<?> returnType, String collection) {
|
||||
return converter.convert(delegate.execute(query, type, returnType, collection));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,12 +21,15 @@ import static org.springframework.data.couchbase.core.support.TemplateUtils.SELE
|
||||
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_ID;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.couchbase.client.core.error.CouchbaseException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
@@ -127,28 +130,38 @@ public class StringBasedN1qlQueryParser {
|
||||
this.queryMethod = queryMethod;
|
||||
this.couchbaseConverter = couchbaseConverter;
|
||||
String collection = queryMethod.getCollection();
|
||||
this.statementContext = createN1qlSpelValues(bucketName, collection, null, null, typeField, typeValue, false, null);
|
||||
this.countContext = createN1qlSpelValues(bucketName, collection, null, null, typeField, typeValue, true, null);
|
||||
this.statementContext = createN1qlSpelValues(bucketName, collection, null, null, typeField, typeValue, false, null,
|
||||
null);
|
||||
this.countContext = createN1qlSpelValues(bucketName, collection, null, null, typeField, typeValue, true, null,
|
||||
null);
|
||||
this.parsedExpression = getExpression(accessor, getParameters(accessor), null, parser, evaluationContextProvider);
|
||||
checkPlaceholders(this.parsedExpression.toString());
|
||||
}
|
||||
|
||||
public StringBasedN1qlQueryParser(String bucketName, String collection, CouchbaseConverter couchbaseConverter,
|
||||
Class domainClass, Class resultClass, String typeField, String typeValue, String[] distinctFields) {
|
||||
Class domainClass, Class resultClass, String typeField, String typeValue, boolean isCount,
|
||||
String[] distinctFields, String[] fields) {
|
||||
this.statement = null;
|
||||
this.queryMethod = null;
|
||||
this.couchbaseConverter = couchbaseConverter;
|
||||
this.statementContext = createN1qlSpelValues(bucketName, collection, domainClass, resultClass, typeField, typeValue,
|
||||
false, distinctFields);
|
||||
this.countContext = createN1qlSpelValues(bucketName, collection, domainClass, resultClass, typeField, typeValue,
|
||||
true, distinctFields);
|
||||
if (!isCount) {
|
||||
this.statementContext = createN1qlSpelValues(bucketName, collection, domainClass, resultClass, typeField,
|
||||
typeValue, false, distinctFields, fields);
|
||||
this.countContext = null;
|
||||
} else {
|
||||
this.statementContext = null;
|
||||
this.countContext = createN1qlSpelValues(bucketName, collection, domainClass, resultClass, typeField, typeValue,
|
||||
true, distinctFields, fields);
|
||||
}
|
||||
this.parsedExpression = null;
|
||||
}
|
||||
|
||||
public N1qlSpelValues createN1qlSpelValues(String bucketName, String collection, Class domainClass, Class resultClass,
|
||||
String typeField, String typeValue, boolean isCount, String[] distinctFields) {
|
||||
String typeField, String typeValue, boolean isCount, String[] distinctFields, String[] fields) {
|
||||
String b = collection != null ? collection : bucketName;
|
||||
String projectedFields = getProjectedFields(b, resultClass);
|
||||
Assert.isTrue(!(distinctFields != null && fields != null),
|
||||
"only one of project(fields) and distinct(distinctFields) can be specified");
|
||||
String projectedFields = getProjectedFields(b, resultClass, fields);
|
||||
String entity = "META(" + i(b) + ").id AS " + SELECT_ID + ", META(" + i(b) + ").cas AS " + SELECT_CAS;
|
||||
String count = "COUNT(*) AS " + CountFragment.COUNT_ALIAS;
|
||||
String selectEntity;
|
||||
@@ -163,7 +176,7 @@ public class StringBasedN1qlQueryParser {
|
||||
} else if (isCount) {
|
||||
selectEntity = "SELECT " + count + " FROM " + i(b);
|
||||
} else {
|
||||
selectEntity = "SELECT " + entity + ", " + projectedFields + " FROM " + i(b);
|
||||
selectEntity = "SELECT " + entity + (!projectedFields.isEmpty() ? ", " : " ") + projectedFields + " FROM " + i(b);
|
||||
}
|
||||
String typeSelection = "`" + typeField + "` = \"" + typeValue + "\"";
|
||||
|
||||
@@ -177,50 +190,77 @@ public class StringBasedN1qlQueryParser {
|
||||
return i(distinctFields).toString();
|
||||
}
|
||||
|
||||
private String getProjectedFields(String b, Class resultClass) {
|
||||
private String getProjectedFields(String b, Class resultClass, String[] fields) {
|
||||
|
||||
String projectedFields = i(b) + ".*";
|
||||
if (resultClass != null) {
|
||||
PersistentEntity persistentEntity = couchbaseConverter.getMappingContext().getPersistentEntity(resultClass);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
getProjectedFieldsInternal(b, null, sb, persistentEntity.getTypeInformation()/*, ""*/);
|
||||
getProjectedFieldsInternal(b, null, sb, persistentEntity.getTypeInformation(), fields/*, ""*/);
|
||||
projectedFields = sb.toString();
|
||||
}
|
||||
return projectedFields;
|
||||
}
|
||||
|
||||
private void getProjectedFieldsInternal(String bucketName, CouchbasePersistentProperty parent, StringBuilder sb,
|
||||
TypeInformation resultClass/*, String path*/) {
|
||||
TypeInformation resultClass, String[] fields/*, String path*/) {
|
||||
|
||||
PersistentEntity persistentEntity = couchbaseConverter.getMappingContext().getPersistentEntity(resultClass);
|
||||
// CouchbasePersistentProperty property = path.getLeafProperty();
|
||||
persistentEntity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
|
||||
@Override
|
||||
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
|
||||
if (prop.isIdProperty() && parent == null) {
|
||||
return;
|
||||
if (resultClass != null) {
|
||||
Set<String> fieldList = fields != null ? new HashSet<>(Arrays.asList(fields)) : null;
|
||||
PersistentEntity persistentEntity = couchbaseConverter.getMappingContext().getPersistentEntity(resultClass);
|
||||
// CouchbasePersistentProperty property = path.getLeafProperty();
|
||||
|
||||
persistentEntity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
|
||||
@Override
|
||||
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
|
||||
if (prop.isIdProperty() && parent == null) {
|
||||
return;
|
||||
}
|
||||
if (prop.isVersionProperty()) {
|
||||
return;
|
||||
}
|
||||
String projectField = null;
|
||||
|
||||
if (fieldList == null || fieldList.contains(prop.getFieldName())) {
|
||||
PersistentPropertyPath<CouchbasePersistentProperty> path = couchbaseConverter.getMappingContext()
|
||||
.getPersistentPropertyPath(prop.getName(), resultClass.getType());
|
||||
projectField = N1qlQueryCreator.addMetaIfRequired(bucketName, path, prop).toString();
|
||||
if (sb.length() > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(projectField); // from N1qlQueryCreator
|
||||
}
|
||||
|
||||
if (fieldList != null) {
|
||||
fieldList.remove(prop.getFieldName());
|
||||
}
|
||||
// The current limitation is that only top-level properties can be projected
|
||||
// This traversing of nested data structures would need to replicate the processing done by
|
||||
// MappingCouchbaseConverter. Either the read or write
|
||||
// And the n1ql to project lower-level properties is complex
|
||||
|
||||
// if (!conversions.isSimpleType(prop.getType())) {
|
||||
// getProjectedFieldsInternal(prop, sb, prop.getTypeInformation(), path+prop.getName()+".");
|
||||
// } else {
|
||||
|
||||
// }
|
||||
}
|
||||
if (prop.isVersionProperty()) {
|
||||
return;
|
||||
}
|
||||
PersistentPropertyPath<CouchbasePersistentProperty> path = couchbaseConverter.getMappingContext()
|
||||
.getPersistentPropertyPath(prop.getName(), resultClass.getType());
|
||||
|
||||
// The current limitation is that only top-level properties can be projected
|
||||
// This traversing of nested data structures would need to replicate the processing done by
|
||||
// MappingCouchbaseConverter. Either the read or write
|
||||
// And the n1ql to project lower-level properties is complex
|
||||
|
||||
// if (!conversions.isSimpleType(prop.getType())) {
|
||||
// getProjectedFieldsInternal(prop, sb, prop.getTypeInformation(), path+prop.getName()+".");
|
||||
// } else {
|
||||
});
|
||||
// throw an exception if there is an request for a field not in the entity.
|
||||
// needs further discussion as removing a field from an entity could cause this and not necessarily be an error
|
||||
if (fieldList != null && !fieldList.isEmpty()) {
|
||||
throw new CouchbaseException(
|
||||
"projected fields (" + fieldList + ") not found in entity: " + persistentEntity.getName());
|
||||
}
|
||||
} else {
|
||||
for (String field : fields) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(N1qlQueryCreator.addMetaIfRequired(bucketName, path, prop)); // from N1qlQueryCreator
|
||||
// }
|
||||
sb.append(x(field));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// this static method can be used to test the parsing behavior for Couchbase specific spel variables
|
||||
|
||||
@@ -27,10 +27,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.couchbase.client.core.error.CouchbaseException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
@@ -38,18 +41,23 @@ import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.data.couchbase.core.ExecutableRemoveByIdOperation.ExecutableRemoveById;
|
||||
import org.springframework.data.couchbase.core.ExecutableReplaceByIdOperation.ExecutableReplaceById;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllEntity;
|
||||
import org.springframework.data.couchbase.domain.Address;
|
||||
import org.springframework.data.couchbase.domain.Course;
|
||||
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
|
||||
import org.springframework.data.couchbase.domain.PersonValue;
|
||||
import org.springframework.data.couchbase.domain.Submission;
|
||||
import org.springframework.data.couchbase.domain.User;
|
||||
import org.springframework.data.couchbase.domain.UserAnnotated;
|
||||
import org.springframework.data.couchbase.domain.UserAnnotated2;
|
||||
import org.springframework.data.couchbase.domain.UserAnnotated3;
|
||||
import org.springframework.data.couchbase.domain.UserSubmission;
|
||||
import org.springframework.data.couchbase.util.ClusterType;
|
||||
import org.springframework.data.couchbase.util.IgnoreWhen;
|
||||
import org.springframework.data.couchbase.util.JavaIntegrationTests;
|
||||
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
;
|
||||
@@ -86,8 +94,8 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
|
||||
modifying.setVersion(user.getVersion());
|
||||
modified = couchbaseTemplate.replaceById(User.class).one(modifying);
|
||||
assertEquals(modifying, modified);
|
||||
if(user == modified){
|
||||
throw new RuntimeException ( " user == modified ");
|
||||
if (user == modified) {
|
||||
throw new RuntimeException(" user == modified ");
|
||||
}
|
||||
assertNotEquals(user, modified);
|
||||
assertEquals(NaiveAuditorAware.AUDITOR, modified.getCreatedBy());
|
||||
@@ -107,6 +115,52 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
|
||||
couchbaseTemplate.removeById().one(user.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findProjected() {
|
||||
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
|
||||
couchbaseTemplate.insertById(User.class).one(user);
|
||||
User found = couchbaseTemplate.findById(User.class).project(new String[] { "firstname" }).one(user.getId());
|
||||
System.err.println(found);
|
||||
couchbaseTemplate.removeById(User.class).one(user.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findProjecting() {
|
||||
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
|
||||
couchbaseTemplate.insertById(User.class).one(user);
|
||||
List<User> found = couchbaseTemplate.findByQuery(User.class).project(new String[] { "firstname" })
|
||||
.withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)).all();
|
||||
assertEquals(1, found.size());
|
||||
assertNotEquals(user, found.get(0), "should have found this document");
|
||||
assertEquals(user.getFirstname(), found.get(0).getFirstname(), "firstname should match");
|
||||
assertNull(found.get(0).getLastname(), "lastname should be null");
|
||||
couchbaseTemplate.removeById(User.class).one(user.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findProjectingPath() {
|
||||
UserSubmission user = new UserSubmission();
|
||||
user.setId(UUID.randomUUID().toString());
|
||||
user.setUsername("dave");
|
||||
user.setRoles(Arrays.asList("role1", "role2"));
|
||||
Address address = new Address();
|
||||
address.setStreet("1234 Olcott Street");
|
||||
address.setCity("Santa Clara");
|
||||
user.setAddress(address);
|
||||
user.setSubmissions(
|
||||
Arrays.asList(new Submission(UUID.randomUUID().toString(), user.getId(), "tid", "status", 123)));
|
||||
couchbaseTemplate.upsertById(UserSubmission.class).one(user);
|
||||
assertThrows(CouchbaseException.class, () -> couchbaseTemplate.findByQuery(UserSubmission.class).project(new String[] { "address.street" })
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).all());
|
||||
|
||||
List<UserSubmission> found = couchbaseTemplate.findByQuery(UserSubmission.class).project(new String[] { "address" })
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
assertEquals(found.size(), 1);
|
||||
assertEquals(found.get(0).getAddress(), address);
|
||||
assertNull(found.get(0).getUsername(), "username should have been null");
|
||||
couchbaseTemplate.removeById(User.class).one(user.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withDurability()
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.domain;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
|
||||
/**
|
||||
* AirportMini entity
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@Document
|
||||
public class AirportMini extends ComparableEntity {
|
||||
|
||||
@Id private String id;
|
||||
private String iata;
|
||||
private Address address;
|
||||
|
||||
@PersistenceConstructor
|
||||
public AirportMini(final String id, final String iata) {
|
||||
this.id = id;
|
||||
this.iata = iata;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getIata() {
|
||||
return iata;
|
||||
}
|
||||
|
||||
public void setIata(String iata) {
|
||||
this.iata = iata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, iata);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -68,6 +68,9 @@ public interface AirportRepository extends CouchbaseRepository<Airport, String>,
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
List<Airport> findAllByIata(String iata);
|
||||
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
List<AirportMini> getByIata(String iata);
|
||||
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
@ComposedMetaAnnotation(collection = "_default", timeoutMs = 1000)
|
||||
Airport findByIata(String iata);
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.springframework.data.couchbase.core.query.N1QLExpression;
|
||||
import org.springframework.data.couchbase.core.query.QueryCriteria;
|
||||
import org.springframework.data.couchbase.domain.Address;
|
||||
import org.springframework.data.couchbase.domain.Airport;
|
||||
import org.springframework.data.couchbase.domain.AirportMini;
|
||||
import org.springframework.data.couchbase.domain.AirportRepository;
|
||||
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
|
||||
import org.springframework.data.couchbase.domain.Person;
|
||||
@@ -239,6 +240,20 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void findBySimplePropertyReturnType() {
|
||||
Airport vie = null;
|
||||
try {
|
||||
vie = new Airport("airports::vie", "vie", "low6");
|
||||
vie = airportRepository.save(vie);
|
||||
List<AirportMini> airports = airportRepository.getByIata("vie");
|
||||
assertEquals(1, airports.size());
|
||||
System.out.println(airports.get(0));
|
||||
} finally {
|
||||
airportRepository.delete(vie);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByTypeAlias() {
|
||||
Airport vie = null;
|
||||
|
||||
Reference in New Issue
Block a user