DATACOUCH-588 - Refactoring part 1 of n. (#278)

* DATACOUCH-588 - Implement pageable and realign repo query

This implements pageable for non-reactive queries and
realigns reactive queries with other spring-data projects
to facilitate the implementaion of pageable (done) and other
types of queries such as projection and distinct (not yet
done)

* DATACOUCH-588 - Implement pageable and realign repo query

This implements pageable for non-reactive queries and
realigns reactive queries with other spring-data projects
to facilitate the implementaion of pageable (done) and other
types of queries such as projection and distinct (not yet
done)

* DATACOUCH-588 - Refactoring part 1 of n.

Co-authored-by: mikereiche <michael.reiche@couchbase.com>
This commit is contained in:
Michael Reiche
2020-12-21 13:48:50 -08:00
committed by GitHub
parent 9c9dde7312
commit 3364e0fa7f
59 changed files with 2389 additions and 241 deletions

View File

@@ -85,6 +85,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.1.0.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.couchbase.core;
import org.springframework.data.couchbase.core.ReactiveExistsByIdOperationSupport.ReactiveExistsByIdSupport;
import java.util.Collection;
import java.util.Map;
@@ -36,12 +38,11 @@ public class ExecutableExistsByIdOperationSupport implements ExecutableExistsByI
static class ExecutableExistsByIdSupport implements ExecutableExistsById {
private final CouchbaseTemplate template;
private final ReactiveExistsByIdOperationSupport.ReactiveExistsByIdSupport reactiveSupport;
private final ReactiveExistsByIdSupport reactiveSupport;
ExecutableExistsByIdSupport(final CouchbaseTemplate template, final String collection) {
this.template = template;
this.reactiveSupport = new ReactiveExistsByIdOperationSupport.ReactiveExistsByIdSupport(template.reactive(),
collection);
this.reactiveSupport = new ReactiveExistsByIdSupport(template.reactive(), collection);
}
@Override

View File

@@ -15,13 +15,14 @@
*/
package org.springframework.data.couchbase.core;
import org.springframework.data.couchbase.core.ReactiveFindByAnalyticsOperationSupport.ReactiveFindByAnalyticsSupport;
import java.util.List;
import java.util.stream.Stream;
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
import com.couchbase.client.java.query.QueryScanConsistency;
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
import org.springframework.data.couchbase.core.query.Query;
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
public class ExecutableFindByAnalyticsOperationSupport implements ExecutableFindByAnalyticsOperation {
@@ -35,14 +36,15 @@ public class ExecutableFindByAnalyticsOperationSupport implements ExecutableFind
@Override
public <T> ExecutableFindByAnalytics<T> findByAnalytics(final Class<T> domainType) {
return new ExecutableFindByAnalyticsSupport<>(template, domainType, ALL_QUERY, AnalyticsScanConsistency.NOT_BOUNDED);
return new ExecutableFindByAnalyticsSupport<>(template, domainType, ALL_QUERY,
AnalyticsScanConsistency.NOT_BOUNDED);
}
static class ExecutableFindByAnalyticsSupport<T> implements ExecutableFindByAnalytics<T> {
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final ReactiveFindByAnalyticsOperationSupport.ReactiveFindByAnalyticsSupport<T> reactiveSupport;
private final ReactiveFindByAnalyticsSupport<T> reactiveSupport;
private final AnalyticsQuery query;
private final AnalyticsScanConsistency scanConsistency;
@@ -51,8 +53,8 @@ public class ExecutableFindByAnalyticsOperationSupport implements ExecutableFind
this.template = template;
this.domainType = domainType;
this.query = query;
this.reactiveSupport = new ReactiveFindByAnalyticsOperationSupport.ReactiveFindByAnalyticsSupport<>(
template.reactive(), domainType, query, scanConsistency);
this.reactiveSupport = new ReactiveFindByAnalyticsSupport<>(template.reactive(), domainType, query,
scanConsistency);
this.scanConsistency = scanConsistency;
}

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.couchbase.core;
import org.springframework.data.couchbase.core.ReactiveFindByIdOperationSupport.ReactiveFindByIdSupport;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -40,15 +42,14 @@ public class ExecutableFindByIdOperationSupport implements ExecutableFindByIdOpe
private final Class<T> domainType;
private final String collection;
private final List<String> fields;
private final ReactiveFindByIdOperationSupport.ReactiveFindByIdSupport<T> reactiveSupport;
private final ReactiveFindByIdSupport<T> reactiveSupport;
ExecutableFindByIdSupport(CouchbaseTemplate template, Class<T> domainType, String collection, List<String> fields) {
this.template = template;
this.domainType = domainType;
this.collection = collection;
this.fields = fields;
this.reactiveSupport = new ReactiveFindByIdOperationSupport.ReactiveFindByIdSupport<>(template.reactive(),
domainType, collection, fields);
this.reactiveSupport = new ReactiveFindByIdSupport<>(template.reactive(), domainType, collection, fields);
}
@Override

View File

@@ -130,6 +130,17 @@ public interface ExecutableFindByQueryOperation {
}
interface ExecutableFindByQuery<T> extends FindByQueryConsistentWith<T> {}
interface FindByQueryInCollection<T> extends FindByQueryConsistentWith<T> {
/**
* Allows to override the default scan consistency.
*
* @param collection the collection to use for this query.
*/
FindByQueryInCollection<T> inCollection(String collection);
}
interface ExecutableFindByQuery<T> extends FindByQueryInCollection<T> {}
}

View File

@@ -18,11 +18,17 @@ package org.springframework.data.couchbase.core;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport;
import org.springframework.data.couchbase.core.query.Query;
import com.couchbase.client.java.query.QueryScanConsistency;
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport;
/**
* {@link ExecutableFindByQueryOperation} implementations for Couchbase.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQueryOperation {
private static final Query ALL_QUERY = new Query();
@@ -35,7 +41,8 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
@Override
public <T> ExecutableFindByQuery<T> findByQuery(final Class<T> domainType) {
return new ExecutableFindByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED);
return new ExecutableFindByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED,
"_default._default");
}
static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T> {
@@ -43,16 +50,19 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final Query query;
private final ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport<T> reactiveSupport;
private final ReactiveFindByQuerySupport<T> reactiveSupport;
private final QueryScanConsistency scanConsistency;
private final String collection;
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<T> domainType, final Query query,
final QueryScanConsistency scanConsistency) {
final QueryScanConsistency scanConsistency, final String collection) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, query, scanConsistency);
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, query, scanConsistency,
collection);
this.scanConsistency = scanConsistency;
this.collection = collection;
}
@Override
@@ -78,12 +88,17 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
} else {
scanCons = scanConsistency;
}
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanCons);
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanCons, collection);
}
@Override
public FindByQueryConsistentWith<T> consistentWith(final QueryScanConsistency scanConsistency) {
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency);
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
@Override
public FindByQueryInCollection<T> inCollection(final String collection) {
return new ExecutableFindByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
@Override

View File

@@ -17,6 +17,7 @@ package org.springframework.data.couchbase.core;
import java.util.Collection;
import org.springframework.data.couchbase.core.ReactiveFindFromReplicasByIdOperationSupport.ReactiveFindFromReplicasByIdSupport;
import org.springframework.util.Assert;
public class ExecutableFindFromReplicasByIdOperationSupport implements ExecutableFindFromReplicasByIdOperation {
@@ -37,14 +38,13 @@ public class ExecutableFindFromReplicasByIdOperationSupport implements Executabl
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final String collection;
private final ReactiveFindFromReplicasByIdOperationSupport.ReactiveFindFromReplicasByIdSupport<T> reactiveSupport;
private final ReactiveFindFromReplicasByIdSupport<T> reactiveSupport;
ExecutableFindFromReplicasByIdSupport(CouchbaseTemplate template, Class<T> domainType, String collection) {
this.template = template;
this.domainType = domainType;
this.collection = collection;
this.reactiveSupport = new ReactiveFindFromReplicasByIdOperationSupport.ReactiveFindFromReplicasByIdSupport<>(
template.reactive(), domainType, collection);
this.reactiveSupport = new ReactiveFindFromReplicasByIdSupport<>(template.reactive(), domainType, collection);
}
@Override

View File

@@ -18,6 +18,7 @@ package org.springframework.data.couchbase.core;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.couchbase.core.ReactiveInsertByIdOperationSupport.ReactiveInsertByIdSupport;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
@@ -48,7 +49,7 @@ public class ExecutableInsertByIdOperationSupport implements ExecutableInsertByI
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveInsertByIdOperationSupport.ReactiveInsertByIdSupport<T> reactiveSupport;
private final ReactiveInsertByIdSupport<T> reactiveSupport;
ExecutableInsertByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String collection,
final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel,
@@ -60,8 +61,8 @@ public class ExecutableInsertByIdOperationSupport implements ExecutableInsertByI
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.reactiveSupport = new ReactiveInsertByIdOperationSupport.ReactiveInsertByIdSupport<>(template.reactive(),
domainType, collection, persistTo, replicateTo, durabilityLevel, expiry);
this.reactiveSupport = new ReactiveInsertByIdSupport<>(template.reactive(), domainType, collection, persistTo,
replicateTo, durabilityLevel, expiry);
}
@Override

View File

@@ -18,6 +18,7 @@ package org.springframework.data.couchbase.core;
import java.util.Collection;
import java.util.List;
import org.springframework.data.couchbase.core.ReactiveRemoveByIdOperationSupport.ReactiveRemoveByIdSupport;
import org.springframework.util.Assert;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
@@ -44,7 +45,7 @@ public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByI
private final PersistTo persistTo;
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final ReactiveRemoveByIdOperationSupport.ReactiveRemoveByIdSupport reactiveRemoveByIdSupport;
private final ReactiveRemoveByIdSupport reactiveRemoveByIdSupport;
ExecutableRemoveByIdSupport(final CouchbaseTemplate template, final String collection, final PersistTo persistTo,
final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel) {
@@ -53,8 +54,8 @@ public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByI
this.persistTo = persistTo;
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.reactiveRemoveByIdSupport = new ReactiveRemoveByIdOperationSupport.ReactiveRemoveByIdSupport(
template.reactive(), collection, persistTo, replicateTo, durabilityLevel);
this.reactiveRemoveByIdSupport = new ReactiveRemoveByIdSupport(template.reactive(), collection, persistTo,
replicateTo, durabilityLevel);
}
@Override

View File

@@ -43,6 +43,12 @@ public interface ExecutableRemoveByQueryOperation {
}
interface ExecutableRemoveByQuery<T> extends RemoveByQueryConsistentWith<T> {}
interface RemoveByQueryInCollection<T> extends RemoveByQueryConsistentWith<T> {
RemoveByQueryConsistentWith<T> inCollection(String collection);
}
interface ExecutableRemoveByQuery<T> extends RemoveByQueryInCollection<T> {}
}

View File

@@ -17,6 +17,7 @@ package org.springframework.data.couchbase.core;
import java.util.List;
import org.springframework.data.couchbase.core.ReactiveRemoveByQueryOperationSupport.ReactiveRemoveByQuerySupport;
import org.springframework.data.couchbase.core.query.Query;
import com.couchbase.client.java.query.QueryScanConsistency;
@@ -33,7 +34,8 @@ public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemove
@Override
public <T> ExecutableRemoveByQuery<T> removeByQuery(Class<T> domainType) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED);
return new ExecutableRemoveByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED,
"_default._default");
}
static class ExecutableRemoveByQuerySupport<T> implements ExecutableRemoveByQuery<T> {
@@ -41,17 +43,19 @@ public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemove
private final CouchbaseTemplate template;
private final Class<T> domainType;
private final Query query;
private final ReactiveRemoveByQueryOperationSupport.ReactiveRemoveByQuerySupport<T> reactiveSupport;
private final ReactiveRemoveByQuerySupport<T> reactiveSupport;
private final QueryScanConsistency scanConsistency;
private final String collection;
ExecutableRemoveByQuerySupport(final CouchbaseTemplate template, final Class<T> domainType, final Query query,
final QueryScanConsistency scanConsistency) {
final QueryScanConsistency scanConsistency, String collection) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.reactiveSupport = new ReactiveRemoveByQueryOperationSupport.ReactiveRemoveByQuerySupport<>(
template.reactive(), domainType, query, scanConsistency);
this.reactiveSupport = new ReactiveRemoveByQuerySupport<>(template.reactive(), domainType, query, scanConsistency,
collection);
this.scanConsistency = scanConsistency;
this.collection = collection;
}
@Override
@@ -61,12 +65,17 @@ public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemove
@Override
public TerminatingRemoveByQuery<T> matching(final Query query) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency);
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
@Override
public RemoveByQueryWithQuery<T> consistentWith(final QueryScanConsistency scanConsistency) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency);
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
@Override
public RemoveByQueryInCollection<T> inCollection(final String collection) {
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
}

View File

@@ -19,6 +19,7 @@ import java.time.Duration;
import java.util.Collection;
import org.springframework.util.Assert;
import org.springframework.data.couchbase.core.ReactiveReplaceByIdOperationSupport.ReactiveReplaceByIdSupport;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
@@ -48,7 +49,7 @@ public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceB
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveReplaceByIdOperationSupport.ReactiveReplaceByIdSupport<T> reactiveSupport;
private final ReactiveReplaceByIdSupport<T> reactiveSupport;
ExecutableReplaceByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String collection,
final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel,
@@ -60,7 +61,7 @@ public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceB
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.reactiveSupport = new ReactiveReplaceByIdOperationSupport.ReactiveReplaceByIdSupport<>(template.reactive(),
this.reactiveSupport = new ReactiveReplaceByIdSupport<>(template.reactive(),
domainType, collection, persistTo, replicateTo, durabilityLevel, expiry);
}

View File

@@ -19,6 +19,7 @@ import java.time.Duration;
import java.util.Collection;
import org.springframework.util.Assert;
import org.springframework.data.couchbase.core.ReactiveUpsertByIdOperationSupport.ReactiveUpsertByIdSupport;
import com.couchbase.client.core.msg.kv.DurabilityLevel;
import com.couchbase.client.java.kv.PersistTo;
@@ -48,7 +49,7 @@ public class ExecutableUpsertByIdOperationSupport implements ExecutableUpsertByI
private final ReplicateTo replicateTo;
private final DurabilityLevel durabilityLevel;
private final Duration expiry;
private final ReactiveUpsertByIdOperationSupport.ReactiveUpsertByIdSupport<T> reactiveSupport;
private final ReactiveUpsertByIdSupport<T> reactiveSupport;
ExecutableUpsertByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String collection,
final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel,
@@ -60,7 +61,7 @@ public class ExecutableUpsertByIdOperationSupport implements ExecutableUpsertByI
this.replicateTo = replicateTo;
this.durabilityLevel = durabilityLevel;
this.expiry = expiry;
this.reactiveSupport = new ReactiveUpsertByIdOperationSupport.ReactiveUpsertByIdSupport<>(template.reactive(),
this.reactiveSupport = new ReactiveUpsertByIdSupport<>(template.reactive(),
domainType, collection, persistTo, replicateTo, durabilityLevel, expiry);
}

View File

@@ -17,7 +17,6 @@ package org.springframework.data.couchbase.core;
import com.couchbase.client.java.analytics.AnalyticsOptions;
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
import com.couchbase.client.java.query.QueryOptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

View File

@@ -15,14 +15,20 @@
*/
package org.springframework.data.couchbase.core;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.couchbase.core.query.Query;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* ReactiveFindByQueryOperation
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
public interface ReactiveFindByQueryOperation {
/**
@@ -104,6 +110,129 @@ public interface ReactiveFindByQueryOperation {
}
interface ReactiveFindByQuery<T> extends FindByQueryConsistentWith<T> {}
/**
* Collection override (optional).
*/
interface FindByQueryInCollection<T> extends FindByQueryWithQuery<T> {
/**
* Explicitly set the name of the collection to perform the query on. <br />
* Skip this step to use the default collection derived from the domain type.
*
* @param collection must not be {@literal null} nor {@literal empty}.
* @return new instance of {@link FindWithProjection}.
* @throws IllegalArgumentException if collection is {@literal null}.
*/
FindByQueryInCollection<T> inCollection(String collection);
}
/**
* Result type override (optional).
*/
interface FindWithProjection<T> extends FindByQueryInCollection<T>, FindDistinct {
/**
* 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 resultType must not be {@literal null}.
* @param <R> result type.
* @return new instance of {@link FindWithProjection}.
* @throws IllegalArgumentException if resultType is {@literal null}.
*/
<R> FindByQueryWithQuery<R> as(Class<R> resultType);
}
/**
* Distinct Find support.
*
* @author Christoph Strobl
* @since 2.1
*/
interface FindDistinct {
/**
* Finds the distinct values for a specified {@literal field} across a single {@link } or view.
*
* @param field name of the field. Must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if field is {@literal null}.
*/
TerminatingDistinct<Object> distinct(String field);
}
/**
* Result type override. Optional.
*
* @author Christoph Strobl
* @since 2.1
*/
interface DistinctWithProjection {
/**
* Define the target type the result should be mapped to. <br />
* Skip this step if you are anyway fine with the default conversion.
* <dl>
* <dt>{@link Object} (the default)</dt>
* <dd>Result is mapped according to the {@link } converting eg. {@link } into plain {@link String}, {@link } to
* {@link Long}, etc. always picking the most concrete type with respect to the domain types property.<br />
* Any {@link } is run through the {@link org.springframework.data.convert.EntityReader} to obtain the domain type.
* <br />
* Using {@link Object} also works for non strictly typed fields. Eg. a mixture different types like fields using
* {@link String} in one {@link } while {@link Long} in another.</dd>
* <dt>Any Simple type like {@link String}, {@link Long}, ...</dt>
* <dd>The result is mapped directly by the Couchbase Java driver and the {@link } in place. This works only for
* results where all documents considered for the operation use the very same type for the field.</dd>
* <dt>Any Domain type</dt>
* <dd>Domain types can only be mapped if the if the result of the actual {@code distinct()} operation returns
* {@link }.</dd>
* <dt>{@link }</dt>
* <dd>Using {@link } allows retrieval of the raw driver specific format, which returns eg. {@link }.</dd>
* </dl>
*
* @param resultType must not be {@literal null}.
* @param <R> result type.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if resultType is {@literal null}.
*/
<R> TerminatingDistinct<R> as(Class<R> resultType);
}
/**
* Result restrictions. Optional.
*
* @author Christoph Strobl
* @since 2.1
*/
interface DistinctWithQuery<T> extends DistinctWithProjection {
/**
* Set the filter {@link Query criteria} to be used.
*
* @param query must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
TerminatingDistinct<T> matching(Query query);
}
/**
* Terminating distinct find operations.
*
* @author Christoph Strobl
* @since 2.1
*/
interface TerminatingDistinct<T> extends DistinctWithQuery<T> {
/**
* Get all matching distinct field values.
*
* @return empty {@link Flux} if not match found. Never {@literal null}.
*/
Flux<T> all();
}
interface ReactiveFindByQuery<T> extends FindByQueryConsistentWith<T>, FindByQueryInCollection<T>, FindDistinct {}
}

View File

@@ -15,16 +15,18 @@
*/
package org.springframework.data.couchbase.core;
import org.springframework.data.couchbase.core.support.TemplateUtils;
import org.springframework.data.couchbase.core.query.Query;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.support.TemplateUtils;
import com.couchbase.client.java.query.QueryScanConsistency;
import com.couchbase.client.java.query.ReactiveQueryResult;
/**
* {@link ReactiveFindByQueryOperation} implementations for Couchbase.
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
@@ -40,7 +42,8 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
@Override
public <T> ReactiveFindByQuery<T> findByQuery(final Class<T> domainType) {
return new ReactiveFindByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED);
return new ReactiveFindByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED,
"_default._default");
}
static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
@@ -49,13 +52,15 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
private final Class<T> domainType;
private final Query query;
private final QueryScanConsistency scanConsistency;
private final String collection;
ReactiveFindByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final Query query,
final QueryScanConsistency scanConsistency) {
final QueryScanConsistency scanConsistency, final String collection) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.scanConsistency = scanConsistency;
this.collection = collection;
}
@Override
@@ -66,12 +71,22 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
} else {
scanCons = scanConsistency;
}
return new ReactiveFindByQuerySupport<>(template, domainType, query, scanCons);
return new ReactiveFindByQuerySupport<>(template, domainType, query, scanCons, collection);
}
@Override
public FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency) {
return new ReactiveFindByQuerySupport<>(template, domainType, query, scanConsistency);
return new ReactiveFindByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
@Override
public FindByQueryInCollection<T> inCollection(String collection) {
return new ReactiveFindByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
@Override
public TerminatingDistinct<Object> distinct(String field) {
throw new RuntimeException(("not implemented"));
}
@Override
@@ -116,8 +131,9 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
} else {
return throwable;
}
}).flatMapMany(ReactiveQueryResult::rowsAsObject).map(row -> row.getLong(TemplateUtils.SELECT_COUNT))
.next();
}).flatMapMany(ReactiveQueryResult::rowsAsObject).map(row -> {
return row.getLong(TemplateUtils.SELECT_COUNT);
}).next();
});
}
@@ -129,7 +145,6 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
private String assembleEntityQuery(final boolean count) {
return query.toN1qlSelectString(template, this.domainType, count);
}
}
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.couchbase.core;
import static com.couchbase.client.java.kv.GetAnyReplicaOptions.*;
import static com.couchbase.client.java.kv.GetAnyReplicaOptions.getAnyReplicaOptions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

View File

@@ -22,6 +22,7 @@ import java.time.Duration;
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;

View File

@@ -41,6 +41,12 @@ public interface ReactiveRemoveByQueryOperation {
}
interface ReactiveRemoveByQuery<T> extends RemoveByQueryConsistentWith<T> {}
interface RemoveByQueryInCollection<T> extends RemoveByQueryConsistentWith<T> {
RemoveByQueryConsistentWith<T> inCollection(String collection);
}
interface ReactiveRemoveByQuery<T> extends RemoveByQueryInCollection<T> {}
}

View File

@@ -38,7 +38,8 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu
@Override
public <T> ReactiveRemoveByQuery<T> removeByQuery(Class<T> domainType) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED);
return new ReactiveRemoveByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED,
"_default._default");
}
static class ReactiveRemoveByQuerySupport<T> implements ReactiveRemoveByQuery<T> {
@@ -47,13 +48,15 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu
private final Class<T> domainType;
private final Query query;
private final QueryScanConsistency scanConsistency;
private final String collection;
ReactiveRemoveByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final Query query,
final QueryScanConsistency scanConsistency) {
final QueryScanConsistency scanConsistency, String collection) {
this.template = template;
this.domainType = domainType;
this.query = query;
this.scanConsistency = scanConsistency;
this.collection = collection;
}
@Override
@@ -69,7 +72,8 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu
return throwable;
}
}).flatMapMany(ReactiveQueryResult::rowsAsObject)
.map(row -> new RemoveResult(row.getString(TemplateUtils.SELECT_ID), row.getLong(TemplateUtils.SELECT_CAS), Optional.empty()));
.map(row -> new RemoveResult(row.getString(TemplateUtils.SELECT_ID), row.getLong(TemplateUtils.SELECT_CAS),
Optional.empty()));
});
}
@@ -83,12 +87,17 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu
@Override
public TerminatingRemoveByQuery<T> matching(final Query query) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency);
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
@Override
public RemoveByQueryWithQuery<T> consistentWith(final QueryScanConsistency scanConsistency) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency);
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
@Override
public RemoveByQueryInCollection<T> inCollection(final String collection) {
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
}
private String assembleDeleteQuery() {

View File

@@ -16,7 +16,8 @@
package org.springframework.data.couchbase.core.convert;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.*;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.UNIQUE;
import static org.springframework.data.couchbase.core.mapping.id.GenerationStrategy.USE_ATTRIBUTES;
import java.util.ArrayList;
import java.util.Collection;
@@ -45,8 +46,13 @@ import org.springframework.data.couchbase.core.mapping.id.IdAttribute;
import org.springframework.data.couchbase.core.mapping.id.IdPrefix;
import org.springframework.data.couchbase.core.mapping.id.IdSuffix;
import org.springframework.data.couchbase.core.query.N1qlJoin;
import org.springframework.data.mapping.*;
import org.springframework.data.mapping.Alias;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.AssociationHandler;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
@@ -261,7 +267,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
|| prop.isAnnotationPresent(N1qlJoin.class)) {
return;
}
Object obj = prop.isIdProperty() ? source.getId() : getValueInternal(prop, source, instance);
Object obj = prop.isIdProperty() && parent == null ? source.getId() : getValueInternal(prop, source, instance);
accessor.setProperty(prop, obj);
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 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.core.query;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Meta-data for {@link Query} instances.
*
* @author Michael Reiche
*/
public class Meta {
private enum MetaKey {
EXAMPLE("$example");
private String key;
MetaKey(String key) {
this.key = key;
}
}
private final Map<String, Object> values = new LinkedHashMap<>(2);
public Meta() {}
/**
* Copy a {@link Meta} object.
*
* @since 2.2
* @param source
*/
Meta(Meta source) {
this.values.putAll(source.values);
}
/**
* @return
*/
public boolean hasValues() {
return !this.values.isEmpty();
}
/**
* Get {@link Iterable} of set meta values.
*
* @return
*/
public Iterable<Entry<String, Object>> values() {
return Collections.unmodifiableSet(this.values.entrySet());
}
/**
* Sets or removes the value in case of {@literal null} or empty {@link String}.
*
* @param key must not be {@literal null} or empty.
* @param value
*/
void setValue(String key, @Nullable Object value) {
Assert.hasText(key, "Meta key must not be 'null' or blank.");
if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) {
this.values.remove(key);
}
this.values.put(key, value);
}
@Nullable
@SuppressWarnings("unchecked")
private <T> T getValue(String key) {
return (T) this.values.get(key);
}
private <T> T getValue(String key, T defaultValue) {
T value = getValue(key);
return value != null ? value : defaultValue;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int hash = ObjectUtils.nullSafeHashCode(this.values);
return hash;
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Meta)) {
return false;
}
Meta other = (Meta) obj;
return ObjectUtils.nullSafeEquals(this.values, other.values);
}
}

View File

@@ -20,11 +20,6 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.json.JsonValue;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
@@ -37,6 +32,12 @@ import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.json.JsonValue;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* @author Michael Nitschinger
* @author Michael Reiche
@@ -138,7 +139,6 @@ public class Query {
return queryScanConsistency;
}
/**
* Sets the given scan consistency on the {@link Query} instance.
*
@@ -319,4 +319,7 @@ public class Query {
return options;
}
public void setMeta(Meta metaAnnotation) {
Meta meta = metaAnnotation;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 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;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.QueryAnnotation;
/**
* @author Michael Reiche
* @since 4.1
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
@QueryAnnotation
public @interface Meta {
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright 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.core.convert.converter.Converter;
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.couchbase.repository.query.CouchbaseQueryExecution.DeleteExecution;
import org.springframework.data.couchbase.repository.query.CouchbaseQueryExecution.PagedExecution;
import org.springframework.data.repository.core.EntityMetadata;
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.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link RepositoryQuery} implementation for Couchbase.
*
* @author Michael Reiche
* @since 4.1
*/
public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<CouchbaseOperations>
implements RepositoryQuery {
private final ExecutableFindByQueryOperation.ExecutableFindByQuery<?> findOperationWithProjection;
/**
* Creates a new {@link AbstractCouchbaseQuery} from the given {@link ReactiveCouchbaseQueryMethod} and
* {@link org.springframework.data.couchbase.core.CouchbaseOperations}.
*
* @param method must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
*/
public AbstractCouchbaseQuery(CouchbaseQueryMethod method, CouchbaseOperations operations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, operations, expressionParser, evaluationContextProvider);
Assert.notNull(method, "CouchbaseQueryMethod must not be null!");
Assert.notNull(operations, "ReactiveCouchbaseOperations must not be null!");
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");
Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!");
// this.operations = operations;
EntityMetadata<?> metadata = method.getEntityInformation();
Class<?> type = metadata.getJavaType();
this.findOperationWithProjection = operations.findByQuery(type);
}
/**
* Execute the {@link RepositoryQuery} of the given method with the parameters provided by the
* {@link ParametersParameterAccessor accessor}
*
* @param method the {@link ReactiveCouchbaseQueryMethod} invoked. Never {@literal null}.
* @param processor {@link ResultProcessor} for post procession. Never {@literal null}.
* @param accessor for providing invocation arguments. Never {@literal null}.
* @param typeToRead the desired component target type. Can be {@literal null}.
*/
@Override
protected Object doExecute(CouchbaseQueryMethod method, ResultProcessor processor,
ParametersParameterAccessor accessor, @Nullable Class<?> typeToRead) {
Query query = createQuery(accessor);
query = applyAnnotatedConsistencyIfPresent(query);
// query = applyAnnotatedCollationIfPresent(query, accessor); // not yet implemented
ExecutableFindByQueryOperation.ExecutableFindByQuery<?> find = typeToRead == null
? findOperationWithProjection //
: findOperationWithProjection; // not yet implemented in core .as(typeToRead);
String collection = "_default._default";// method.getEntityInformation().getCollectionName(); // not yet implemented
CouchbaseQueryExecution execution = getExecution(accessor,
new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find);
return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
}
/**
* Returns the execution instance to use.
*
* @param accessor must not be {@literal null}.
* @param resultProcessing must not be {@literal null}.
* @return
*/
private CouchbaseQueryExecution getExecution(ParameterAccessor accessor, Converter<Object, Object> resultProcessing,
ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation) {
return new CouchbaseQueryExecution.ResultProcessingExecution(getExecutionToWrap(accessor, operation),
resultProcessing);
}
/**
* Returns the execution to wrap
*
* @param accessor must not be {@literal null}.
* @param operation must not be {@literal null}.
* @return
*/
private CouchbaseQueryExecution getExecutionToWrap(ParameterAccessor accessor,
ExecutableFindByQueryOperation.ExecutableFindByQuery<?> 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()
} else if (getQueryMethod().isCollectionQuery()) {
return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all();
} else if (isCountQuery()) {
return (q, t, c) -> operation.matching(q).count();
} else if (isExistsQuery()) {
return (q, t, c) -> operation.matching(q).exists();
} else if (getQueryMethod().isPageQuery()) {
return new PagedExecution(operation, accessor.getPageable());
} else {
return (q, t, c) -> {
ExecutableFindByQueryOperation.TerminatingFindByQuery<?> find = operation.matching(q);
if (isCountQuery()) {
return find.count();
}
return isLimiting() ? find.first() : find.one();
};
}
}
/**
* Apply Meta annotation to query
*
* @param query must not be {@literal null}.
* @return Query
*/
Query applyQueryMetaAttributesWhenPresent(Query query) {
if (getQueryMethod().hasQueryMetaAttributes()) {
query.setMeta(getQueryMethod().getQueryMetaAttributes());
}
return query;
}
}

View File

@@ -0,0 +1,247 @@
/*
* Copyright 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 reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.reactivestreams.Publisher;
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.mapping.model.EntityInstantiators;
import org.springframework.data.repository.core.EntityMetadata;
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.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* {@link RepositoryQuery} implementation for Couchbase.
*
* CouchbaseOperationsType is either CouchbaseOperations or ReactiveCouchbaseOperations
* @author Michael Reiche
* @since 4.1
*/
public abstract class AbstractCouchbaseQueryBase<CouchbaseOperationsType> implements RepositoryQuery {
private final CouchbaseQueryMethod method;
private final CouchbaseOperationsType operations;
private final EntityInstantiators instantiators;
private final ExecutableFindByQueryOperation.ExecutableFindByQuery<?> findOperationWithProjection;
private final SpelExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
/**
* Creates a new {@link AbstractCouchbaseQuery} from the given {@link ReactiveCouchbaseQueryMethod} and
* {@link org.springframework.data.couchbase.core.CouchbaseOperations}.
*
* @param method must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
*/
public AbstractCouchbaseQueryBase(CouchbaseQueryMethod method, CouchbaseOperationsType operations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
Assert.notNull(method, "CouchbaseQueryMethod must not be null!");
Assert.notNull(operations, "ReactiveCouchbaseOperations must not be null!");
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");
Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!");
this.method = method;
this.operations = operations;
this.instantiators = new EntityInstantiators();
this.expressionParser = expressionParser;
this.evaluationContextProvider = evaluationContextProvider;
EntityMetadata<?> metadata = method.getEntityInformation();
Class<?> type = metadata.getJavaType();
this.findOperationWithProjection = operations instanceof CouchbaseOperations
? ((CouchbaseOperations) operations).findByQuery(type)
: null; // not yet implemented for Reactive
}
/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod()
*/
public CouchbaseQueryMethod getQueryMethod() {
return method;
};
/*
* (non-Javadoc)
*/
public CouchbaseOperationsType getOperations() {
return operations;
}
/*
* (non-Javadoc)
*/
EntityInstantiators getInstantiators() {
return instantiators;
}
/**
* Execute the query with the provided parameters
* @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[])
*/
public Object execute(Object[] parameters) {
return method.hasReactiveWrapperParameter() ? executeDeferred(parameters)
: execute(new ReactiveCouchbaseParameterAccessor(getQueryMethod(), parameters));
}
private Object executeDeferred(Object[] parameters) {
ReactiveCouchbaseParameterAccessor parameterAccessor = new ReactiveCouchbaseParameterAccessor(method, parameters);
if (getQueryMethod().isCollectionQuery()) {
return Flux.defer(() -> (Publisher<Object>) execute(parameterAccessor));
}
return Mono.defer(() -> (Mono<Object>) execute(parameterAccessor));
}
private Object execute(ParametersParameterAccessor parameterAccessor) {
TypeInformation<?> returnType = ClassTypeInformation
.from(method.getResultProcessor().getReturnedType().getReturnedType());
ResultProcessor processor = method.getResultProcessor().withDynamicProjection(parameterAccessor);
Class<?> typeToRead = processor.getReturnedType().getTypeToRead();
if (typeToRead == null && returnType.getComponentType() != null) {
typeToRead = returnType.getComponentType().getType();
}
return doExecute(getQueryMethod(), processor, parameterAccessor, typeToRead);
}
/**
* Execute the {@link RepositoryQuery} of the given method with the parameters provided by the
* {@link ParametersParameterAccessor accessor}
*
* @param method the {@link ReactiveCouchbaseQueryMethod} invoked. Never {@literal null}.
* @param processor {@link ResultProcessor} for post procession. Never {@literal null}.
* @param accessor for providing invocation arguments. Never {@literal null}.
* @param typeToRead the desired component target type. Can be {@literal null}.
*/
abstract protected Object doExecute(CouchbaseQueryMethod method, ResultProcessor processor,
ParametersParameterAccessor accessor, @Nullable Class<?> typeToRead);
/**
* Add a scan consistency from {@link org.springframework.data.couchbase.repository.ScanConsistency} to the given
* {@link Query} if present.
*
* @param query the {@link Query} to potentially apply the sort to.
* @return the query with potential scan consistency applied.
* @since 4.1
*/
Query applyAnnotatedConsistencyIfPresent(Query query) {
if (!method.hasScanConsistencyAnnotation()) {
return query;
}
return query.scanConsistency(method.getScanConsistencyAnnotation().query());
}
/**
* Creates a {@link Query} instance using the given {@link ParametersParameterAccessor}. Will delegate to
* {@link #createQuery(ParametersParameterAccessor)} by default but allows customization of the count query to be
* triggered.
*
* @param accessor must not be {@literal null}.
* @return
*/
protected abstract Query createCountQuery(ParametersParameterAccessor accessor);
/**
* Creates a {@link Query} instance using the given {@link ParameterAccessor}
*
* @param accessor must not be {@literal null}.
* @return
*/
protected abstract Query createQuery(ParametersParameterAccessor accessor);
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractReactiveCouchbaseQuery#isCountQuery()
*/
protected boolean isCountQuery() {
return getQueryMethod().isCountQuery();
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractReactiveCouchbaseQuery#isExistsQuery()
*/
protected boolean isExistsQuery() {
return getQueryMethod().isExistsQuery();
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractReactiveCouchbaseQuery#isDeleteQuery()
*/
protected boolean isDeleteQuery() {
return getQueryMethod().isDeleteQuery();
}
/**
* Return whether the query is tailable
*
* @return
*/
boolean isTailable(CouchbaseQueryMethod method) {
return false; // method.getTailableAnnotation() != null; // Not yet implemented
}
/**
* Return whether the query has an explicit limit set.
*
* @return
*/
protected abstract boolean isLimiting();
/**
* Return whether there are ambiguous projection flags
*
* @return
*/
static boolean hasAmbiguousProjectionFlags(boolean isCountQuery, boolean isExistsQuery, boolean isDeleteQuery) {
return multipleOf(isCountQuery, isExistsQuery, isDeleteQuery);
}
/**
* Count the number of {@literal true} values.
*
* @param values
* @return are there more than one of these true?
*/
static boolean multipleOf(boolean... values) {
int count = 0;
for (boolean value : values) {
if (value) {
if (count != 0) {
return true;
}
count++;
}
}
return false;
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 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.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations;
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperation;
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperation.ReactiveFindByQuery;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.repository.query.ReactiveCouchbaseQueryExecution.DeleteExecution;
import org.springframework.data.couchbase.repository.query.ReactiveCouchbaseQueryExecution.ResultProcessingExecution;
import org.springframework.data.repository.core.EntityMetadata;
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.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Base class for reactive {@link RepositoryQuery} implementations for Couchbase.
*
* @author Michael Reiche
* @since 4.1
*/
public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQueryBase<ReactiveCouchbaseOperations>
implements RepositoryQuery {
private final ReactiveFindByQuery<?> findOperationWithProjection;
/**
* Creates a new {@link AbstractReactiveCouchbaseQuery} from the given {@link ReactiveCouchbaseQueryMethod} and
* {@link org.springframework.data.couchbase.core.CouchbaseOperations}.
*
* @param method must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
*/
public AbstractReactiveCouchbaseQuery(ReactiveCouchbaseQueryMethod method, ReactiveCouchbaseOperations operations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, operations, expressionParser, evaluationContextProvider);
Assert.notNull(method, "CouchbaseQueryMethod must not be null!");
Assert.notNull(operations, "ReactiveCouchbaseOperations must not be null!");
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");
Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!");
EntityMetadata<?> metadata = method.getEntityInformation();
Class<?> type = metadata.getJavaType();
this.findOperationWithProjection = operations.findByQuery(type);
}
/**
* Execute the {@link RepositoryQuery} of the given method with the parameters provided by the
* {@link ParametersParameterAccessor accessor}
*
* @param method the {@link ReactiveCouchbaseQueryMethod} invoked. Never {@literal null}.
* @param processor {@link ResultProcessor} for post procession. Never {@literal null}.
* @param accessor for providing invocation arguments. Never {@literal null}.
* @param typeToRead the desired component target type. Can be {@literal null}.
*/
protected Object doExecute(CouchbaseQueryMethod method, ResultProcessor processor,
ParametersParameterAccessor accessor, @Nullable Class<?> typeToRead) {
Query query = createQuery(accessor);
query = applyAnnotatedConsistencyIfPresent(query);
// query = applyAnnotatedCollationIfPresent(query, accessor); // not yet implemented
ReactiveFindByQueryOperation.FindByQueryWithQuery<?> find = typeToRead == null //
? findOperationWithProjection //
: findOperationWithProjection; // note yet implemented in core .as(typeToRead);
String collection = "_default._default";// method.getEntityInformation().getCollectionName(); // not yet implemented
ReactiveCouchbaseQueryExecution execution = getExecution(accessor,
new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find);
return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
}
/**
* Returns the execution instance to use.
*
* @param accessor must not be {@literal null}.
* @param resultProcessing must not be {@literal null}.
* @return
*/
private ReactiveCouchbaseQueryExecution getExecution(ParameterAccessor accessor,
Converter<Object, Object> resultProcessing, ReactiveFindByQueryOperation.FindByQueryWithQuery<?> operation) {
return new ResultProcessingExecution(getExecutionToWrap(accessor, operation), resultProcessing);
}
/**
* Returns the execution to wrap
*
* @param accessor must not be {@literal null}.
* @param operation must not be {@literal null}.
* @return
*/
private ReactiveCouchbaseQueryExecution getExecutionToWrap(ParameterAccessor accessor,
ReactiveFindByQueryOperation.FindByQueryWithQuery<?> 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()
} else if (getQueryMethod().isCollectionQuery()) {
return (q, t, c) -> operation.matching(q.with(accessor.getPageable())).all();
} else if (isCountQuery()) {
return (q, t, c) -> operation.matching(q).count();
} else if (isExistsQuery()) {
return (q, t, c) -> operation.matching(q).exists();
} else {
return (q, t, c) -> {
ReactiveFindByQueryOperation.TerminatingFindByQuery<?> find = operation.matching(q);
return isLimiting() ? find.first() : find.one();
};
}
}
/**
* Apply Meta annotation to query
*
* @param query must not be {@literal null}.
* @return Query
*/
Query applyQueryMetaAttributesWhenPresent(Query query) {
if (getQueryMethod().hasQueryMetaAttributes()) {
query.setMeta(getQueryMethod().getQueryMetaAttributes());
}
return query;
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 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 java.util.List;
import org.springframework.core.convert.converter.Converter;
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.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.util.Assert;
/**
* Set of classes to contain query execution strategies. Depending (mostly) on the return type of a
* {@link org.springframework.data.repository.query.QueryMethod} a {@link Query} can be executed in various flavors.
*
* @author Michael Reiche
* @since 4.1
*/
@FunctionalInterface
interface CouchbaseQueryExecution {
Object execute(Query query, Class<?> type, String collection);
/**
* {@link CouchbaseQueryExecution} removing documents matching the query.
*/
final class DeleteExecution implements CouchbaseQueryExecution {
private final CouchbaseOperations operations;
private final QueryMethod method;
public DeleteExecution(CouchbaseOperations operations, QueryMethod method) {
this.operations = operations;
this.method = method;
}
/*
* (non-Javadoc)
* @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) {
return operations.removeByQuery(type).matching(query).all();
}
}
/**
* An {@link ReactiveCouchbaseQueryExecution} that wraps the results of the given delegate with the given result
* processing.
*/
final class ResultProcessingExecution implements CouchbaseQueryExecution {
private final CouchbaseQueryExecution delegate;
private final Converter<Object, Object> converter;
public ResultProcessingExecution(CouchbaseQueryExecution delegate, Converter<Object, Object> converter) {
Assert.notNull(delegate, "Delegate must not be null!");
Assert.notNull(converter, "Converter must not be null!");
this.delegate = delegate;
this.converter = converter;
}
@Override
public Object execute(Query query, Class<?> type, String collection) {
return converter.convert(delegate.execute(query, type, collection));
}
}
/**
* {@link CouchbaseQueryExecution} for {@link Slice} query methods.
*/
final class SlicedExecution implements CouchbaseQueryExecution {
private final ExecutableFindByQueryOperation.ExecutableFindByQuery<?> find;
private final Pageable pageable;
public SlicedExecution(ExecutableFindByQueryOperation.ExecutableFindByQuery find, Pageable pageable) {
Assert.notNull(find, "Find must not be null!");
Assert.notNull(pageable, "Pageable must not be null!");
this.find = find;
this.pageable = pageable;
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.CouchbaseQueryExecution#execute(org.springframework.data.couchbase.core.query.Query)
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Object execute(Query query, Class<?> type, String collection) {
int pageSize = pageable.getPageSize();
// Apply Pageable but tweak limit to peek into next page
Query modifiedQuery = query.with(pageable).limit(pageSize + 1);
List result = find.matching(modifiedQuery).all();
boolean hasNext = result.size() > pageSize;
return new SliceImpl<Object>(hasNext ? result.subList(0, pageSize) : result, pageable, hasNext);
}
}
/**
* {@link CouchbaseQueryExecution} for pagination queries.
*/
final class PagedExecution<FindWithQuery> implements CouchbaseQueryExecution {
private final ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation;
private final Pageable pageable;
public PagedExecution(ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation, Pageable pageable) {
Assert.notNull(operation, "Operation must not be null!");
Assert.notNull(pageable, "Pageable must not be null!");
this.operation = operation;
this.pageable = pageable;
}
/*
* (non-Javadoc)
* @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) {
int overallLimit = 0; // query.getLimit();
ExecutableFindByQueryOperation.TerminatingFindByQuery<?> matching = operation.matching(query);
// Apply raw pagination
query.with(pageable);
// Adjust limit if page would exceed the overall limit
if (overallLimit != 0 && pageable.getOffset() + pageable.getPageSize() > overallLimit) {
query.limit((int) (overallLimit - pageable.getOffset()));
}
return PageableExecutionUtils.getPage(matching.all(), pageable, () -> {
long count = operation.matching(query.skip(-1).limit(-1)).count();
return overallLimit != 0 ? Math.min(count, overallLimit) : count;
});
}
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.data.couchbase.repository.query;
import java.lang.reflect.Method;
import java.util.Locale;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
@@ -24,12 +25,17 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProper
import org.springframework.data.couchbase.core.query.Dimensional;
import org.springframework.data.couchbase.core.query.View;
import org.springframework.data.couchbase.core.query.WithConsistency;
import org.springframework.data.couchbase.repository.Meta;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.couchbase.repository.ScanConsistency;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.util.ReactiveWrapperConverters;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -39,6 +45,7 @@ import org.springframework.util.StringUtils;
* @author Michael Nitschinger
* @author Simon Baslé
* @author Oliver Gierke
* @author Michael Reiche
*/
public class CouchbaseQueryMethod extends QueryMethod {
@@ -49,6 +56,7 @@ public class CouchbaseQueryMethod extends QueryMethod {
super(method, metadata, factory);
this.method = method;
}
/**
@@ -171,6 +179,38 @@ public class CouchbaseQueryMethod extends QueryMethod {
return method.getAnnotation(ScanConsistency.class);
}
/**
* @return return true if {@link Meta} annotation is available.
*/
public boolean hasQueryMetaAttributes() {
return getMetaAnnotation() != null;
}
/**
* @return return {@link Meta} annotation
*/
private Meta getMetaAnnotation() {
return method.getAnnotation(Meta.class);
}
/**
* Returns the {@link org.springframework.data.couchbase.core.query.Meta} attributes to be applied.
*
* @return never {@literal null}.
*/
@Nullable
public org.springframework.data.couchbase.core.query.Meta getQueryMetaAttributes() {
Meta meta = getMetaAnnotation();
if (meta == null) {
return new org.springframework.data.couchbase.core.query.Meta();
}
org.springframework.data.couchbase.core.query.Meta metaAttributes = new org.springframework.data.couchbase.core.query.Meta();
return metaAttributes;
}
/**
* Returns the query string declared in a {@link Query} annotation or {@literal null} if neither the annotation found
* nor the attribute was specified.
@@ -182,6 +222,24 @@ public class CouchbaseQueryMethod extends QueryMethod {
return StringUtils.hasText(query) ? query : null;
}
/**
* is this a 'delete'?
*
* @return is this a 'delete'?
*/
public boolean isDeleteQuery() {
return getName().toLowerCase(Locale.ROOT).startsWith("delete");
}
/**
* is this an 'exists' query?
*
* @return is this an 'exists' query?
*/
public boolean isExistsQuery() {
return getName().toLowerCase(Locale.ROOT).startsWith("exists");
}
/**
* indicates if the method begins with "count"
*
@@ -189,11 +247,21 @@ public class CouchbaseQueryMethod extends QueryMethod {
* all().
*/
public boolean isCountQuery() {
return getName().toLowerCase().startsWith("count");
return getName().toLowerCase(Locale.ROOT).startsWith("count");
}
@Override
public String toString() {
return super.toString();
}
public boolean hasReactiveWrapperParameter() {
for (Parameter p : getParameters()) {
if (ReactiveWrapperConverters.supports(p.getType())) {
return true;
}
}
return false;
}
}

View File

@@ -18,28 +18,35 @@ 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 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).execute(parameters);
return new N1qlRepositoryQueryExecutor(operations, queryMethod, namedQueries, evaluationContextProvider)
.execute(parameters);
}
@Override

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2015-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.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.SimplePropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.EntityInstantiator;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.util.Assert;
/**
* {@link Converter} to instantiate DTOs from fully equipped domain objects.
*
* @author Michael Reiche
* @since 4.1
*/
class DtoInstantiatingConverter implements Converter<Object, Object> {
private final Class<?> targetType;
private final MappingContext<? extends PersistentEntity<?, ?>, ? extends PersistentProperty<?>> context;
private final EntityInstantiator instantiator;
/**
* Creates a new {@link Converter} to instantiate DTOs.
*
* @param dtoType must not be {@literal null}.
* @param context must not be {@literal null}.
* @param entityInstantiators must not be {@literal null}.
*/
public DtoInstantiatingConverter(Class<?> dtoType,
MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> context,
EntityInstantiators entityInstantiators) {
Assert.notNull(dtoType, "DTO type must not be null!");
Assert.notNull(context, "MappingContext must not be null!");
Assert.notNull(entityInstantiators, "EntityInstantiators must not be null!");
this.targetType = dtoType;
this.context = context;
this.instantiator = entityInstantiators.getInstantiatorFor(context.getRequiredPersistentEntity(dtoType));
}
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public Object convert(Object source) {
if (targetType.isInterface()) {
return source;
}
final PersistentEntity<?, ?> sourceEntity = context.getRequiredPersistentEntity(source.getClass());
final PersistentPropertyAccessor sourceAccessor = sourceEntity.getPropertyAccessor(source);
final PersistentEntity<?, ?> targetEntity = context.getRequiredPersistentEntity(targetType);
final PreferredConstructor<?, ? extends PersistentProperty<?>> constructor = targetEntity
.getPersistenceConstructor();
@SuppressWarnings({ "rawtypes", "unchecked" })
Object dto = instantiator.createInstance(targetEntity, new ParameterValueProvider() {
@Override
public Object getParameterValue(Parameter parameter) {
return sourceAccessor.getProperty(sourceEntity.getPersistentProperty(parameter.getName().toString()));
}
});
final PersistentPropertyAccessor dtoAccessor = targetEntity.getPropertyAccessor(dto);
targetEntity.doWithProperties(new SimplePropertyHandler() {
@Override
public void doWithPersistentProperty(PersistentProperty<?> property) {
if (constructor.isConstructorParameter(property)) {
return;
}
dtoAccessor.setProperty(property,
sourceAccessor.getProperty(sourceEntity.getPersistentProperty(property.getName())));
}
});
return dto;
}
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.couchbase.repository.query;
import static org.springframework.data.couchbase.core.query.QueryCriteria.*;
import static org.springframework.data.couchbase.core.query.QueryCriteria.where;
import java.util.Iterator;

View File

@@ -15,33 +15,43 @@
*/
package org.springframework.data.couchbase.repository.query;
import com.couchbase.client.java.query.QueryScanConsistency;
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 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 }
*
@@ -52,25 +62,31 @@ public class N1qlRepositoryQueryExecutor {
final Class<?> domainClass = queryMethod.getResultProcessor().getReturnedType().getDomainType();
final ParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
// this is identical to ReactiveN1qlRespositoryQueryExecutor,
// except for the type of 'q', and the call to oneValue() vs one()
// counterpart to ReactiveN1qlRespositoryQueryExecutor,
Query query;
ExecutableFindByQueryOperation.ExecutableFindByQuery q;
if (queryMethod.hasN1qlAnnotation()) {
query = new StringN1qlQueryCreator(accessor, queryMethod, operations.getConverter(), operations.getBucketName(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries).createQuery();
SPEL_PARSER, evaluationContextProvider, namedQueries).createQuery();
} else {
final PartTree tree = new PartTree(queryMethod.getName(), domainClass);
query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter()).createQuery();
}
q = (ExecutableFindByQueryOperation.ExecutableFindByQuery) operations.findByQuery(domainClass)
.consistentWith(buildQueryScanConsistency()).matching(query);
// q = (ExecutableFindByQueryOperation.ExecutableFindByQuery) operations.findByQuery(domainClass)
// .consistentWith(buildQueryScanConsistency()).matching(query);
ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation = (ExecutableFindByQueryOperation.ExecutableFindByQuery<?>) operations
.findByQuery(domainClass).consistentWith(buildQueryScanConsistency());
if (queryMethod.isCountQuery()) {
return q.count();
return operation.matching(query).count();
} else if (queryMethod.isCollectionQuery()) {
return q.all();
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 q.oneValue();
return operation.matching(query).oneValue();
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 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.CouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* {@link RepositoryQuery} implementation for Couchbase. Replaces PartTreeN1qlBasedQuery
*
* @author Michael Reiche
* @since 4.1
*/
public class PartTreeCouchbaseQuery extends AbstractCouchbaseQuery {
private final PartTree tree;
private final CouchbaseConverter converter;
/**
* Creates a new {@link PartTreeCouchbaseQuery} from the given {@link QueryMethod} and {@link CouchbaseTemplate}.
*
* @param method must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
*/
public PartTreeCouchbaseQuery(CouchbaseQueryMethod method, CouchbaseOperations operations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, operations, expressionParser, evaluationContextProvider);
ResultProcessor processor = method.getResultProcessor();
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());
this.converter = operations.getConverter();
}
/**
* Return the {@link PartTree} backing the query.
*
* @return the tree
*/
public PartTree getTree() {
return tree;
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery#createQuery(org.springframework.data.couchbase.repository.query.ConvertingParameterAccessor, boolean)
*/
@Override
protected Query createQuery(ParametersParameterAccessor accessor) {
N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter);
Query query = creator.createQuery();
if (tree.isLimiting()) {
query.limit(tree.getMaxResults());
}
return query;
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery#createCountQuery(org.springframework.data.couchbase.repository.query.ConvertingParameterAccessor)
*/
@Override
protected Query createCountQuery(ParametersParameterAccessor accessor) {
return new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter).createQuery();
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery#isCountQuery()
*/
@Override
protected boolean isCountQuery() {
return tree.isCountProjection();
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery#isExistsQuery()
*/
@Override
protected boolean isExistsQuery() {
return tree.isExistsProjection();
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery#isDeleteQuery()
*/
@Override
protected boolean isDeleteQuery() {
return tree.isDelete();
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery#isLimiting()
*/
@Override
protected boolean isLimiting() {
return tree.isLimiting();
}
}

View File

@@ -16,8 +16,12 @@
package org.springframework.data.couchbase.repository.query;
import static org.springframework.data.couchbase.core.query.N1QLExpression.*;
import static org.springframework.data.couchbase.repository.query.support.N1qlUtils.*;
import static org.springframework.data.couchbase.core.query.N1QLExpression.count;
import static org.springframework.data.couchbase.core.query.N1QLExpression.delete;
import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
import static org.springframework.data.couchbase.core.query.N1QLExpression.select;
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
import static org.springframework.data.couchbase.repository.query.support.N1qlUtils.createReturningExpressionForDelete;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.query.N1QLExpression;
@@ -37,7 +41,9 @@ import com.couchbase.client.java.json.JsonValue;
* @author Simon Baslé
* @author Subhashni Balakrishnan
* @author Mark Paluch
* @author Michael Reiche
*/
@Deprecated
public class PartTreeN1qlBasedQuery extends AbstractN1qlBasedQuery {
private final PartTree partTree;
@@ -46,6 +52,7 @@ public class PartTreeN1qlBasedQuery extends AbstractN1qlBasedQuery {
public PartTreeN1qlBasedQuery(CouchbaseQueryMethod queryMethod, CouchbaseOperations couchbaseOperations) {
super(queryMethod, couchbaseOperations);
this.partTree = new PartTree(queryMethod.getName(), queryMethod.getEntityInformation().getJavaType());
throw new RuntimeException("Deprecated");
}
@Override

View File

@@ -0,0 +1,82 @@
/*
* Copyright 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.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.util.Assert;
/**
* Set of classes to contain query execution strategies. Depending (mostly) on the return type of a
* {@link org.springframework.data.repository.query.QueryMethod} a {@link AbstractReactiveCouchbaseQuery} can be
* executed in various flavors.
*
* @author Michael Reiche
* @since 4.1
*/
interface ReactiveCouchbaseQueryExecution {
Object execute(Query query, Class<?> type, String collection);
/**
* {@link ReactiveCouchbaseQueryExecution} removing documents matching the query.
*/
final class DeleteExecution implements ReactiveCouchbaseQueryExecution {
private final ReactiveCouchbaseOperations operations;
private final CouchbaseQueryMethod method;
public DeleteExecution(ReactiveCouchbaseOperations operations, CouchbaseQueryMethod method) {
this.operations = operations;
this.method = method;
}
/*
* (non-Javadoc)
* @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) {
return operations.removeByQuery(type)/*.inCollection(collection)*/.matching(query).all();
}
}
/**
* An {@link ReactiveCouchbaseQueryExecution} that wraps the results of the given delegate with the given result
* processing.
*/
final class ResultProcessingExecution implements ReactiveCouchbaseQueryExecution {
private final ReactiveCouchbaseQueryExecution delegate;
private final Converter<Object, Object> converter;
public ResultProcessingExecution(ReactiveCouchbaseQueryExecution delegate, Converter<Object, Object> converter) {
Assert.notNull(delegate, "Delegate must not be null!");
Assert.notNull(converter, "Converter must not be null!");
this.delegate = delegate;
this.converter = converter;
}
@Override
public Object execute(Query query, Class<?> type, String collection) {
return converter.convert(delegate.execute(query, type, collection));
}
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright 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.repository.util.ClassUtils.hasParameterOfType;
import java.lang.reflect.Method;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.util.ReactiveWrappers;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.ClassUtils;
/**
* Reactive specific implementation of {@link CouchbaseQueryMethod}.
*
* @author Michael Reiche
* @since 4.1
*/
public class ReactiveCouchbaseQueryMethod extends CouchbaseQueryMethod {
private static final ClassTypeInformation<Page> PAGE_TYPE = ClassTypeInformation.from(Page.class);
private static final ClassTypeInformation<Slice> SLICE_TYPE = ClassTypeInformation.from(Slice.class);
private final Method method;
private final Lazy<Boolean> isCollectionQueryCouchbase; // not to be confused with QueryMethod.isCollectionQuery
/**
* Creates a new {@link ReactiveCouchbaseQueryMethod} from the given {@link Method}.
*
* @param method must not be {@literal null}.
* @param metadata must not be {@literal null}.
* @param projectionFactory must not be {@literal null}.
* @param mappingContext must not be {@literal null}.
*/
public ReactiveCouchbaseQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory projectionFactory,
MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext) {
super(method, metadata, projectionFactory, mappingContext);
if (hasParameterOfType(method, Pageable.class)) {
TypeInformation<?> returnType = ClassTypeInformation.fromReturnTypeOf(method);
boolean multiWrapper = ReactiveWrappers.isMultiValueType(returnType.getType());
boolean singleWrapperWithWrappedPageableResult = ReactiveWrappers.isSingleValueType(returnType.getType())
&& (PAGE_TYPE.isAssignableFrom(returnType.getRequiredComponentType())
|| SLICE_TYPE.isAssignableFrom(returnType.getRequiredComponentType()));
if (singleWrapperWithWrappedPageableResult) {
throw new InvalidDataAccessApiUsageException(
String.format("'%s.%s' must not use sliced or paged execution. Please use Flux.buffer(size, skip).",
ClassUtils.getShortName(method.getDeclaringClass()), method.getName()));
}
if (!multiWrapper) {
throw new IllegalStateException(String.format(
"Method has to use a either multi-item reactive wrapper return type or a wrapped Page/Slice type. Offending method: %s",
method.toString()));
}
if (hasParameterOfType(method, Sort.class)) {
throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameter. "
+ "Use sorting capabilities on Pageable instead! Offending method: %s", method.toString()));
}
}
this.method = method;
this.isCollectionQueryCouchbase = Lazy.of(() -> {
boolean result = !(isPageQuery() || isSliceQuery())
&& ReactiveWrappers.isMultiValueType(metadata.getReturnType(method).getType());
return result;
});
}
/*
* All reactive query methods are streaming queries.
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryMethod#isStreamQuery()
*/
@Override
public boolean isStreamQuery() {
return true;
}
/*
* does this query return a collection?
* This must override QueryMethod.isCollection() as isCollectionQueryCouchbase is different from isCollectionQuery
*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryMethod#isCollection()
*/
@Override
public boolean isCollectionQuery() {
return (Boolean) this.isCollectionQueryCouchbase.get();
}
}

View File

@@ -16,34 +16,60 @@
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.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
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
*/
public class ReactiveCouchbaseRepositoryQuery implements RepositoryQuery {
@Deprecated
public class ReactiveCouchbaseRepositoryQuery extends AbstractReactiveCouchbaseQuery {
private final ReactiveCouchbaseOperations operations;
private final CouchbaseQueryMethod queryMethod;
private final ReactiveCouchbaseQueryMethod queryMethod;
private final NamedQueries namedQueries;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
public ReactiveCouchbaseRepositoryQuery(final ReactiveCouchbaseOperations operations,
final CouchbaseQueryMethod queryMethod, final NamedQueries namedQueries) {
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).execute(parameters);
return new ReactiveN1qlRepositoryQueryExecutor(operations, queryMethod, namedQueries, evaluationContextProvider)
.execute(parameters);
}
@Override
public QueryMethod getQueryMethod() {
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;
}

View File

@@ -15,36 +15,32 @@
*/
package org.springframework.data.couchbase.repository.query;
import com.couchbase.client.java.query.QueryScanConsistency;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation;
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperation;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.query.*;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import reactor.core.publisher.Flux;
import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.repository.query.parser.PartTree;
import java.util.List;
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 CouchbaseQueryMethod queryMethod;
private final ReactiveCouchbaseQueryMethod queryMethod;
private final NamedQueries namedQueries;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
public ReactiveN1qlRepositoryQueryExecutor(final ReactiveCouchbaseOperations operations,
final CouchbaseQueryMethod queryMethod, final NamedQueries namedQueries) {
final ReactiveCouchbaseQueryMethod queryMethod, final NamedQueries namedQueries,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
this.operations = operations;
this.queryMethod = queryMethod;
this.namedQueries = namedQueries;
this.evaluationContextProvider = evaluationContextProvider;
throw new RuntimeException("Deprecated");
}
/**
@@ -54,41 +50,16 @@ public class ReactiveN1qlRepositoryQueryExecutor {
* @return
*/
public Object execute(final Object[] parameters) {
final Class<?> domainClass = queryMethod.getResultProcessor().getReturnedType().getDomainType();
final ParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
final String namedQueryName = queryMethod.getNamedQueryName();
// counterpart to N1qlRespositoryQueryExecutor,
// this is identical to ExecutableN1qlRespositoryQueryExecutor,
// except for the type of 'q', and the call to one() vs oneValue()
Query query;
ReactiveFindByQueryOperation.ReactiveFindByQuery q;
if (queryMethod.hasN1qlAnnotation()) {
query = new StringN1qlQueryCreator(accessor, queryMethod, operations.getConverter(), operations.getBucketName(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries).createQuery();
return new ReactiveStringBasedCouchbaseQuery(queryMethod, operations, new SpelExpressionParser(),
evaluationContextProvider, namedQueries).execute(parameters);
} else {
final PartTree tree = new PartTree(queryMethod.getName(), domainClass);
query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter()).createQuery();
return new ReactivePartTreeCouchbaseQuery(queryMethod, operations, new SpelExpressionParser(),
evaluationContextProvider).execute(parameters);
}
q = (ReactiveFindByQueryOperation.ReactiveFindByQuery) operations.findByQuery(domainClass)
.consistentWith(buildQueryScanConsistency()).matching(query);
if (queryMethod.isCountQuery()) {
return q.count();
} else if (queryMethod.isCollectionQuery()) {
return q.all();
} else {
return q.one();
}
}
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;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Reactive PartTree {@link RepositoryQuery} implementation for Couchbase. Replaces ReactivePartN1qlBasedQuery
*
* @author Michael Reiche
* @since 4.1
*/
public class ReactivePartTreeCouchbaseQuery extends AbstractReactiveCouchbaseQuery {
private final PartTree tree;
private final CouchbaseConverter converter;
private static final Logger LOG = LoggerFactory.getLogger(ReactivePartTreeCouchbaseQuery.class);
/**
* Creates a new {@link ReactivePartTreeCouchbaseQuery} from the given {@link QueryMethod} and
* {@link CouchbaseTemplate}.
*
* @param method must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
*/
public ReactivePartTreeCouchbaseQuery(ReactiveCouchbaseQueryMethod method, ReactiveCouchbaseOperations operations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, operations, expressionParser, evaluationContextProvider);
this.tree = new PartTree(method.getName(), method.getResultProcessor().getReturnedType().getDomainType());
this.converter = operations.getConverter();
}
/**
* Return the {@link PartTree} backing the query.
*
* @return the tree
*/
public PartTree getTree() {
return tree;
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbease.repository.query.AbstractCouchbaseQuery#createQuery(org.springframework.data.couchbase.repository.query.ConvertingParameterAccessor, boolean)
*/
@Override
protected Query createQuery(ParametersParameterAccessor accessor) {
N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter);
Query query = creator.createQuery();
if (tree.isLimiting()) {
query.limit(tree.getMaxResults());
}
if (LOG.isDebugEnabled()) {
LOG.debug("Created query {} for * fields.", query.export());
}
return query;
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractReactiveCouchbaseQuery#createCountQuery(org.springframework.data.couchbase.repository.query.ConvertingParameterAccessor)
*/
@Override
protected Query createCountQuery(ParametersParameterAccessor accessor) {
Query query = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter).createQuery();
if (LOG.isDebugEnabled()) {
LOG.debug("Created query {} for * fields.", query.export());
}
return query;
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractReactiveCouchbaseQuery#isLimiting()
*/
@Override
protected boolean isLimiting() {
return tree.isLimiting();
}
}

View File

@@ -15,7 +15,9 @@
*/
package org.springframework.data.couchbase.repository.query;
import static org.springframework.data.couchbase.core.query.N1QLExpression.*;
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;
@@ -31,8 +33,11 @@ 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;
@@ -41,6 +46,7 @@ public class ReactivePartTreeN1qlBasedQuery extends ReactiveAbstractN1qlBasedQue
public ReactivePartTreeN1qlBasedQuery(CouchbaseQueryMethod queryMethod, ReactiveCouchbaseOperations operations) {
super(queryMethod, operations);
this.partTree = new PartTree(queryMethod.getName(), queryMethod.getEntityInformation().getJavaType());
throw new RuntimeException("deprecated");
}
@Override

View File

@@ -0,0 +1,105 @@
/*
* Copyright 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
import org.springframework.util.Assert;
/**
* Query to use a plain JSON String to create the {@link Query} to actually execute.
*
* @author Michael Reiche
* @since 4.1
*/
public class ReactiveStringBasedCouchbaseQuery extends AbstractReactiveCouchbaseQuery {
private static final String COUNT_EXISTS_AND_DELETE = "Manually defined query for %s cannot be a count and exists or delete query at the same time!";
private static final Logger LOG = LoggerFactory.getLogger(ReactiveStringBasedCouchbaseQuery.class);
private final SpelExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
private final NamedQueries namedQueries;
/**
* Creates a new {@link ReactiveStringBasedCouchbaseQuery} for the given {@link String}, {@link CouchbaseQueryMethod},
* {@link ReactiveCouchbaseOperations}, {@link SpelExpressionParser} and {@link QueryMethodEvaluationContextProvider}.
*
* @param method must not be {@literal null}.
* @param couchbaseOperations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
* @param namedQueries must not be {@literal null}.
*/
public ReactiveStringBasedCouchbaseQuery(ReactiveCouchbaseQueryMethod method,
ReactiveCouchbaseOperations couchbaseOperations, SpelExpressionParser expressionParser,
QueryMethodEvaluationContextProvider evaluationContextProvider, NamedQueries namedQueries) {
super(method, couchbaseOperations, expressionParser, evaluationContextProvider);
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");
this.expressionParser = expressionParser;
this.evaluationContextProvider = evaluationContextProvider;
if (hasAmbiguousProjectionFlags(isCountQuery(), isExistsQuery(), isDeleteQuery())) {
throw new IllegalArgumentException(String.format(COUNT_EXISTS_AND_DELETE, method));
}
this.namedQueries = namedQueries;
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractReactiveCouchbaseQuery#createQuery(org.springframework.data.couchbase.repository.query.ConvertingParameterAccessor)
*/
@Override
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()) {
LOG.debug("Created query " + query.export());
}
return query;
}
@Override
protected Query createCountQuery(ParametersParameterAccessor accessor) {
return applyQueryMetaAttributesWhenPresent(createQuery(accessor));
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractReactiveCouchbaseQuery#isLimiting()
*/
@Override
protected boolean isLimiting() {
return false; // not yet implemented
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 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.reactivestreams.Publisher;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
final class ResultProcessingConverter<CouchbaseOperationsType> implements Converter<Object, Object> {
private final ResultProcessor processor;
private final CouchbaseOperationsType operations;
private final EntityInstantiators instantiators;
public ResultProcessingConverter(ResultProcessor processor, CouchbaseOperationsType operations,
EntityInstantiators instantiators) {
Assert.notNull(processor, "Processor must not be null!");
Assert.notNull(operations, "Operations must not be null!");
Assert.notNull(instantiators, "Instantiators must not be null!");
this.processor = processor;
this.operations = operations;
this.instantiators = instantiators;
}
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Override
public Object convert(Object source) {
ReturnedType returnedType = processor.getReturnedType();
if (isVoid(returnedType)) {
if (source instanceof Mono) {
return ((Mono<?>) source).then();
}
if (source instanceof Publisher) {
return Flux.from((Publisher<?>) source).then();
}
}
if (ClassUtils.isPrimitiveOrWrapper(returnedType.getReturnedType())) {
return source;
}
CouchbaseConverter cvtr = operations instanceof CouchbaseOperations
? ((CouchbaseOperations) operations).getConverter()
: ((ReactiveCouchbaseOperations) operations).getConverter();
if (!cvtr.getMappingContext().hasPersistentEntityFor(returnedType.getReturnedType())) {
return source;
}
Converter<Object, Object> converter = new DtoInstantiatingConverter(returnedType.getReturnedType(),
cvtr.getMappingContext(), instantiators);
return processor.processResult(source, converter);
}
static boolean isVoid(ReturnedType returnedType) {
return returnedType.getReturnedType().equals(Void.class);
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.CouchbaseOperations;
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;
import org.springframework.util.Assert;
/**
* Query to use a plain JSON String to create the {@link Query} to actually execute.
*
* @author Michael Reiche
* @since 4.1
*/
public class StringBasedCouchbaseQuery extends AbstractCouchbaseQuery {
private static final String COUNT_EXISTS_AND_DELETE = "Manually defined query for %s cannot be a count and exists or delete query at the same time!";
private static final Logger LOG = LoggerFactory.getLogger(StringBasedCouchbaseQuery.class);
private final SpelExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
private final NamedQueries namedQueries;
/**
* Creates a new {@link StringBasedCouchbaseQuery} for the given {@link String}, {@link CouchbaseQueryMethod},
* {@link CouchbaseOperations}, {@link SpelExpressionParser} and {@link QueryMethodEvaluationContextProvider}.
*
* @param method must not be {@literal null}.
* @param couchbaseOperations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
* @param namedQueries must not be {@literal null}.
*/
public StringBasedCouchbaseQuery(CouchbaseQueryMethod method, CouchbaseOperations couchbaseOperations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider,
NamedQueries namedQueries) {
super(method, couchbaseOperations, expressionParser, evaluationContextProvider);
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");
this.expressionParser = expressionParser;
this.evaluationContextProvider = evaluationContextProvider;
if (hasAmbiguousProjectionFlags(isCountQuery(), isExistsQuery(), isDeleteQuery())) {
throw new IllegalArgumentException(String.format(COUNT_EXISTS_AND_DELETE, method));
}
this.namedQueries = namedQueries;
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery#createQuery(org.springframework.data.couchbase.repository.query.ConvertingParameterAccessor)
*/
@Override
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()) {
LOG.debug("Created query " + query.export());
}
return query;
}
@Override
protected Query createCountQuery(ParametersParameterAccessor accessor) {
return applyQueryMetaAttributesWhenPresent(createQuery(accessor));
}
/*
* (non-Javadoc)
* @see org.springframework.data.couchbase.repository.query.AbstractCouchbaseQuery#isLimiting()
*/
@Override
protected boolean isLimiting() {
return false; // not yet implemented
}
}

View File

@@ -15,8 +15,10 @@
*/
package org.springframework.data.couchbase.repository.query;
import static org.springframework.data.couchbase.core.query.N1QLExpression.*;
import static org.springframework.data.couchbase.core.support.TemplateUtils.*;
import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_CAS;
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_ID;
import java.util.ArrayList;
import java.util.Collection;
@@ -25,7 +27,6 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.couchbase.client.core.error.InvalidArgumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
@@ -42,11 +43,12 @@ import org.springframework.data.repository.query.ReturnedType;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.Assert;
import com.couchbase.client.core.error.InvalidArgumentException;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.json.JsonValue;
import org.springframework.util.Assert;
/**
* @author Subhashni Balakrishnan

View File

@@ -15,12 +15,6 @@
*/
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.ArrayList;
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;
@@ -30,19 +24,21 @@ 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.couchbase.core.query.StringQuery;
import org.springframework.data.couchbase.repository.query.support.N1qlUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.query.*;
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.parser.AbstractQueryCreator;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.Assert;
import java.util.Iterator;
import static org.springframework.data.couchbase.core.query.QueryCriteria.where;
/**
* @author Michael Reiche
@@ -56,11 +52,10 @@ public class StringN1qlQueryCreator extends AbstractQueryCreator<Query, QueryCri
private final StringBasedN1qlQueryParser queryParser;
private final QueryMethod queryMethod;
private final CouchbaseConverter couchbaseConverter;
private static final SpelExpressionParser SPEL_PARSER = new SpelExpressionParser();
private final N1QLExpression parsedExpression;
public StringN1qlQueryCreator(final ParameterAccessor accessor, CouchbaseQueryMethod queryMethod,
CouchbaseConverter couchbaseConverter, String bucketName,
CouchbaseConverter couchbaseConverter, String bucketName, SpelExpressionParser spelExpressionParser,
QueryMethodEvaluationContextProvider evaluationContextProvider, NamedQueries namedQueries) {
// AbstractQueryCreator needs a PartTree, so we give it a dummy one.
@@ -84,8 +79,8 @@ public class StringN1qlQueryCreator extends AbstractQueryCreator<Query, QueryCri
throw new IllegalArgumentException("query has no inline Query or named Query not found");
}
this.queryParser = new StringBasedN1qlQueryParser(queryString, queryMethod, bucketName, couchbaseConverter,
getTypeField(), getTypeValue(), accessor, SPEL_PARSER, evaluationContextProvider);
this.parser = SPEL_PARSER;
getTypeField(), getTypeValue(), accessor, spelExpressionParser, evaluationContextProvider);
this.parser = spelExpressionParser;
this.parsedExpression = this.queryParser.parsedExpression;
}
@@ -139,8 +134,8 @@ public class StringN1qlQueryCreator extends AbstractQueryCreator<Query, QueryCri
return q;
}
private QueryCriteria from(final Part part, final CouchbasePersistentProperty property,
final QueryCriteria criteria, final Iterator<Object> parameters) {
private QueryCriteria from(final Part part, final CouchbasePersistentProperty property, final QueryCriteria criteria,
final Iterator<Object> parameters) {
final Part.Type type = part.getType();
switch (type) {

View File

@@ -26,7 +26,8 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProper
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod;
import org.springframework.data.couchbase.repository.query.CouchbaseRepositoryQuery;
import org.springframework.data.couchbase.repository.query.PartTreeCouchbaseQuery;
import org.springframework.data.couchbase.repository.query.StringBasedCouchbaseQuery;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
@@ -156,25 +157,14 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
metadata.getRepositoryInterface(), metadata.getDomainType());
CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, factory, mappingContext);
return new CouchbaseRepositoryQuery(couchbaseOperations, queryMethod, namedQueries);
/*CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, factory, mappingContext);
String namedQueryName = queryMethod.getNamedQueryName();
if (queryMethod.hasN1qlAnnotation()) {
if (queryMethod.hasInlineN1qlQuery()) {
return new StringN1qlBasedQuery(queryMethod.getInlineN1qlQuery(), queryMethod, couchbaseOperations,
SPEL_PARSER, evaluationContextProvider);
} else if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName);
return new StringN1qlBasedQuery(namedQuery, queryMethod, couchbaseOperations, SPEL_PARSER,
evaluationContextProvider);
}
return new StringBasedCouchbaseQuery(queryMethod, couchbaseOperations, new SpelExpressionParser(),
evaluationContextProvider, namedQueries);
} else {
return new PartTreeCouchbaseQuery(queryMethod, couchbaseOperations, new SpelExpressionParser(),
evaluationContextProvider);
}
return new PartTreeN1qlBasedQuery(queryMethod, couchbaseOperations);*/
}
}

View File

@@ -24,8 +24,9 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod;
import org.springframework.data.couchbase.repository.query.ReactiveCouchbaseRepositoryQuery;
import org.springframework.data.couchbase.repository.query.ReactiveCouchbaseQueryMethod;
import org.springframework.data.couchbase.repository.query.ReactivePartTreeCouchbaseQuery;
import org.springframework.data.couchbase.repository.query.ReactiveStringBasedCouchbaseQuery;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
@@ -151,8 +152,16 @@ public class ReactiveCouchbaseRepositoryFactory extends ReactiveRepositoryFactor
NamedQueries namedQueries) {
final ReactiveCouchbaseOperations couchbaseOperations = couchbaseOperationsMapping.resolve(
metadata.getRepositoryInterface(), metadata.getDomainType());
return new ReactiveCouchbaseRepositoryQuery(couchbaseOperations,
new CouchbaseQueryMethod(method, metadata, factory, mappingContext), namedQueries);
ReactiveCouchbaseQueryMethod queryMethod = new ReactiveCouchbaseQueryMethod(method, metadata, factory,
mappingContext);
if (queryMethod.hasN1qlAnnotation()) {
return new ReactiveStringBasedCouchbaseQuery(queryMethod, couchbaseOperations, new SpelExpressionParser(),
evaluationContextProvider, namedQueries);
} else {
return new ReactivePartTreeCouchbaseQuery(queryMethod, couchbaseOperations, new SpelExpressionParser(),
evaluationContextProvider);
}
}
}

View File

@@ -16,7 +16,7 @@
package org.springframework.data.couchbase.repository.support;
import static org.springframework.data.couchbase.repository.support.Util.*;
import static org.springframework.data.couchbase.repository.support.Util.hasNonZeroVersionProperty;
import java.util.Collection;
import java.util.List;

View File

@@ -16,8 +16,12 @@
package org.springframework.data.couchbase.core;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.data.couchbase.config.BeanNames.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
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.io.IOException;
import java.time.Instant;
@@ -99,7 +103,6 @@ class CouchbaseTemplateQueryIntegrationTests extends ClusterAwareIntegrationTest
.consistentWith(QueryScanConsistency.REQUEST_PLUS).all();
for (User u : foundUsers) {
System.out.println(u);
if (!(u.equals(user1) || u.equals(user2))) {
// somebody didn't clean up after themselves.
couchbaseTemplate.removeById().one(u.getId());
@@ -177,8 +180,7 @@ class CouchbaseTemplateQueryIntegrationTests extends ClusterAwareIntegrationTest
Query nonSpecialUsers = new Query(QueryCriteria.where("firstname").notLike("special"));
couchbaseTemplate.removeByQuery(User.class).consistentWith(QueryScanConsistency.REQUEST_PLUS)
.matching(nonSpecialUsers)
.all();
.matching(nonSpecialUsers).all();
assertNull(couchbaseTemplate.findById(User.class).one(user1.getId()));
assertNull(couchbaseTemplate.findById(User.class).one(user2.getId()));

View File

@@ -20,6 +20,12 @@ import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.couchbase.core.mapping.Document;
/**
* Airport entity
*
* @author Michael Nitschinger
* @author Michael Reiche
*/
@Document
public class Airport {
@Id String id;

View File

@@ -18,13 +18,16 @@ package org.springframework.data.couchbase.domain;
import java.util.List;
import com.couchbase.client.java.query.QueryScanConsistency;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.couchbase.repository.ScanConsistency;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* template class for Reactive Couchbase operations
*
@@ -64,11 +67,12 @@ public interface AirportRepository extends PagingAndSortingRepository<Airport, S
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
long count();
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} " +
" #{#projectIds != null ? 'AND iata IN $1' : ''} " +
" #{#planIds != null ? 'AND icao IN $2' : ''} " +
" #{#active != null ? 'AND false = $3' : ''} ")
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} #{#projectIds != null ? 'AND iata IN $1' : ''} "
+ " #{#planIds != null ? 'AND icao IN $2' : ''} #{#active != null ? 'AND false = $3' : ''} ")
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Long countFancyExpression(@Param("projectIds") List<String> projectIds, @Param("planIds") List<String> planIds, @Param("active") Boolean active);
Long countFancyExpression(@Param("projectIds") List<String> projectIds, @Param("planIds") List<String> planIds,
@Param("active") Boolean active);
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Page<Airport> findAllByIataNot(String iata, Pageable pageable);
}

View File

@@ -16,31 +16,29 @@
package org.springframework.data.couchbase.domain;
import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.ClusterOptions;
import com.couchbase.client.java.env.ClusterEnvironment;
import java.lang.reflect.InvocationTargetException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CouchbaseCustomConversions;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider;
import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.repository.config.ReactiveRepositoryOperationsMapping;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
import org.springframework.data.util.TypeInformation;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import com.couchbase.client.core.deps.com.fasterxml.jackson.databind.DeserializationFeature;
import com.couchbase.client.java.json.JacksonTransformers;
/**
* @author Michael Nitschinger
@@ -190,6 +188,17 @@ public class Config extends AbstractCouchbaseConfiguration {
return converter;
}
@Override
@Bean(name = "couchbaseTranslationService")
public TranslationService couchbaseTranslationService() {
final JacksonTranslationService jacksonTranslationService = new JacksonTranslationService();
jacksonTranslationService.afterPropertiesSet();
// for sdk3, we need to ask the mapper _it_ uses to ignore extra fields...
JacksonTransformers.MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return jacksonTranslationService;
}
@Override
public String typeKey() {
return "t"; // this will override '_class', is passed in to new CustomMappingCouchbaseConverter

View File

@@ -16,13 +16,19 @@
package org.springframework.data.couchbase.domain;
import org.springframework.data.couchbase.repository.Query;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.ArrayList;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.couchbase.repository.ScanConsistency;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Mono;
import com.couchbase.client.java.query.QueryScanConsistency;
@@ -62,4 +68,25 @@ public interface ReactiveAirportRepository extends ReactiveSortingRepository<Air
@Override
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Mono<Airport> findById(String var1);
// use parameter type PageRequest instead of Pageable. Pageable requires a return type of Page<>
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Flux<Airport> findAllByIataLike(String iata, final PageRequest page);
// use parameter type PageRequest instead of Pageable. Pageable requires a return type of Page<>
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Flux<Airport> findAllByIataLike(String iata);
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Mono<Airport> findByIata(String iata);
// This is not efficient. See findAllByIataLike for efficient reactive paging
default public Mono<Page<Airport>> findAllAirportsPaged(Pageable pageable) {
return count().flatMap(airportCount -> {
return findAll(pageable.getSort())
.buffer(pageable.getPageSize(), (pageable.getPageNumber() * pageable.getPageSize()))
.elementAt(pageable.getPageNumber(), new ArrayList<>())
.map(airports -> new PageImpl<Airport>(airports, pageable, airportCount));
});
}
}

View File

@@ -40,17 +40,19 @@ import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.domain.Address;
import org.springframework.data.couchbase.domain.Airport;
import org.springframework.data.couchbase.domain.AirportRepository;
import org.springframework.data.couchbase.domain.ReactiveUserRepository;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserRepository;
import org.springframework.data.couchbase.domain.Person;
import org.springframework.data.couchbase.domain.PersonRepository;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserRepository;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.springframework.data.util.StreamUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.couchbase.client.core.error.IndexExistsException;
@@ -141,13 +143,16 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
Airport vie = null;
try {
vie = new Airport("airports::vie", "vie", "loww");
airportRepository.save(vie);
vie = airportRepository.save(vie);
List<Airport> airports = airportRepository.findAllByIata("vie");
assertEquals(vie.getId(), airports.get(0).getId());
assertEquals(1, airports.size());
Airport airport1 = airportRepository.findById(airports.get(0).getId()).get();
assertEquals(airport1.getIata(), vie.getIata());
Airport airport2 = airportRepository.findByIata(airports.get(0).getIata());
assertEquals(airport1.getId(), vie.getId());
} finally {
airportRepository.delete(vie);
}
}
@Test
@@ -164,11 +169,8 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
@Test
void count() {
String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" };
Future[] future = new Future[iatas.length];
ExecutorService executorService = Executors.newFixedThreadPool(iatas.length);
try {
Callable<Boolean>[] suppliers = new Callable[iatas.length];
for (int i = 0; i < iatas.length; i++) {
Airport airport = new Airport("airports::" + iatas[i], iatas[i] /*iata*/,
iatas[i].toLowerCase(Locale.ROOT) /* lcao */);
@@ -178,6 +180,11 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
Long count = airportRepository.countFancyExpression(asList("JFK"), asList("jfk"), false);
assertEquals(1, count);
Pageable pageable = PageRequest.of(0, 2);
Page<Airport> aPage = airportRepository.findAllByIataNot("JFK", pageable);
assertEquals(iatas.length - 1, aPage.getTotalElements());
assertEquals(pageable.getPageSize(), aPage.getContent().size());
long airportCount = airportRepository.count();
assertEquals(7, airportCount);

View File

@@ -22,10 +22,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -49,6 +50,7 @@ import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.couchbase.client.core.error.IndexExistsException;
@@ -80,16 +82,21 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends ClusterAwa
@Test
void shouldSaveAndFindAll() {
Airport vie = null;
Airport jfk = null;
try {
vie = new Airport("airports::vie", "vie", "loww");
airportRepository.save(vie).block();
jfk = new Airport("airports::jfk", "JFK", "xxxx");
airportRepository.save(jfk).block();
List<Airport> all = airportRepository.findAll().toStream().collect(Collectors.toList());
assertFalse(all.isEmpty());
assertTrue(all.stream().anyMatch(a -> a.getId().equals("airports::vie")));
assertTrue(all.stream().anyMatch(a -> a.getId().equals("airports::jfk")));
} finally {
airportRepository.delete(vie).block();
airportRepository.delete(jfk).block();
}
}
@@ -103,6 +110,13 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends ClusterAwa
assertEquals(1, airports1.size());
List<Airport> airports2 = airportRepository.findAllByIata("vie").collectList().block();
assertEquals(1, airports2.size());
vie = airportRepository.save(vie).block();
List<Airport> airports = airportRepository.findAllByIata("vie").collectList().block();
assertEquals(1, airports.size());
Airport airport1 = airportRepository.findById(airports.get(0).getId()).block();
assertEquals(airport1.getIata(), vie.getIata());
Airport airport2 = airportRepository.findByIata(airports.get(0).getIata()).block();
assertEquals(airport1.getId(), vie.getId());
} finally {
airportRepository.delete(vie).block();
}
@@ -121,18 +135,35 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends ClusterAwa
@Test
void count() {
String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" };
Future[] future = new Future[iatas.length];
ExecutorService executorService = Executors.newFixedThreadPool(iatas.length);
Set<String> iatas = new HashSet();
iatas.add("JFK");
iatas.add("IAD");
iatas.add("SFO");
iatas.add("SJC");
iatas.add("SEA");
iatas.add("LAX");
iatas.add("PHX");
Future[] future = new Future[iatas.size()];
ExecutorService executorService = Executors.newFixedThreadPool(iatas.size());
try {
Callable<Boolean>[] suppliers = new Callable[iatas.length];
for (int i = 0; i < iatas.length; i++) {
Airport airport = new Airport("airports::" + iatas[i], iatas[i] /*iata*/, iatas[i].toLowerCase() /* lcao */);
Callable<Boolean>[] suppliers = new Callable[iatas.size()];
for (String iata : iatas) {
Airport airport = new Airport("airports::" + iata, iata, iata.toLowerCase() /* lcao */);
airportRepository.save(airport).block();
}
int page = 0;
airportRepository.findAllByIataLike("S%", PageRequest.of(page++, 2)).as(StepVerifier::create) //
.expectNextMatches(a -> {
return iatas.contains(a.getIata());
}).expectNextMatches(a -> iatas.contains(a.getIata())).verifyComplete();
airportRepository.findAllByIataLike("S%", PageRequest.of(page++, 2)).as(StepVerifier::create) //
.expectNextMatches(a -> iatas.contains(a.getIata())).verifyComplete();
Long airportCount = airportRepository.count().block();
assertEquals(iatas.length, airportCount);
assertEquals(iatas.size(), airportCount);
airportCount = airportRepository.countByIataIn("JFK", "IAD", "SFO").block();
assertEquals(3, airportCount);
@@ -144,8 +175,8 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends ClusterAwa
assertEquals(0, airportCount);
} finally {
for (int i = 0; i < iatas.length; i++) {
Airport airport = new Airport("airports::" + iatas[i], iatas[i] /*iata*/, iatas[i] /* lcao */);
for (String iata : iatas) {
Airport airport = new Airport("airports::" + iata, iata, iata.toLowerCase() /* lcao */);
try {
airportRepository.delete(airport).block();
} catch (DataRetrievalFailureException drfe) {

View File

@@ -15,6 +15,13 @@
*/
package org.springframework.data.couchbase.repository.query;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
import java.lang.reflect.Method;
import java.util.Properties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
@@ -37,14 +44,12 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
import org.springframework.data.repository.query.*;
import java.lang.reflect.Method;
import java.util.Properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
import org.springframework.data.repository.query.DefaultParameters;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* @author Michael Nitschinger
@@ -75,7 +80,8 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver", "Twist"),
queryMethod, converter, "travel-sample", QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, "travel-sample", new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
assertEquals(
@@ -93,7 +99,8 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver", "Twist"),
queryMethod, converter, "travel-sample", QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, "travel-sample", new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
assertEquals(
@@ -112,7 +119,8 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
try {
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver"),
queryMethod, converter, "travel-sample", QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, "travel-sample", new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
} catch (IllegalArgumentException e) {
return;
}
@@ -129,7 +137,8 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
try {
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver"),
queryMethod, converter, "travel-sample", QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, "travel-sample", new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
} catch (IllegalArgumentException e) {
return;
}

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.couchbase.repository.query;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
import java.lang.reflect.Method;
@@ -37,7 +37,8 @@ import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.domain.*;
import org.springframework.data.couchbase.domain.Airline;
import org.springframework.data.couchbase.domain.AirlineRepository;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
@@ -48,7 +49,12 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
import org.springframework.data.repository.query.*;
import org.springframework.data.repository.query.DefaultParameters;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
@@ -87,10 +93,10 @@ class StringN1qlQueryCreatorTests extends ClusterAwareIntegrationTests {
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Continental"),
queryMethod, converter, config().bucketname(), QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, config().bucketname(), new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
System.out.println(query.toN1qlSelectString(couchbaseTemplate.reactive(), Airline.class, false));
try {
Thread.sleep(3000);

View File

@@ -12,4 +12,4 @@ cluster.mocked.numReplicas=1
# Entry point configuration if not managed
# value of hostname and ns_server port
cluster.unmanaged.seed=127.0.0.1:8091
cluster.unmanaged.numReplicas=0
cluster.unmanaged.numReplicas=0