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:
7
pom.xml
7
pom.xml
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user