Scope and collection API for template. (#1133)
Scope and Collection API for template. Closes #963. Original pull request: #1071.
This commit is contained in:
committed by
mikereiche
parent
0930e5b5fe
commit
42b7fb3af8
28
pom.xml
28
pom.xml
@@ -25,16 +25,16 @@
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers-bom</artifactId>
|
||||
<version>${testcontainers}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers-bom</artifactId>
|
||||
<version>${testcontainers}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -88,7 +88,7 @@
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<version>3.1.0.RELEASE</version>
|
||||
<version>3.4.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -170,12 +170,6 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Kotlin extension -->
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.data.couchbase.core.index.CouchbasePersistentEntityIn
|
||||
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.support.PseudoArgs;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -60,14 +61,14 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex
|
||||
this.converter = converter;
|
||||
this.templateSupport = new CouchbaseTemplateSupport(converter, translationService);
|
||||
this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter, translationService);
|
||||
|
||||
|
||||
this.mappingContext = this.converter.getMappingContext();
|
||||
if (mappingContext instanceof CouchbaseMappingContext) {
|
||||
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
|
||||
if (cmc.isAutoIndexCreation()) {
|
||||
indexCreator = new CouchbasePersistentEntityIndexCreator(cmc, this);
|
||||
}
|
||||
}
|
||||
if (mappingContext instanceof CouchbaseMappingContext) {
|
||||
CouchbaseMappingContext cmc = (CouchbaseMappingContext) mappingContext;
|
||||
if (cmc.isAutoIndexCreation()) {
|
||||
indexCreator = new CouchbasePersistentEntityIndexCreator(cmc, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,4 +185,5 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex
|
||||
TemplateSupport support() {
|
||||
return templateSupport;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,9 +18,19 @@ package org.springframework.data.couchbase.core;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllExists;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithExistsOptions;
|
||||
|
||||
import com.couchbase.client.java.kv.ExistsOptions;
|
||||
|
||||
/**
|
||||
* Insert Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableExistsByIdOperation {
|
||||
|
||||
/**
|
||||
@@ -28,6 +38,9 @@ public interface ExecutableExistsByIdOperation {
|
||||
*/
|
||||
ExecutableExistsById existsById();
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingExistsById extends OneAndAllExists {
|
||||
|
||||
/**
|
||||
@@ -36,6 +49,7 @@ public interface ExecutableExistsByIdOperation {
|
||||
* @param id the ID to perform the operation on.
|
||||
* @return true if the document exists, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
boolean one(String id);
|
||||
|
||||
/**
|
||||
@@ -44,20 +58,59 @@ public interface ExecutableExistsByIdOperation {
|
||||
* @param ids the ids to check.
|
||||
* @return a map consisting of the document IDs as the keys and if they exist as the value.
|
||||
*/
|
||||
@Override
|
||||
Map<String, Boolean> all(Collection<String> ids);
|
||||
|
||||
}
|
||||
|
||||
interface ExistsByIdWithCollection extends TerminatingExistsById, WithCollection {
|
||||
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ExistsByIdWithOptions<T> extends TerminatingExistsById, WithExistsOptions<T> {
|
||||
/**
|
||||
* Allows to specify a different collection than the default one configured.
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param collection the collection to use in this scope.
|
||||
* @param options options to use for execution
|
||||
*/
|
||||
TerminatingExistsById inCollection(String collection);
|
||||
@Override
|
||||
TerminatingExistsById withOptions(ExistsOptions options);
|
||||
}
|
||||
|
||||
interface ExecutableExistsById extends ExistsByIdWithCollection {}
|
||||
/**
|
||||
*
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ExistsByIdInCollection<T> extends ExistsByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
ExistsByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ExistsByIdInScope<T> extends ExistsByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
ExistsByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing KV exists operations in a fluent way.
|
||||
*/
|
||||
interface ExecutableExistsById extends ExistsByIdInScope {}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.core;
|
||||
|
||||
import org.springframework.data.couchbase.core.ReactiveExistsByIdOperationSupport.ReactiveExistsByIdSupport;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.couchbase.core.ReactiveExistsByIdOperationSupport.ReactiveExistsByIdSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.java.kv.ExistsOptions;
|
||||
|
||||
public class ExecutableExistsByIdOperationSupport implements ExecutableExistsByIdOperation {
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
@@ -32,17 +33,25 @@ public class ExecutableExistsByIdOperationSupport implements ExecutableExistsByI
|
||||
|
||||
@Override
|
||||
public ExecutableExistsById existsById() {
|
||||
return new ExecutableExistsByIdSupport(template, null);
|
||||
return new ExecutableExistsByIdSupport(template, null, null, null);
|
||||
}
|
||||
|
||||
static class ExecutableExistsByIdSupport implements ExecutableExistsById {
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final ExistsOptions options;
|
||||
|
||||
private final ReactiveExistsByIdSupport reactiveSupport;
|
||||
|
||||
ExecutableExistsByIdSupport(final CouchbaseTemplate template, final String collection) {
|
||||
ExecutableExistsByIdSupport(final CouchbaseTemplate template, final String scope, final String collection,
|
||||
final ExistsOptions options) {
|
||||
this.template = template;
|
||||
this.reactiveSupport = new ReactiveExistsByIdSupport(template.reactive(), collection);
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.reactiveSupport = new ReactiveExistsByIdSupport(template.reactive(), scope, collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,11 +65,22 @@ public class ExecutableExistsByIdOperationSupport implements ExecutableExistsByI
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingExistsById inCollection(final String collection) {
|
||||
public ExistsByIdWithOptions inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableExistsByIdSupport(template, collection);
|
||||
return new ExecutableExistsByIdSupport(template, scope, collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingExistsById withOptions(final ExistsOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableExistsByIdSupport(template, scope, collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExistsByIdInCollection inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableExistsByIdSupport(template, scope, collection, options);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,14 +21,23 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAll;
|
||||
import org.springframework.data.couchbase.core.support.WithAnalyticsConsistency;
|
||||
import org.springframework.data.couchbase.core.support.WithAnalyticsOptions;
|
||||
import org.springframework.data.couchbase.core.support.WithAnalyticsQuery;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import com.couchbase.client.java.analytics.AnalyticsOptions;
|
||||
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
|
||||
|
||||
public interface ExecutableFindByAnalyticsOperation {
|
||||
/**
|
||||
* FindByAnalytics Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/public interface ExecutableFindByAnalyticsOperation {
|
||||
|
||||
/**
|
||||
* Queries the analytics service.
|
||||
@@ -117,8 +126,53 @@ public interface ExecutableFindByAnalyticsOperation {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use.
|
||||
*/
|
||||
interface FindByAnalyticsWithOptions<T> extends FindByAnalyticsWithQuery<T>, WithAnalyticsOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
@Override
|
||||
FindByAnalyticsWithQuery<T> withOptions(AnalyticsOptions options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByAnalyticsInCollection<T> extends FindByAnalyticsWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
FindByAnalyticsWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByAnalyticsInScope<T> extends FindByAnalyticsInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
FindByAnalyticsInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
interface FindByAnalyticsConsistentWith<T> extends FindByAnalyticsWithQuery<T> {
|
||||
interface FindByAnalyticsConsistentWith<T> extends FindByAnalyticsInScope<T> {
|
||||
|
||||
/**
|
||||
* Allows to override the default scan consistency.
|
||||
@@ -138,9 +192,24 @@ public interface ExecutableFindByAnalyticsOperation {
|
||||
* @param scanConsistency the custom scan consistency to use for this analytics query.
|
||||
*/
|
||||
FindByAnalyticsConsistentWith<T> withConsistency(AnalyticsScanConsistency scanConsistency);
|
||||
|
||||
}
|
||||
|
||||
interface ExecutableFindByAnalytics<T> extends FindByAnalyticsWithConsistency<T> {}
|
||||
/**
|
||||
* Result type override (Optional).
|
||||
*/
|
||||
interface FindByAnalyticsWithProjection<T> extends FindByAnalyticsWithConsistency<T> {
|
||||
|
||||
/**
|
||||
* Define the target type fields should be mapped to. <br />
|
||||
* Skip this step if you are anyway only interested in the original domain type.
|
||||
*
|
||||
* @param returnType must not be {@literal null}.
|
||||
* @return new instance of {@link FindByAnalyticsWithConsistency}.
|
||||
* @throws IllegalArgumentException if returnType is {@literal null}.
|
||||
*/
|
||||
<R> FindByAnalyticsWithConsistency<R> as(Class<R> returnType);
|
||||
}
|
||||
|
||||
interface ExecutableFindByAnalytics<T> extends FindByAnalyticsWithProjection<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.data.couchbase.core.ReactiveFindByAnalyticsOperationSupport.ReactiveFindByAnalyticsSupport;
|
||||
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.java.analytics.AnalyticsOptions;
|
||||
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
|
||||
|
||||
public class ExecutableFindByAnalyticsOperationSupport implements ExecutableFindByAnalyticsOperation {
|
||||
@@ -35,26 +37,34 @@ 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, domainType, ALL_QUERY, null, null, null, null);
|
||||
}
|
||||
|
||||
static class ExecutableFindByAnalyticsSupport<T> implements ExecutableFindByAnalytics<T> {
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final Class<?> domainType;
|
||||
private final Class<T> returnType;
|
||||
private final ReactiveFindByAnalyticsSupport<T> reactiveSupport;
|
||||
private final AnalyticsQuery query;
|
||||
private final AnalyticsScanConsistency scanConsistency;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final AnalyticsOptions options;
|
||||
|
||||
ExecutableFindByAnalyticsSupport(final CouchbaseTemplate template, final Class<T> domainType,
|
||||
final AnalyticsQuery query, final AnalyticsScanConsistency scanConsistency) {
|
||||
ExecutableFindByAnalyticsSupport(final CouchbaseTemplate template, final Class<?> domainType,
|
||||
final Class<T> returnType, final AnalyticsQuery query, final AnalyticsScanConsistency scanConsistency,
|
||||
final String scope, final String collection, final AnalyticsOptions options) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.returnType = returnType;
|
||||
this.query = query;
|
||||
this.reactiveSupport = new ReactiveFindByAnalyticsSupport<>(template.reactive(), domainType, query,
|
||||
scanConsistency, new NonReactiveSupportWrapper(template.support()));
|
||||
this.reactiveSupport = new ReactiveFindByAnalyticsSupport<>(template.reactive(), domainType, returnType, query,
|
||||
scanConsistency, scope, collection, options, new NonReactiveSupportWrapper(template.support()));
|
||||
this.scanConsistency = scanConsistency;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,18 +84,49 @@ public class ExecutableFindByAnalyticsOperationSupport implements ExecutableFind
|
||||
|
||||
@Override
|
||||
public TerminatingFindByAnalytics<T> matching(final AnalyticsQuery query) {
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, query, scanConsistency);
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByAnalyticsWithQuery<T> withOptions(final AnalyticsOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByAnalyticsInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByAnalyticsWithConsistency<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public FindByAnalyticsWithQuery<T> consistentWith(final AnalyticsScanConsistency scanConsistency) {
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, query, scanConsistency);
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByAnalyticsWithConsistency<T> withConsistency(final AnalyticsScanConsistency scanConsistency) {
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, query, scanConsistency);
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> FindByAnalyticsWithConsistency<R> as(final Class<R> returnType) {
|
||||
Assert.notNull(returnType, "returnType must not be null!");
|
||||
return new ExecutableFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,9 +18,19 @@ package org.springframework.data.couchbase.core;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllId;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithGetOptions;
|
||||
import org.springframework.data.couchbase.core.support.WithProjectionId;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
|
||||
import com.couchbase.client.java.kv.GetOptions;
|
||||
|
||||
/**
|
||||
* Get Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableFindByIdOperation {
|
||||
|
||||
/**
|
||||
@@ -30,6 +40,11 @@ public interface ExecutableFindByIdOperation {
|
||||
*/
|
||||
<T> ExecutableFindById<T> findById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface TerminatingFindById<T> extends OneAndAllId<T> {
|
||||
|
||||
/**
|
||||
@@ -50,28 +65,66 @@ public interface ExecutableFindByIdOperation {
|
||||
|
||||
}
|
||||
|
||||
interface FindByIdWithCollection<T> extends TerminatingFindById<T>, WithCollection<T> {
|
||||
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByIdWithOptions<T> extends TerminatingFindById<T>, WithGetOptions<T> {
|
||||
/**
|
||||
* Allows to specify a different collection than the default one configured.
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param collection the collection to use in this scope.
|
||||
* @param options options to use for execution
|
||||
*/
|
||||
TerminatingFindById<T> inCollection(String collection);
|
||||
|
||||
@Override
|
||||
TerminatingFindById<T> withOptions(GetOptions options);
|
||||
}
|
||||
|
||||
interface FindByIdWithProjection<T> extends FindByIdWithCollection<T>, WithProjectionId<T> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByIdInCollection<T> extends FindByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
FindByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByIdInScope<T> extends FindByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
FindByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
interface FindByIdWithProjection<T> extends FindByIdInScope<T>, WithProjectionId<T> {
|
||||
/**
|
||||
* Load only certain fields for the document.
|
||||
*
|
||||
* @param fields the projected fields to load.
|
||||
*/
|
||||
FindByIdWithCollection<T> project(String... fields);
|
||||
|
||||
@Override
|
||||
FindByIdInScope<T> project(String... fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing query operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to use for the results
|
||||
*/
|
||||
interface ExecutableFindById<T> extends FindByIdWithProjection<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import java.util.List;
|
||||
import org.springframework.data.couchbase.core.ReactiveFindByIdOperationSupport.ReactiveFindByIdSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.java.kv.GetOptions;
|
||||
|
||||
public class ExecutableFindByIdOperationSupport implements ExecutableFindByIdOperation {
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
@@ -32,23 +34,29 @@ public class ExecutableFindByIdOperationSupport implements ExecutableFindByIdOpe
|
||||
|
||||
@Override
|
||||
public <T> ExecutableFindById<T> findById(Class<T> domainType) {
|
||||
return new ExecutableFindByIdSupport<>(template, domainType, null, null);
|
||||
return new ExecutableFindByIdSupport<>(template, domainType, null, null, null, null);
|
||||
}
|
||||
|
||||
static class ExecutableFindByIdSupport<T> implements ExecutableFindById<T> {
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final GetOptions options;
|
||||
private final List<String> fields;
|
||||
private final ReactiveFindByIdSupport<T> reactiveSupport;
|
||||
|
||||
ExecutableFindByIdSupport(CouchbaseTemplate template, Class<T> domainType, String collection, List<String> fields) {
|
||||
ExecutableFindByIdSupport(CouchbaseTemplate template, Class<T> domainType, String scope, String collection,
|
||||
GetOptions options, List<String> fields) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.fields = fields;
|
||||
this.reactiveSupport = new ReactiveFindByIdSupport<>(template.reactive(), domainType, collection, fields, new NonReactiveSupportWrapper(template.support()));
|
||||
this.reactiveSupport = new ReactiveFindByIdSupport<>(template.reactive(), domainType, scope, collection, options,
|
||||
fields, new NonReactiveSupportWrapper(template.support()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,16 +70,29 @@ public class ExecutableFindByIdOperationSupport implements ExecutableFindByIdOpe
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingFindById<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableFindByIdSupport<>(template, domainType, collection, fields);
|
||||
public TerminatingFindById<T> withOptions(final GetOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByIdWithCollection<T> project(String... fields) {
|
||||
Assert.notEmpty(fields, "Fields must not be null nor empty.");
|
||||
return new ExecutableFindByIdSupport<>(template, domainType, collection, Arrays.asList(fields));
|
||||
public FindByIdWithOptions<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByIdInScope<T> project(String... fields) {
|
||||
Assert.notEmpty(fields, "Fields must not be null nor empty.");
|
||||
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,16 +22,24 @@ import java.util.stream.Stream;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAll;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithConsistency;
|
||||
import org.springframework.data.couchbase.core.support.WithDistinct;
|
||||
import org.springframework.data.couchbase.core.support.WithProjection;
|
||||
import org.springframework.data.couchbase.core.support.WithQuery;
|
||||
import org.springframework.data.couchbase.core.support.WithQueryOptions;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* Query Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableFindByQueryOperation {
|
||||
|
||||
/**
|
||||
@@ -41,13 +49,18 @@ public interface ExecutableFindByQueryOperation {
|
||||
*/
|
||||
<T> ExecutableFindByQuery<T> findByQuery(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingFindByQuery<T> extends OneAndAll<T> {
|
||||
|
||||
/**
|
||||
* Get exactly zero or one result.
|
||||
*
|
||||
* @return {@link Optional#empty()} if no match found.
|
||||
* @throws IncorrectResultSizeDataAccessException if more than one match found.
|
||||
*/
|
||||
@Override
|
||||
default Optional<T> one() {
|
||||
return Optional.ofNullable(oneValue());
|
||||
}
|
||||
@@ -59,6 +72,7 @@ public interface ExecutableFindByQueryOperation {
|
||||
* @throws IncorrectResultSizeDataAccessException if more than one match found.
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
T oneValue();
|
||||
|
||||
/**
|
||||
@@ -66,6 +80,7 @@ public interface ExecutableFindByQueryOperation {
|
||||
*
|
||||
* @return {@link Optional#empty()} if no match found.
|
||||
*/
|
||||
@Override
|
||||
default Optional<T> first() {
|
||||
return Optional.ofNullable(firstValue());
|
||||
}
|
||||
@@ -76,13 +91,15 @@ public interface ExecutableFindByQueryOperation {
|
||||
* @return {@literal null} if no match found.
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
T firstValue();
|
||||
|
||||
/**
|
||||
* Get all matching elements.
|
||||
* Get all matching documents.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
List<T> all();
|
||||
|
||||
/**
|
||||
@@ -90,6 +107,7 @@ public interface ExecutableFindByQueryOperation {
|
||||
*
|
||||
* @return a {@link Stream} of results. Never {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
Stream<T> stream();
|
||||
|
||||
/**
|
||||
@@ -97,6 +115,7 @@ public interface ExecutableFindByQueryOperation {
|
||||
*
|
||||
* @return total number of matching elements.
|
||||
*/
|
||||
@Override
|
||||
long count();
|
||||
|
||||
/**
|
||||
@@ -104,15 +123,15 @@ public interface ExecutableFindByQueryOperation {
|
||||
*
|
||||
* @return {@literal true} if at least one matching element exists.
|
||||
*/
|
||||
@Override
|
||||
boolean exists();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual query execution.
|
||||
* Fluent methods to specify the query
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithQuery<T> extends TerminatingFindByQuery<T>, WithQuery<T> {
|
||||
|
||||
@@ -122,6 +141,7 @@ public interface ExecutableFindByQueryOperation {
|
||||
* @param query must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if query is {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
TerminatingFindByQuery<T> matching(Query query);
|
||||
|
||||
/**
|
||||
@@ -131,25 +151,65 @@ public interface ExecutableFindByQueryOperation {
|
||||
* @return new instance of {@link ExecutableFindByQuery}.
|
||||
* @throws IllegalArgumentException if criteria is {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
default TerminatingFindByQuery<T> matching(QueryCriteriaDefinition criteria) {
|
||||
return matching(Query.query(criteria));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface FindByQueryInCollection<T> extends FindByQueryWithQuery<T>, WithCollection<T> {
|
||||
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithOptions<T> extends FindByQueryWithQuery<T>, WithQueryOptions<T> {
|
||||
/**
|
||||
* Allows to override the default scan consistency.
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param collection the collection to use for this query.
|
||||
* @param options to use for execution
|
||||
*/
|
||||
FindByQueryWithQuery<T> inCollection(String collection);
|
||||
|
||||
@Override
|
||||
TerminatingFindByQuery<T> withOptions(QueryOptions options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryInCollection<T> extends FindByQueryWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
FindByQueryWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryInScope<T> extends FindByQueryInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
FindByQueryInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be removed at the next major release. use WithConsistency instead
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
@Deprecated
|
||||
interface FindByQueryConsistentWith<T> extends FindByQueryInCollection<T> {
|
||||
interface FindByQueryConsistentWith<T> extends FindByQueryInScope<T> {
|
||||
|
||||
/**
|
||||
* Allows to override the default scan consistency.
|
||||
@@ -157,10 +217,14 @@ public interface ExecutableFindByQueryOperation {
|
||||
* @param scanConsistency the custom scan consistency to use for this query.
|
||||
*/
|
||||
@Deprecated
|
||||
FindByQueryInCollection<T> consistentWith(QueryScanConsistency scanConsistency);
|
||||
|
||||
FindByQueryInScope<T> consistentWith(QueryScanConsistency scanConsistency);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithConsistency<T> extends FindByQueryConsistentWith<T>, WithConsistency<T> {
|
||||
|
||||
/**
|
||||
@@ -168,18 +232,20 @@ public interface ExecutableFindByQueryOperation {
|
||||
*
|
||||
* @param scanConsistency the custom scan consistency to use for this query.
|
||||
*/
|
||||
@Override
|
||||
FindByQueryConsistentWith<T> withConsistency(QueryScanConsistency scanConsistency);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Result type override (Optional).
|
||||
* Fluent method to specify a return type different than the the entity type to use for the results.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithProjection<T> extends FindByQueryWithConsistency<T> {
|
||||
|
||||
/**
|
||||
* Define the target type fields should be mapped to. <br />
|
||||
* Skip this step if you are anyway only interested in the original domain type.
|
||||
* Skip this step if you are only interested in the original the entity type to use for the results.
|
||||
*
|
||||
* @param returnType must not be {@literal null}.
|
||||
* @return new instance of {@link FindByQueryWithProjection}.
|
||||
@@ -189,7 +255,9 @@ public interface ExecutableFindByQueryOperation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Distinct Find support.
|
||||
* Fluent method to specify DISTINCT fields
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjection<T>, WithDistinct<T> {
|
||||
|
||||
@@ -200,14 +268,15 @@ public interface ExecutableFindByQueryOperation {
|
||||
* @return new instance of {@link ExecutableFindByQuery}.
|
||||
* @throws IllegalArgumentException if field is {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
FindByQueryWithProjection<T> distinct(String[] distinctFields);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ExecutableFindByQuery} provides methods for constructing lookup operations in a fluent way.
|
||||
* Provides methods for constructing query operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to use for the results
|
||||
*/
|
||||
|
||||
interface ExecutableFindByQuery<T> extends FindByQueryWithDistinct<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSuppo
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
@@ -42,8 +43,8 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
|
||||
@Override
|
||||
public <T> ExecutableFindByQuery<T> findByQuery(final Class<T> domainType) {
|
||||
return new ExecutableFindByQuerySupport<T>(template, domainType, domainType, ALL_QUERY,
|
||||
QueryScanConsistency.NOT_BOUNDED, null, null);
|
||||
return new ExecutableFindByQuerySupport<T>(template, domainType, domainType, ALL_QUERY, null, null, null, null,
|
||||
null);
|
||||
}
|
||||
|
||||
static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T> {
|
||||
@@ -54,20 +55,24 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
private final Query query;
|
||||
private final ReactiveFindByQuerySupport<T> reactiveSupport;
|
||||
private final QueryScanConsistency scanConsistency;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final QueryOptions options;
|
||||
private final String[] distinctFields;
|
||||
|
||||
ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<?> domainType, final Class<T> returnType,
|
||||
final Query query, final QueryScanConsistency scanConsistency, final String collection,
|
||||
final String[] distinctFields) {
|
||||
final Query query, final QueryScanConsistency scanConsistency, final String scope, final String collection,
|
||||
final QueryOptions options, final String[] distinctFields) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.returnType = returnType;
|
||||
this.query = query;
|
||||
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, returnType, query,
|
||||
scanConsistency, collection, distinctFields, new NonReactiveSupportWrapper(template.support()));
|
||||
scanConsistency, scope, collection, options, distinctFields, new NonReactiveSupportWrapper(template.support()));
|
||||
this.scanConsistency = scanConsistency;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.distinctFields = distinctFields;
|
||||
}
|
||||
|
||||
@@ -94,42 +99,35 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
} else {
|
||||
scanCons = scanConsistency;
|
||||
}
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanCons, collection,
|
||||
distinctFields);
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
|
||||
options, distinctFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public FindByQueryInCollection<T> consistentWith(final QueryScanConsistency scanConsistency) {
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields);
|
||||
public FindByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields);
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithConsistency<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> FindByQueryWithConsistency<R> as(final Class<R> resturnType) {
|
||||
Assert.notNull(resturnType, "returnType must not be null!");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, resturnType, query, scanConsistency, collection,
|
||||
distinctFields);
|
||||
public <R> FindByQueryWithConsistency<R> as(final Class<R> returnType) {
|
||||
Assert.notNull(returnType, "returnType must not be null!");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithProjection<T> distinct(final String[] distinctFields) {
|
||||
Assert.notNull(distinctFields, "distinctFields must not be null!");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields);
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,6 +144,28 @@ public class ExecutableFindByQueryOperationSupport implements ExecutableFindByQu
|
||||
public boolean exists() {
|
||||
return count() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithConsistency<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,25 +18,100 @@ package org.springframework.data.couchbase.core;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.AnyId;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.WithGetAnyReplicaOptions;
|
||||
|
||||
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
|
||||
|
||||
/**
|
||||
* Query Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableFindFromReplicasByIdOperation {
|
||||
|
||||
/**
|
||||
* Loads a document from a replica.
|
||||
*
|
||||
* @param domainType the entity type to use for the results.
|
||||
*/
|
||||
<T> ExecutableFindFromReplicasById<T> findFromReplicasById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual get execution.
|
||||
*/
|
||||
interface TerminatingFindFromReplicasById<T> extends AnyId<T> {
|
||||
|
||||
/**
|
||||
* Finds one document based on the given ID.
|
||||
*
|
||||
* @param id the document ID.
|
||||
* @return the entity if found.
|
||||
*/
|
||||
@Override
|
||||
T any(String id);
|
||||
|
||||
/**
|
||||
* Finds a list of documents based on the given IDs.
|
||||
*
|
||||
* @param ids the document ID ids.
|
||||
* @return the list of found entities.
|
||||
*/
|
||||
@Override
|
||||
Collection<? extends T> any(Collection<String> ids);
|
||||
|
||||
}
|
||||
|
||||
interface FindFromReplicasByIdWithCollection<T> extends TerminatingFindFromReplicasById<T>, WithCollection<T> {
|
||||
|
||||
TerminatingFindFromReplicasById<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindFromReplicasByIdWithOptions<T> extends TerminatingFindFromReplicasById<T>, WithGetAnyReplicaOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingFindFromReplicasById<T> withOptions(GetAnyReplicaOptions options);
|
||||
}
|
||||
|
||||
interface ExecutableFindFromReplicasById<T> extends FindFromReplicasByIdWithCollection<T> {}
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindFromReplicasByIdInCollection<T> extends FindFromReplicasByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
FindFromReplicasByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindFromReplicasByIdInScope<T> extends FindFromReplicasByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
FindFromReplicasByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing get operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to use for the results
|
||||
*/
|
||||
interface ExecutableFindFromReplicasById<T> extends FindFromReplicasByIdInScope<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.couchbase.core;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.ReactiveFindFromReplicasByIdOperationSupport.ReactiveFindFromReplicasByIdSupport;
|
||||
|
||||
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class ExecutableFindFromReplicasByIdOperationSupport implements ExecutableFindFromReplicasByIdOperation {
|
||||
@@ -30,7 +32,7 @@ public class ExecutableFindFromReplicasByIdOperationSupport implements Executabl
|
||||
|
||||
@Override
|
||||
public <T> ExecutableFindFromReplicasById<T> findFromReplicasById(Class<T> domainType) {
|
||||
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, domainType, null);
|
||||
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, domainType, null, null, null);
|
||||
}
|
||||
|
||||
static class ExecutableFindFromReplicasByIdSupport<T> implements ExecutableFindFromReplicasById<T> {
|
||||
@@ -38,17 +40,21 @@ public class ExecutableFindFromReplicasByIdOperationSupport implements Executabl
|
||||
private final CouchbaseTemplate template;
|
||||
private final Class<?> domainType;
|
||||
private final Class<T> returnType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final GetAnyReplicaOptions options;
|
||||
private final ReactiveFindFromReplicasByIdSupport<T> reactiveSupport;
|
||||
|
||||
ExecutableFindFromReplicasByIdSupport(CouchbaseTemplate template, Class<?> domainType, Class<T> returnType,
|
||||
String collection) {
|
||||
String scope, String collection, GetAnyReplicaOptions options) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.returnType = returnType;
|
||||
this.reactiveSupport = new ReactiveFindFromReplicasByIdSupport<>(template.reactive(), domainType, returnType,
|
||||
collection, new NonReactiveSupportWrapper(template.support()));
|
||||
scope, collection, options, new NonReactiveSupportWrapper(template.support()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,9 +68,21 @@ public class ExecutableFindFromReplicasByIdOperationSupport implements Executabl
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingFindFromReplicasById<T> inCollection(final String collection) {
|
||||
public TerminatingFindFromReplicasById<T> withOptions(final GetAnyReplicaOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindFromReplicasByIdWithOptions<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType, collection);
|
||||
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindFromReplicasByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,37 +18,107 @@ package org.springframework.data.couchbase.core;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllEntity;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithInsertOptions;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.InsertOptions;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
/**
|
||||
* Insert Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableInsertByIdOperation {
|
||||
|
||||
/**
|
||||
* Insert using the KV service.
|
||||
*
|
||||
* @param domainType the entity type to insert.
|
||||
*/
|
||||
<T> ExecutableInsertById<T> insertById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingInsertById<T> extends OneAndAllEntity<T> {
|
||||
|
||||
/**
|
||||
* Insert one entity.
|
||||
*
|
||||
* @return Inserted entity.
|
||||
*/
|
||||
@Override
|
||||
T one(T object);
|
||||
|
||||
/**
|
||||
* Insert a collection of entities.
|
||||
*
|
||||
* @return Inserted entities
|
||||
*/
|
||||
@Override
|
||||
Collection<? extends T> all(Collection<? extends T> objects);
|
||||
|
||||
}
|
||||
|
||||
interface InsertByIdWithCollection<T> extends TerminatingInsertById<T>, WithCollection<T> {
|
||||
|
||||
TerminatingInsertById<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use.
|
||||
*/
|
||||
interface InsertByIdWithOptions<T>
|
||||
extends TerminatingInsertById<T>, WithInsertOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution.
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingInsertById<T> withOptions(InsertOptions options);
|
||||
}
|
||||
|
||||
interface InsertByIdWithDurability<T> extends InsertByIdWithCollection<T>, WithDurability<T> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface InsertByIdInCollection<T> extends InsertByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
InsertByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
InsertByIdWithCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface InsertByIdInScope<T> extends InsertByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
InsertByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
InsertByIdWithCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
interface InsertByIdWithDurability<T> extends InsertByIdInScope<T>, WithDurability<T> {
|
||||
|
||||
@Override
|
||||
InsertByIdInCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
|
||||
@Override
|
||||
InsertByIdInCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
|
||||
}
|
||||
|
||||
@@ -58,6 +128,11 @@ public interface ExecutableInsertByIdOperation {
|
||||
InsertByIdWithDurability<T> withExpiry(Duration expiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing KV insert operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to insert
|
||||
*/
|
||||
interface ExecutableInsertById<T> extends InsertByIdWithExpiry<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.data.couchbase.core.ReactiveInsertByIdOperationSuppor
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.InsertOptions;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
@@ -36,7 +37,7 @@ public class ExecutableInsertByIdOperationSupport implements ExecutableInsertByI
|
||||
@Override
|
||||
public <T> ExecutableInsertById<T> insertById(final Class<T> domainType) {
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, null, null, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
DurabilityLevel.NONE, null);
|
||||
}
|
||||
|
||||
@@ -44,25 +45,29 @@ public class ExecutableInsertByIdOperationSupport implements ExecutableInsertByI
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final InsertOptions options;
|
||||
private final PersistTo persistTo;
|
||||
private final ReplicateTo replicateTo;
|
||||
private final DurabilityLevel durabilityLevel;
|
||||
private final Duration expiry;
|
||||
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,
|
||||
final Duration expiry) {
|
||||
ExecutableInsertByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String scope,
|
||||
final String collection, final InsertOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
final DurabilityLevel durabilityLevel, final Duration expiry) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.persistTo = persistTo;
|
||||
this.replicateTo = replicateTo;
|
||||
this.durabilityLevel = durabilityLevel;
|
||||
this.expiry = expiry;
|
||||
this.reactiveSupport = new ReactiveInsertByIdSupport<>(template.reactive(), domainType, collection, persistTo,
|
||||
replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
|
||||
this.reactiveSupport = new ReactiveInsertByIdSupport<>(template.reactive(), domainType, scope, collection,
|
||||
options, persistTo, replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,31 +81,45 @@ public class ExecutableInsertByIdOperationSupport implements ExecutableInsertByI
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingInsertById<T> inCollection(final String collection) {
|
||||
public TerminatingInsertById<T> withOptions(final InsertOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdWithOptions<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdWithCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
public InsertByIdInCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdWithCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
public InsertByIdInCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
Assert.notNull(persistTo, "PersistTo must not be null.");
|
||||
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdWithDurability<T> withExpiry(final Duration expiry) {
|
||||
Assert.notNull(expiry, "expiry must not be null.");
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
return new ExecutableInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,35 +18,100 @@ package org.springframework.data.couchbase.core;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.couchbase.core.query.WithConsistency;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllId;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithRemoveOptions;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.RemoveOptions;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
/**
|
||||
* Remove Operations on KV service.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableRemoveByIdOperation {
|
||||
|
||||
/**
|
||||
* Removes a document.
|
||||
*/
|
||||
ExecutableRemoveById removeById();
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingRemoveById extends OneAndAllId<RemoveResult> {
|
||||
|
||||
/**
|
||||
* Remove one document based on the given ID.
|
||||
*
|
||||
* @param id the document ID.
|
||||
* @return result of the remove
|
||||
*/
|
||||
@Override
|
||||
RemoveResult one(String id);
|
||||
|
||||
/**
|
||||
* Remove the documents in the collection.
|
||||
*
|
||||
* @param ids the document IDs.
|
||||
* @return result of the removes.
|
||||
*/
|
||||
@Override
|
||||
List<RemoveResult> all(Collection<String> ids);
|
||||
|
||||
}
|
||||
|
||||
interface RemoveByIdWithCollection extends TerminatingRemoveById, WithCollection<RemoveResult> {
|
||||
|
||||
TerminatingRemoveById inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*/
|
||||
interface RemoveByIdWithOptions extends TerminatingRemoveById, WithRemoveOptions<RemoveResult> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingRemoveById withOptions(RemoveOptions options);
|
||||
}
|
||||
|
||||
interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurability<RemoveResult> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*/
|
||||
interface RemoveByIdInCollection extends RemoveByIdWithOptions, InCollection<Object> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
RemoveByIdWithOptions inCollection(String collection);
|
||||
}
|
||||
|
||||
RemoveByIdWithCollection withDurability(DurabilityLevel durabilityLevel);
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*/
|
||||
interface RemoveByIdInScope extends RemoveByIdInCollection, InScope<Object> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
RemoveByIdInCollection inScope(String scope);
|
||||
}
|
||||
|
||||
RemoveByIdWithCollection withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
interface RemoveByIdWithDurability extends RemoveByIdInScope, WithDurability<RemoveResult> {
|
||||
|
||||
@Override
|
||||
RemoveByIdInCollection withDurability(DurabilityLevel durabilityLevel);
|
||||
|
||||
@Override
|
||||
RemoveByIdInCollection withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
|
||||
}
|
||||
|
||||
@@ -55,6 +120,9 @@ public interface ExecutableRemoveByIdOperation {
|
||||
RemoveByIdWithDurability withCas(Long cas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing remove operations in a fluent way.
|
||||
*/
|
||||
interface ExecutableRemoveById extends RemoveByIdWithCas {}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.RemoveOptions;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByIdOperation {
|
||||
@@ -35,30 +36,35 @@ public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByI
|
||||
|
||||
@Override
|
||||
public ExecutableRemoveById removeById() {
|
||||
return new ExecutableRemoveByIdSupport(template, null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE,
|
||||
null);
|
||||
return new ExecutableRemoveByIdSupport(template, null, null, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
DurabilityLevel.NONE, null);
|
||||
}
|
||||
|
||||
static class ExecutableRemoveByIdSupport implements ExecutableRemoveById {
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final RemoveOptions options;
|
||||
private final PersistTo persistTo;
|
||||
private final ReplicateTo replicateTo;
|
||||
private final DurabilityLevel durabilityLevel;
|
||||
private final Long cas;
|
||||
private final ReactiveRemoveByIdSupport reactiveRemoveByIdSupport;
|
||||
|
||||
ExecutableRemoveByIdSupport(final CouchbaseTemplate template, final String collection, final PersistTo persistTo,
|
||||
final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel, Long cas) {
|
||||
ExecutableRemoveByIdSupport(final CouchbaseTemplate template, final String scope, final String collection,
|
||||
final RemoveOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
final DurabilityLevel durabilityLevel, Long cas) {
|
||||
this.template = template;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.persistTo = persistTo;
|
||||
this.replicateTo = replicateTo;
|
||||
this.durabilityLevel = durabilityLevel;
|
||||
this.reactiveRemoveByIdSupport = new ReactiveRemoveByIdSupport(template.reactive(), scope, collection, options,
|
||||
persistTo, replicateTo, durabilityLevel, cas);
|
||||
this.cas = cas;
|
||||
this.reactiveRemoveByIdSupport = new ReactiveRemoveByIdSupport(template.reactive(), collection, persistTo,
|
||||
replicateTo, durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,30 +78,46 @@ public class ExecutableRemoveByIdOperationSupport implements ExecutableRemoveByI
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingRemoveById inCollection(final String collection) {
|
||||
public RemoveByIdWithOptions inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null);
|
||||
return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithCollection withDurability(final DurabilityLevel durabilityLevel) {
|
||||
public RemoveByIdInCollection withDurability(final DurabilityLevel durabilityLevel) {
|
||||
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
|
||||
return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null);
|
||||
return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithCollection withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
public RemoveByIdInCollection withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
Assert.notNull(persistTo, "PersistTo must not be null.");
|
||||
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
|
||||
return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null);
|
||||
return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithCas withCas(final Long cas) {
|
||||
Assert.notNull(cas, "CAS must not be null.");
|
||||
return new ExecutableRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, cas);
|
||||
public TerminatingRemoveById withOptions(final RemoveOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdInCollection inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithDurability withCas(Long cas) {
|
||||
return new ExecutableRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,22 +19,47 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.WithConsistency;
|
||||
import org.springframework.data.couchbase.core.support.WithQuery;
|
||||
import org.springframework.data.couchbase.core.support.WithQueryOptions;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* RemoveBy Query Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableRemoveByQueryOperation {
|
||||
|
||||
/**
|
||||
* Remove via the query service.
|
||||
*/
|
||||
<T> ExecutableRemoveByQuery<T> removeByQuery(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingRemoveByQuery<T> {
|
||||
|
||||
/**
|
||||
* Remove all matching documents.
|
||||
*
|
||||
* @return RemoveResult for each matching document
|
||||
*/
|
||||
List<RemoveResult> all();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent methods to specify the query
|
||||
*
|
||||
* @param <T> the entity type.
|
||||
*/
|
||||
interface RemoveByQueryWithQuery<T> extends TerminatingRemoveByQuery<T>, WithQuery<T> {
|
||||
|
||||
TerminatingRemoveByQuery<T> matching(Query query);
|
||||
@@ -45,26 +70,67 @@ public interface ExecutableRemoveByQueryOperation {
|
||||
|
||||
}
|
||||
|
||||
interface RemoveByQueryInCollection<T> extends RemoveByQueryWithQuery<T>, WithCollection<T> {
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface RemoveByQueryWithOptions<T> extends RemoveByQueryWithQuery<T>, WithQueryOptions<RemoveResult> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
RemoveByQueryWithQuery<T> withOptions(QueryOptions options);
|
||||
}
|
||||
|
||||
RemoveByQueryWithQuery<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface RemoveByQueryInCollection<T> extends RemoveByQueryWithOptions<T>, InCollection<Object> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
RemoveByQueryWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface RemoveByQueryInScope<T> extends RemoveByQueryInCollection<T>, InScope<Object> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
RemoveByQueryInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
interface RemoveByQueryConsistentWith<T> extends RemoveByQueryInCollection<T> {
|
||||
interface RemoveByQueryConsistentWith<T> extends RemoveByQueryInScope<T> {
|
||||
|
||||
@Deprecated
|
||||
RemoveByQueryInCollection<T> consistentWith(QueryScanConsistency scanConsistency);
|
||||
RemoveByQueryInScope<T> consistentWith(QueryScanConsistency scanConsistency);
|
||||
|
||||
}
|
||||
|
||||
interface RemoveByQueryWithConsistency<T> extends RemoveByQueryConsistentWith<T>, WithConsistency<T> {
|
||||
|
||||
@Override
|
||||
RemoveByQueryConsistentWith<T> withConsistency(QueryScanConsistency scanConsistency);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing query operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type.
|
||||
*/
|
||||
interface ExecutableRemoveByQuery<T> extends RemoveByQueryWithConsistency<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,9 +19,10 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.couchbase.core.ReactiveRemoveByQueryOperationSupport.ReactiveRemoveByQuerySupport;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemoveByQueryOperation {
|
||||
|
||||
@@ -35,8 +36,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,
|
||||
null);
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, ALL_QUERY, null, null,
|
||||
null, null);
|
||||
}
|
||||
|
||||
static class ExecutableRemoveByQuerySupport<T> implements ExecutableRemoveByQuery<T> {
|
||||
@@ -46,17 +47,21 @@ public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemove
|
||||
private final Query query;
|
||||
private final ReactiveRemoveByQuerySupport<T> reactiveSupport;
|
||||
private final QueryScanConsistency scanConsistency;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final QueryOptions options;
|
||||
|
||||
ExecutableRemoveByQuerySupport(final CouchbaseTemplate template, final Class<T> domainType, final Query query,
|
||||
final QueryScanConsistency scanConsistency, String collection) {
|
||||
final QueryScanConsistency scanConsistency, String scope, String collection, QueryOptions options) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.query = query;
|
||||
this.reactiveSupport = new ReactiveRemoveByQuerySupport<>(template.reactive(), domainType, query, scanConsistency,
|
||||
collection);
|
||||
scope, collection, options);
|
||||
this.scanConsistency = scanConsistency;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,26 +71,43 @@ public class ExecutableRemoveByQueryOperationSupport implements ExecutableRemove
|
||||
|
||||
@Override
|
||||
public TerminatingRemoveByQuery<T> matching(final Query query) {
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public RemoveByQueryInCollection<T> consistentWith(final QueryScanConsistency scanConsistency) {
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
|
||||
public RemoveByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByQueryWithConsistency<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByQueryWithQuery<T> withOptions(final QueryOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByQueryInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,46 +18,117 @@ package org.springframework.data.couchbase.core;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllEntity;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithReplaceOptions;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplaceOptions;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
/**
|
||||
* Replace Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableReplaceByIdOperation {
|
||||
|
||||
/**
|
||||
* Replace using the KV service.
|
||||
*
|
||||
* @param domainType the entity type to replace.
|
||||
*/
|
||||
<T> ExecutableReplaceById<T> replaceById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingReplaceById<T> extends OneAndAllEntity<T> {
|
||||
|
||||
/**
|
||||
* Replace one entity.
|
||||
*
|
||||
* @return Replaced entity.
|
||||
*/
|
||||
@Override
|
||||
T one(T object);
|
||||
|
||||
/**
|
||||
* Replace a collection of entities.
|
||||
*
|
||||
* @return Replaced entities
|
||||
*/
|
||||
@Override
|
||||
Collection<? extends T> all(Collection<? extends T> objects);
|
||||
|
||||
}
|
||||
|
||||
interface ReplaceByIdWithCollection<T> extends TerminatingReplaceById<T>, WithCollection<T> {
|
||||
|
||||
TerminatingReplaceById<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ReplaceByIdWithOptions<T> extends TerminatingReplaceById<T>, WithReplaceOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingReplaceById<T> withOptions(ReplaceOptions options);
|
||||
}
|
||||
|
||||
interface ReplaceByIdWithDurability<T> extends ReplaceByIdWithCollection<T>, WithDurability<T> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ReplaceByIdInCollection<T> extends ReplaceByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
ReplaceByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
ReplaceByIdWithCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ReplaceByIdInScope<T> extends ReplaceByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
ReplaceByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
ReplaceByIdWithCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
interface ReplaceByIdWithDurability<T> extends ReplaceByIdInScope<T>, WithDurability<T> {
|
||||
@Override
|
||||
ReplaceByIdInScope<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
@Override
|
||||
ReplaceByIdInScope<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
|
||||
}
|
||||
|
||||
interface ReplaceByIdWithExpiry<T> extends ReplaceByIdWithDurability<T>, WithExpiry<T> {
|
||||
|
||||
@Override
|
||||
ReplaceByIdWithDurability<T> withExpiry(final Duration expiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing KV replace operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to replace
|
||||
*/
|
||||
interface ExecutableReplaceById<T> extends ReplaceByIdWithExpiry<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,12 @@ package org.springframework.data.couchbase.core;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.data.couchbase.core.ReactiveReplaceByIdOperationSupport.ReactiveReplaceByIdSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplaceOptions;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceByIdOperation {
|
||||
@@ -36,7 +37,7 @@ public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceB
|
||||
@Override
|
||||
public <T> ExecutableReplaceById<T> replaceById(final Class<T> domainType) {
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, null, null, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
DurabilityLevel.NONE, null);
|
||||
}
|
||||
|
||||
@@ -44,25 +45,29 @@ public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceB
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final ReplaceOptions options;
|
||||
private final PersistTo persistTo;
|
||||
private final ReplicateTo replicateTo;
|
||||
private final DurabilityLevel durabilityLevel;
|
||||
private final Duration expiry;
|
||||
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,
|
||||
final Duration expiry) {
|
||||
ExecutableReplaceByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String scope,
|
||||
final String collection, ReplaceOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
final DurabilityLevel durabilityLevel, final Duration expiry) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.persistTo = persistTo;
|
||||
this.replicateTo = replicateTo;
|
||||
this.durabilityLevel = durabilityLevel;
|
||||
this.expiry = expiry;
|
||||
this.reactiveSupport = new ReactiveReplaceByIdSupport<>(template.reactive(),
|
||||
domainType, collection, persistTo, replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
|
||||
this.reactiveSupport = new ReactiveReplaceByIdSupport<>(template.reactive(), domainType, scope, collection,
|
||||
options, persistTo, replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,32 +81,46 @@ public class ExecutableReplaceByIdOperationSupport implements ExecutableReplaceB
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingReplaceById<T> inCollection(final String collection) {
|
||||
public ReplaceByIdWithOptions<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
|
||||
replicateTo, durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdWithCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
public ReplaceByIdInScope<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
|
||||
replicateTo, durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdWithCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
public ReplaceByIdInScope<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
Assert.notNull(persistTo, "PersistTo must not be null.");
|
||||
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
|
||||
replicateTo, durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdWithDurability<T> withExpiry(final Duration expiry) {
|
||||
Assert.notNull(expiry, "expiry must not be null.");
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
|
||||
replicateTo, durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingReplaceById<T> withOptions(final ReplaceOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
|
||||
replicateTo, durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo,
|
||||
replicateTo, durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,46 +18,118 @@ package org.springframework.data.couchbase.core;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllEntity;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithUpsertOptions;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
import com.couchbase.client.java.kv.UpsertOptions;
|
||||
|
||||
/**
|
||||
* Insert Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ExecutableUpsertByIdOperation {
|
||||
|
||||
/**
|
||||
* Upsert using the KV service.
|
||||
*
|
||||
* @param domainType the entity type to upsert.
|
||||
*/
|
||||
<T> ExecutableUpsertById<T> upsertById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingUpsertById<T> extends OneAndAllEntity<T> {
|
||||
|
||||
/**
|
||||
* Upsert one entity.
|
||||
*
|
||||
* @return Upserted entity.
|
||||
*/
|
||||
@Override
|
||||
T one(T object);
|
||||
|
||||
/**
|
||||
* Insert a collection of entities.
|
||||
*
|
||||
* @return Inserted entities
|
||||
*/
|
||||
@Override
|
||||
Collection<? extends T> all(Collection<? extends T> objects);
|
||||
|
||||
}
|
||||
|
||||
interface UpsertByIdWithCollection<T> extends TerminatingUpsertById<T>, WithCollection<T> {
|
||||
|
||||
TerminatingUpsertById<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use.
|
||||
*/
|
||||
interface UpsertByIdWithOptions<T> extends TerminatingUpsertById<T>, WithUpsertOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingUpsertById<T> withOptions(UpsertOptions options);
|
||||
}
|
||||
|
||||
interface UpsertByIdWithDurability<T> extends UpsertByIdWithCollection<T>, WithDurability<T> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface UpsertByIdInCollection<T> extends UpsertByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
UpsertByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
UpsertByIdWithCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface UpsertByIdInScope<T> extends UpsertByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
UpsertByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
UpsertByIdWithCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
interface UpsertByIdWithDurability<T> extends UpsertByIdInScope<T>, WithDurability<T> {
|
||||
@Override
|
||||
UpsertByIdInScope<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
|
||||
@Override
|
||||
UpsertByIdInScope<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
|
||||
}
|
||||
|
||||
interface UpsertByIdWithExpiry<T> extends UpsertByIdWithDurability<T>, WithExpiry<T> {
|
||||
|
||||
@Override
|
||||
UpsertByIdWithDurability<T> withExpiry(Duration expiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing KV operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to upsert
|
||||
*/
|
||||
interface ExecutableUpsertById<T> extends UpsertByIdWithExpiry<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,13 @@ package org.springframework.data.couchbase.core;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.data.couchbase.core.ReactiveUpsertByIdOperationSupport.ReactiveUpsertByIdSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
import com.couchbase.client.java.kv.UpsertOptions;
|
||||
|
||||
public class ExecutableUpsertByIdOperationSupport implements ExecutableUpsertByIdOperation {
|
||||
|
||||
@@ -36,7 +37,7 @@ public class ExecutableUpsertByIdOperationSupport implements ExecutableUpsertByI
|
||||
@Override
|
||||
public <T> ExecutableUpsertById<T> upsertById(final Class<T> domainType) {
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, null, null, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
DurabilityLevel.NONE, null);
|
||||
}
|
||||
|
||||
@@ -44,25 +45,29 @@ public class ExecutableUpsertByIdOperationSupport implements ExecutableUpsertByI
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final UpsertOptions options;
|
||||
private final PersistTo persistTo;
|
||||
private final ReplicateTo replicateTo;
|
||||
private final DurabilityLevel durabilityLevel;
|
||||
private final Duration expiry;
|
||||
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,
|
||||
final Duration expiry) {
|
||||
ExecutableUpsertByIdSupport(final CouchbaseTemplate template, final Class<T> domainType, final String scope,
|
||||
final String collection, final UpsertOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
final DurabilityLevel durabilityLevel, final Duration expiry) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.persistTo = persistTo;
|
||||
this.replicateTo = replicateTo;
|
||||
this.durabilityLevel = durabilityLevel;
|
||||
this.expiry = expiry;
|
||||
this.reactiveSupport = new ReactiveUpsertByIdSupport<>(template.reactive(),
|
||||
domainType, collection, persistTo, replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
|
||||
this.reactiveSupport = new ReactiveUpsertByIdSupport<>(template.reactive(), domainType, scope, collection,
|
||||
options, persistTo, replicateTo, durabilityLevel, expiry, new NonReactiveSupportWrapper(template.support()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -76,31 +81,45 @@ public class ExecutableUpsertByIdOperationSupport implements ExecutableUpsertByI
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingUpsertById<T> inCollection(final String collection) {
|
||||
public TerminatingUpsertById<T> withOptions(final UpsertOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdWithOptions<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdWithCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
public UpsertByIdInScope<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdWithCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
public UpsertByIdInScope<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
Assert.notNull(persistTo, "PersistTo must not be null.");
|
||||
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdWithDurability<T> withExpiry(final Duration expiry) {
|
||||
Assert.notNull(expiry, "expiry must not be null.");
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo,
|
||||
return new ExecutableUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,9 +18,11 @@ package org.springframework.data.couchbase.core;
|
||||
|
||||
import org.springframework.data.couchbase.CouchbaseClientFactory;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
|
||||
/**
|
||||
* Defines common operations on the Couchbase data source, most commonly implemented by {@link ReactiveCouchbaseTemplate}.
|
||||
* Defines common operations on the Couchbase data source, most commonly implemented by
|
||||
* {@link ReactiveCouchbaseTemplate}.
|
||||
*/
|
||||
public interface ReactiveCouchbaseOperations extends ReactiveFluentCouchbaseOperations {
|
||||
|
||||
@@ -44,4 +46,9 @@ public interface ReactiveCouchbaseOperations extends ReactiveFluentCouchbaseOper
|
||||
*/
|
||||
CouchbaseClientFactory getCouchbaseClientFactory();
|
||||
|
||||
/**
|
||||
* @@return the pseudoArgs from the ThreadLocal field of the CouchbaseOperations
|
||||
*/
|
||||
PseudoArgs<?> getPseudoArgs();
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.data.couchbase.CouchbaseClientFactory;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.convert.translation.JacksonTranslationService;
|
||||
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
|
||||
import com.couchbase.client.java.Collection;
|
||||
|
||||
@@ -42,13 +43,14 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A
|
||||
private final CouchbaseConverter converter;
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
private final ReactiveCouchbaseTemplateSupport templateSupport;
|
||||
private ThreadLocal<PseudoArgs<?>> threadLocalArgs = new ThreadLocal<>();
|
||||
|
||||
public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter) {
|
||||
this(clientFactory, converter, new JacksonTranslationService());
|
||||
}
|
||||
|
||||
public ReactiveCouchbaseTemplate(final CouchbaseClientFactory clientFactory, final CouchbaseConverter converter,
|
||||
final TranslationService translationService) {
|
||||
final TranslationService translationService) {
|
||||
this.clientFactory = clientFactory;
|
||||
this.converter = converter;
|
||||
this.exceptionTranslator = clientFactory.getExceptionTranslator();
|
||||
@@ -155,4 +157,12 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A
|
||||
templateSupport.setApplicationContext(applicationContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public PseudoArgs<?> getPseudoArgs() {
|
||||
return threadLocalArgs == null ? null : threadLocalArgs.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,9 +20,18 @@ import reactor.core.publisher.Mono;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllExistsReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithExistsOptions;
|
||||
|
||||
import com.couchbase.client.java.kv.ExistsOptions;
|
||||
/**
|
||||
* Exists Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveExistsByIdOperation {
|
||||
|
||||
/**
|
||||
@@ -30,6 +39,9 @@ public interface ReactiveExistsByIdOperation {
|
||||
*/
|
||||
ReactiveExistsById existsById();
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingExistsById extends OneAndAllExistsReactive {
|
||||
|
||||
/**
|
||||
@@ -38,6 +50,7 @@ public interface ReactiveExistsByIdOperation {
|
||||
* @param id the ID to perform the operation on.
|
||||
* @return true if the document exists, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
Mono<Boolean> one(String id);
|
||||
|
||||
/**
|
||||
@@ -46,21 +59,53 @@ public interface ReactiveExistsByIdOperation {
|
||||
* @param ids the ids to check.
|
||||
* @return a map consisting of the document IDs as the keys and if they exist as the value.
|
||||
*/
|
||||
@Override
|
||||
Mono<Map<String, Boolean>> all(Collection<String> ids);
|
||||
|
||||
}
|
||||
|
||||
interface ExistsByIdWithCollection extends TerminatingExistsById, WithCollection {
|
||||
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*/
|
||||
interface ExistsByIdWithOptions extends TerminatingExistsById, WithExistsOptions {
|
||||
/**
|
||||
* Allows to specify a different collection than the default one configured.
|
||||
* Fluent method to specify options to use for execution.
|
||||
*
|
||||
* @param collection the collection to use in this scope.
|
||||
* @param options to use for execution
|
||||
*/
|
||||
TerminatingExistsById inCollection(String collection);
|
||||
|
||||
@Override
|
||||
TerminatingExistsById withOptions(ExistsOptions options);
|
||||
}
|
||||
|
||||
interface ReactiveExistsById extends ExistsByIdWithCollection {}
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*/
|
||||
interface ExistsByIdInCollection extends ExistsByIdWithOptions, InCollection {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
ExistsByIdWithOptions inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*/
|
||||
interface ExistsByIdInScope extends ExistsByIdInCollection, InScope {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
ExistsByIdInCollection inScope(String scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing KV exists operations in a fluent way.
|
||||
*/
|
||||
interface ReactiveExistsById extends ExistsByIdInScope {}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.core;
|
||||
|
||||
import static com.couchbase.client.java.kv.ExistsOptions.*;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.function.Tuple2;
|
||||
@@ -25,13 +23,18 @@ import reactor.util.function.Tuples;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.java.kv.ExistsOptions;
|
||||
import com.couchbase.client.java.kv.ExistsResult;
|
||||
|
||||
public class ReactiveExistsByIdOperationSupport implements ReactiveExistsByIdOperation {
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReactiveExistsByIdOperationSupport.class);
|
||||
|
||||
ReactiveExistsByIdOperationSupport(ReactiveCouchbaseTemplate template) {
|
||||
this.template = template;
|
||||
@@ -39,23 +42,32 @@ public class ReactiveExistsByIdOperationSupport implements ReactiveExistsByIdOpe
|
||||
|
||||
@Override
|
||||
public ReactiveExistsById existsById() {
|
||||
return new ReactiveExistsByIdSupport(template, null);
|
||||
return new ReactiveExistsByIdSupport(template, null, null, null);
|
||||
}
|
||||
|
||||
static class ReactiveExistsByIdSupport implements ReactiveExistsById {
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final ExistsOptions options;
|
||||
|
||||
ReactiveExistsByIdSupport(final ReactiveCouchbaseTemplate template, final String collection) {
|
||||
ReactiveExistsByIdSupport(final ReactiveCouchbaseTemplate template, final String scope, final String collection,
|
||||
final ExistsOptions options) {
|
||||
this.template = template;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> one(final String id) {
|
||||
return Mono.just(id).flatMap(
|
||||
docId -> template.getCollection(collection).reactive().exists(id, existsOptions()).map(ExistsResult::exists))
|
||||
PseudoArgs<ExistsOptions> pArgs = new PseudoArgs<>(template, scope, collection,
|
||||
options != null ? options : ExistsOptions.existsOptions());
|
||||
LOG.trace("statement: {} scope: {} collection: {}", "exitsById", pArgs.getScope(), pArgs.getCollection());
|
||||
return Mono.just(id)
|
||||
.flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
|
||||
.getCollection(pArgs.getCollection()).reactive().exists(id, pArgs.getOptions()).map(ExistsResult::exists))
|
||||
.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
@@ -72,9 +84,21 @@ public class ReactiveExistsByIdOperationSupport implements ReactiveExistsByIdOpe
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingExistsById inCollection(final String collection) {
|
||||
public ExistsByIdWithOptions inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveExistsByIdSupport(template, collection);
|
||||
return new ReactiveExistsByIdSupport(template, scope, collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingExistsById withOptions(final ExistsOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveExistsByIdSupport(template, scope, collection, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExistsByIdInCollection inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveExistsByIdSupport(template, scope, collection, options);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,12 +20,22 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithAnalyticsConsistency;
|
||||
import org.springframework.data.couchbase.core.support.WithAnalyticsOptions;
|
||||
import org.springframework.data.couchbase.core.support.WithAnalyticsQuery;
|
||||
|
||||
import com.couchbase.client.java.analytics.AnalyticsOptions;
|
||||
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
|
||||
|
||||
/**
|
||||
* FindByAnalytics Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveFindByAnalyticsOperation {
|
||||
|
||||
/**
|
||||
@@ -36,7 +46,7 @@ public interface ReactiveFindByAnalyticsOperation {
|
||||
<T> ReactiveFindByAnalytics<T> findByAnalytics(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Compose find execution by calling one of the terminating methods.
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingFindByAnalytics<T> extends OneAndAllReactive {
|
||||
|
||||
@@ -90,8 +100,53 @@ public interface ReactiveFindByAnalyticsOperation {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use.
|
||||
*/
|
||||
interface FindByAnalyticsWithOptions<T> extends FindByAnalyticsWithQuery<T>, WithAnalyticsOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingFindByAnalytics<T> withOptions(AnalyticsOptions options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByAnalyticsInCollection<T> extends FindByAnalyticsWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
FindByAnalyticsWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByAnalyticsInScope<T> extends FindByAnalyticsInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
FindByAnalyticsInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
interface FindByAnalyticsConsistentWith<T> extends FindByAnalyticsWithQuery<T> {
|
||||
interface FindByAnalyticsConsistentWith<T> extends FindByAnalyticsInScope<T> {
|
||||
|
||||
/**
|
||||
* Allows to override the default scan consistency.
|
||||
@@ -103,17 +158,34 @@ public interface ReactiveFindByAnalyticsOperation {
|
||||
|
||||
}
|
||||
|
||||
interface FindByAnalyticsWithConsistency<T> extends FindByAnalyticsConsistentWith<T>, WithAnalyticsConsistency<T> {
|
||||
interface FindByAnalyticsWithConsistency<T> extends FindByAnalyticsInScope<T>, WithAnalyticsConsistency<T> {
|
||||
|
||||
/**
|
||||
* Allows to override the default scan consistency.
|
||||
*
|
||||
* @param scanConsistency the custom scan consistency to use for this analytics query.
|
||||
*/
|
||||
@Override
|
||||
FindByAnalyticsWithQuery<T> withConsistency(AnalyticsScanConsistency scanConsistency);
|
||||
|
||||
}
|
||||
|
||||
interface ReactiveFindByAnalytics<T> extends FindByAnalyticsWithConsistency<T> {}
|
||||
/**
|
||||
* Result type override (Optional).
|
||||
*/
|
||||
interface FindByAnalyticsWithProjection<T> extends FindByAnalyticsWithConsistency<T> {
|
||||
|
||||
/**
|
||||
* Define the target type fields should be mapped to. <br />
|
||||
* Skip this step if you are anyway only interested in the original domain type.
|
||||
*
|
||||
* @param returnType must not be {@literal null}.
|
||||
* @return new instance of {@link FindByAnalyticsWithConsistency}.
|
||||
* @throws IllegalArgumentException if returnType is {@literal null}.
|
||||
*/
|
||||
<R> FindByAnalyticsWithConsistency<R> as(Class<R> returnType);
|
||||
}
|
||||
|
||||
interface ReactiveFindByAnalytics<T> extends FindByAnalyticsWithProjection<T>, FindByAnalyticsConsistentWith<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.core;
|
||||
|
||||
import com.couchbase.client.java.query.ReactiveQueryResult;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
|
||||
import org.springframework.data.couchbase.core.support.TemplateUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.error.CouchbaseException;
|
||||
import com.couchbase.client.java.analytics.AnalyticsOptions;
|
||||
@@ -39,41 +39,60 @@ public class ReactiveFindByAnalyticsOperationSupport implements ReactiveFindByAn
|
||||
|
||||
@Override
|
||||
public <T> ReactiveFindByAnalytics<T> findByAnalytics(final Class<T> domainType) {
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, ALL_QUERY, AnalyticsScanConsistency.NOT_BOUNDED,
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, domainType, ALL_QUERY, null, null, null, null,
|
||||
template.support());
|
||||
}
|
||||
|
||||
static class ReactiveFindByAnalyticsSupport<T> implements ReactiveFindByAnalytics<T> {
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final Class<?> domainType;
|
||||
private final Class<T> returnType;
|
||||
private final AnalyticsQuery query;
|
||||
private final AnalyticsScanConsistency scanConsistency;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final AnalyticsOptions options;
|
||||
private final ReactiveTemplateSupport support;
|
||||
|
||||
ReactiveFindByAnalyticsSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType,
|
||||
final AnalyticsQuery query, final AnalyticsScanConsistency scanConsistency, ReactiveTemplateSupport support) {
|
||||
ReactiveFindByAnalyticsSupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType,
|
||||
final Class<T> returnType, final AnalyticsQuery query, final AnalyticsScanConsistency scanConsistency,
|
||||
String scope, String collection, AnalyticsOptions options, ReactiveTemplateSupport support) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.returnType = returnType;
|
||||
this.query = query;
|
||||
this.scanConsistency = scanConsistency;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingFindByAnalytics<T> matching(AnalyticsQuery query) {
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, query, scanConsistency, support);
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public FindByAnalyticsWithQuery<T> consistentWith(AnalyticsScanConsistency scanConsistency) {
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, query, scanConsistency, support);
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByAnalyticsWithQuery<T> withConsistency(AnalyticsScanConsistency scanConsistency) {
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, query, scanConsistency, support);
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> FindByAnalyticsWithConsistency<R> as(final Class<R> returnType) {
|
||||
Assert.notNull(returnType, "returnType must not be null!");
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -114,7 +133,7 @@ public class ReactiveFindByAnalyticsOperationSupport implements ReactiveFindByAn
|
||||
cas = row.getLong(TemplateUtils.SELECT_CAS);
|
||||
row.removeKey(TemplateUtils.SELECT_ID);
|
||||
row.removeKey(TemplateUtils.SELECT_CAS);
|
||||
return template.support().decodeEntity(id, row.toString(), cas, domainType);
|
||||
return support.decodeEntity(id, row.toString(), cas, returnType);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -139,6 +158,27 @@ public class ReactiveFindByAnalyticsOperationSupport implements ReactiveFindByAn
|
||||
return count().map(count -> count > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingFindByAnalytics<T> withOptions(final AnalyticsOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByAnalyticsInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByAnalyticsWithConsistency<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveFindByAnalyticsSupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, support);
|
||||
}
|
||||
|
||||
private String assembleEntityQuery(final boolean count) {
|
||||
final String bucket = "`" + template.getBucketName() + "`";
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,10 +20,20 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllIdReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithGetOptions;
|
||||
import org.springframework.data.couchbase.core.support.WithProjectionId;
|
||||
|
||||
import com.couchbase.client.java.kv.GetOptions;
|
||||
|
||||
/**
|
||||
* Get Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveFindByIdOperation {
|
||||
|
||||
/**
|
||||
@@ -33,6 +43,11 @@ public interface ReactiveFindByIdOperation {
|
||||
*/
|
||||
<T> ReactiveFindById<T> findById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface TerminatingFindById<T> extends OneAndAllIdReactive<T> {
|
||||
|
||||
/**
|
||||
@@ -53,27 +68,67 @@ public interface ReactiveFindByIdOperation {
|
||||
|
||||
}
|
||||
|
||||
interface FindByIdWithCollection<T> extends TerminatingFindById<T>, WithCollection<T> {
|
||||
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByIdWithOptions<T> extends TerminatingFindById<T>, WithGetOptions<T> {
|
||||
/**
|
||||
* Allows to specify a different collection than the default one configured.
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param collection the collection to use in this scope.
|
||||
* @param options options to use for execution
|
||||
*/
|
||||
TerminatingFindById<T> inCollection(String collection);
|
||||
@Override
|
||||
TerminatingFindById<T> withOptions(GetOptions options);
|
||||
}
|
||||
|
||||
interface FindByIdWithProjection<T> extends FindByIdWithCollection<T>, WithProjectionId<T> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByIdInCollection<T> extends FindByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
FindByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByIdInScope<T> extends FindByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
FindByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
interface FindByIdWithProjection<T> extends FindByIdInScope<T>, WithProjectionId<T> {
|
||||
|
||||
/**
|
||||
* Load only certain fields for the document.
|
||||
*
|
||||
* @param fields the projected fields to load.
|
||||
*/
|
||||
FindByIdWithCollection<T> project(String... fields);
|
||||
FindByIdInCollection<T> project(String... fields);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing query operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to use for the results
|
||||
*/
|
||||
interface ReactiveFindById<T> extends FindByIdWithProjection<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.error.DocumentNotFoundException;
|
||||
@@ -34,28 +37,34 @@ public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperati
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByIdOperationSupport.class);
|
||||
|
||||
ReactiveFindByIdOperationSupport(ReactiveCouchbaseTemplate template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ReactiveFindById<T> findById(Class<T> domainType) {
|
||||
return new ReactiveFindByIdSupport<>(template, domainType, null, null, template.support());
|
||||
return new ReactiveFindByIdSupport<>(template, domainType, null, null, null, null, template.support());
|
||||
}
|
||||
|
||||
static class ReactiveFindByIdSupport<T> implements ReactiveFindById<T> {
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final GetOptions options;
|
||||
private final List<String> fields;
|
||||
private final ReactiveTemplateSupport support;
|
||||
|
||||
ReactiveFindByIdSupport(ReactiveCouchbaseTemplate template, Class<T> domainType, String collection,
|
||||
List<String> fields, ReactiveTemplateSupport support) {
|
||||
ReactiveFindByIdSupport(ReactiveCouchbaseTemplate template, Class<T> domainType, String scope, String collection,
|
||||
GetOptions options, List<String> fields, ReactiveTemplateSupport support) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.fields = fields;
|
||||
this.support = support;
|
||||
}
|
||||
@@ -63,11 +72,17 @@ public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperati
|
||||
@Override
|
||||
public Mono<T> one(final String id) {
|
||||
return Mono.just(id).flatMap(docId -> {
|
||||
GetOptions options = getOptions().transcoder(RawJsonTranscoder.INSTANCE);
|
||||
if (fields != null && !fields.isEmpty()) {
|
||||
options.project(fields);
|
||||
GetOptions gOptions = options != null ? options : getOptions();
|
||||
if (gOptions.build().transcoder() == null) {
|
||||
gOptions.transcoder(RawJsonTranscoder.INSTANCE);
|
||||
}
|
||||
return template.getCollection(collection).reactive().get(docId, options);
|
||||
if (fields != null && !fields.isEmpty()) {
|
||||
gOptions.project(fields);
|
||||
}
|
||||
PseudoArgs<GetOptions> pArgs = new PseudoArgs(template, scope, collection, gOptions);
|
||||
LOG.trace("statement: {} scope: {} collection: {}", "findById", pArgs.getScope(), pArgs.getCollection());
|
||||
return template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getCollection(pArgs.getCollection())
|
||||
.reactive().get(docId, pArgs.getOptions());
|
||||
}).flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType))
|
||||
.onErrorResume(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
@@ -91,15 +106,28 @@ public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperati
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingFindById<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveFindByIdSupport<>(template, domainType, collection, fields, support);
|
||||
public TerminatingFindById<T> withOptions(final GetOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByIdWithCollection<T> project(String... fields) {
|
||||
public FindByIdWithOptions<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, fields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByIdInScope<T> project(String... fields) {
|
||||
Assert.notEmpty(fields, "Fields must not be null nor empty.");
|
||||
return new ReactiveFindByIdSupport<>(template, domainType, collection, Arrays.asList(fields), support);
|
||||
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields),
|
||||
support);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,17 +21,20 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithConsistency;
|
||||
import org.springframework.data.couchbase.core.support.WithDistinct;
|
||||
import org.springframework.data.couchbase.core.support.WithProjection;
|
||||
import org.springframework.data.couchbase.core.support.WithQuery;
|
||||
import org.springframework.data.couchbase.core.support.WithQueryOptions;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* ReactiveFindByQueryOperation
|
||||
* ReactiveFindByQueryOperation<br>
|
||||
* Queries the N1QL service.
|
||||
*
|
||||
* @author Michael Nitschinger
|
||||
* @author Michael Reiche
|
||||
@@ -39,7 +42,7 @@ import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
public interface ReactiveFindByQueryOperation {
|
||||
|
||||
/**
|
||||
* Queries the N1QL service.
|
||||
* Create the operation for the domainType
|
||||
*
|
||||
* @param domainType the entity type to use for the results.
|
||||
*/
|
||||
@@ -86,18 +89,19 @@ public interface ReactiveFindByQueryOperation {
|
||||
*/
|
||||
Mono<Boolean> exists();
|
||||
|
||||
QueryOptions buildOptions(QueryOptions options);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual query execution.
|
||||
* Fluent methods to filter by query
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithQuery<T> extends TerminatingFindByQuery<T>, WithQuery<T> {
|
||||
|
||||
/**
|
||||
* Set the filter for the query to be used.
|
||||
* Set the filter {@link Query} to be used.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if query is {@literal null}.
|
||||
@@ -108,7 +112,7 @@ public interface ReactiveFindByQueryOperation {
|
||||
* Set the filter {@link QueryCriteriaDefinition criteria} to be used.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
* @return new instance of {@link ExecutableFindByQueryOperation.ExecutableFindByQuery}.
|
||||
* @return new instance of {@link TerminatingFindByQuery}.
|
||||
* @throws IllegalArgumentException if criteria is {@literal null}.
|
||||
*/
|
||||
default TerminatingFindByQuery<T> matching(QueryCriteriaDefinition criteria) {
|
||||
@@ -118,27 +122,42 @@ public interface ReactiveFindByQueryOperation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Collection override (optional).
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryInCollection<T> extends FindByQueryWithQuery<T>, WithCollection<T> {
|
||||
|
||||
interface FindByQueryWithOptions<T> extends FindByQueryWithQuery<T>, WithQueryOptions<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 FindByQueryWithProjection}.
|
||||
* @throws IllegalArgumentException if collection is {@literal null}.
|
||||
* @param options options to use for execution
|
||||
*/
|
||||
FindByQueryWithQuery<T> inCollection(String collection);
|
||||
TerminatingFindByQuery<T> withOptions(QueryOptions options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @see FindByQueryWithConsistency
|
||||
* Fluent method to specify the collection
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryInCollection<T> extends FindByQueryWithOptions<T>, InCollection<T> {
|
||||
FindByQueryWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryInScope<T> extends FindByQueryInCollection<T>, InScope<T> {
|
||||
FindByQueryInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be removed at the next major release. use WithConsistency instead
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
@Deprecated
|
||||
interface FindByQueryConsistentWith<T> extends FindByQueryInCollection<T> {
|
||||
interface FindByQueryConsistentWith<T> extends FindByQueryInScope<T> {
|
||||
|
||||
/**
|
||||
* Allows to override the default scan consistency.
|
||||
@@ -146,10 +165,15 @@ public interface ReactiveFindByQueryOperation {
|
||||
* @param scanConsistency the custom scan consistency to use for this query.
|
||||
*/
|
||||
@Deprecated
|
||||
FindByQueryInCollection<T> consistentWith(QueryScanConsistency scanConsistency);
|
||||
FindByQueryInScope<T> consistentWith(QueryScanConsistency scanConsistency);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithConsistency<T> extends FindByQueryConsistentWith<T>, WithConsistency<T> {
|
||||
|
||||
/**
|
||||
@@ -162,7 +186,9 @@ public interface ReactiveFindByQueryOperation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Result type override (optional).
|
||||
* Fluent method to specify a return type different than the the entity type to use for the results.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithProjection<T> extends FindByQueryWithConsistency<T> {
|
||||
|
||||
@@ -178,9 +204,9 @@ public interface ReactiveFindByQueryOperation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Distinct Find support.
|
||||
* Fluent method to specify DISTINCT fields
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjection<T>, WithDistinct<T> {
|
||||
|
||||
@@ -194,6 +220,11 @@ public interface ReactiveFindByQueryOperation {
|
||||
FindByQueryWithProjection<T> distinct(String[] distinctFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* provides methods for constructing query operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to use for the results
|
||||
*/
|
||||
interface ReactiveFindByQuery<T> extends FindByQueryWithDistinct<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,15 @@ package org.springframework.data.couchbase.core;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
import org.springframework.data.couchbase.core.support.TemplateUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.error.CouchbaseException;
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
import com.couchbase.client.java.query.ReactiveQueryResult;
|
||||
|
||||
@@ -38,14 +42,16 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindByQueryOperationSupport.class);
|
||||
|
||||
public ReactiveFindByQueryOperationSupport(final ReactiveCouchbaseTemplate template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ReactiveFindByQuery<T> findByQuery(final Class<T> domainType) {
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, domainType, ALL_QUERY,
|
||||
QueryScanConsistency.NOT_BOUNDED, null, null, template.support());
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, domainType, ALL_QUERY, null, null, null, null, null,
|
||||
template.support());
|
||||
}
|
||||
|
||||
static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
|
||||
@@ -56,23 +62,30 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
private final Query query;
|
||||
private final QueryScanConsistency scanConsistency;
|
||||
private final String collection;
|
||||
private String scope;
|
||||
private final String[] distinctFields;
|
||||
// this would hold scanConsistency etc. from the fluent api if they were converted from standalone fields
|
||||
// withScope(scopeName) could put raw("query_context",default:<bucket>.<scope>)
|
||||
// this is not the options argument in save( entity, options ). That becomes query.getCouchbaseOptions()
|
||||
private final QueryOptions options;
|
||||
private final ReactiveTemplateSupport support;
|
||||
|
||||
ReactiveFindByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType,
|
||||
final Class<T> returnType, final Query query, final QueryScanConsistency scanConsistency,
|
||||
final String collection, final String[] distinctFields, final ReactiveTemplateSupport support) {
|
||||
this.support = support;
|
||||
final Class<T> returnType, final Query query, final QueryScanConsistency scanConsistency, final String scope,
|
||||
final String collection, final QueryOptions options, final String[] distinctFields,
|
||||
final ReactiveTemplateSupport support) {
|
||||
Assert.notNull(domainType, "domainType must not be null!");
|
||||
Assert.notNull(returnType, "returnType must not be null!");
|
||||
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.returnType = returnType;
|
||||
this.query = query;
|
||||
this.scanConsistency = scanConsistency;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.distinctFields = distinctFields;
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,42 +96,56 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
} else {
|
||||
scanCons = scanConsistency;
|
||||
}
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanCons, collection,
|
||||
distinctFields, support);
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
|
||||
options, distinctFields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryInCollection<T> inCollection(String collection) {
|
||||
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithConsistency<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields, support);
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency) {
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields, support);
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithConsistency<T> withConsistency(QueryScanConsistency scanConsistency) {
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields, support);
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> FindByQueryWithConsistency<R> as(Class<R> returnType) {
|
||||
Assert.notNull(returnType, "returnType must not be null!");
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields, support);
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindByQueryWithDistinct<T> distinct(String[] distinctFields) {
|
||||
Assert.notNull(distinctFields, "distinctFields must not be null!");
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, collection,
|
||||
distinctFields, support);
|
||||
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
|
||||
collection, options, distinctFields, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,12 +161,14 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
@Override
|
||||
public Flux<T> all() {
|
||||
return Flux.defer(() -> {
|
||||
String statement = assembleEntityQuery(false, distinctFields);
|
||||
Mono<ReactiveQueryResult> allResult = this.collection == null
|
||||
PseudoArgs<QueryOptions> pArgs = new PseudoArgs(template, scope, collection, options);
|
||||
String statement = assembleEntityQuery(false, distinctFields, pArgs.getCollection());
|
||||
LOG.trace("statement: {} {}", "findByQuery", statement);
|
||||
Mono<ReactiveQueryResult> allResult = pArgs.getScope() == null
|
||||
? template.getCouchbaseClientFactory().getCluster().reactive().query(statement,
|
||||
query.buildQueryOptions(scanConsistency))
|
||||
: template.getCouchbaseClientFactory().getScope().reactive().query(statement,
|
||||
query.buildQueryOptions(scanConsistency));
|
||||
buildOptions(pArgs.getOptions()))
|
||||
: template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement,
|
||||
buildOptions(pArgs.getOptions()));
|
||||
return allResult.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
@@ -170,15 +199,23 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions buildOptions(QueryOptions options) {
|
||||
QueryOptions opts = query.buildQueryOptions(options, scanConsistency);
|
||||
return opts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Long> count() {
|
||||
return Mono.defer(() -> {
|
||||
String statement = assembleEntityQuery(true, distinctFields);
|
||||
PseudoArgs<QueryOptions> pArgs = new PseudoArgs(template, scope, collection, options);
|
||||
String statement = assembleEntityQuery(true, distinctFields, pArgs.getCollection());
|
||||
LOG.trace("statement: {} {}", "findByQuery", statement);
|
||||
Mono<ReactiveQueryResult> countResult = this.collection == null
|
||||
? template.getCouchbaseClientFactory().getCluster().reactive().query(statement,
|
||||
query.buildQueryOptions(scanConsistency))
|
||||
: template.getCouchbaseClientFactory().getScope().reactive().query(statement,
|
||||
query.buildQueryOptions(scanConsistency));
|
||||
buildOptions(pArgs.getOptions()))
|
||||
: template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement,
|
||||
buildOptions(pArgs.getOptions()));
|
||||
return countResult.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
@@ -193,12 +230,11 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> exists() {
|
||||
return count().map(count -> count > 0);
|
||||
} // not efficient, just need the first one
|
||||
return count().map(count -> count > 0); // not efficient, just need the first one
|
||||
}
|
||||
|
||||
private String assembleEntityQuery(final boolean count, String[] distinctFields) {
|
||||
return query.toN1qlSelectString(template, this.collection, this.domainType, this.returnType, count,
|
||||
distinctFields);
|
||||
private String assembleEntityQuery(final boolean count, String[] distinctFields, String collection) {
|
||||
return query.toN1qlSelectString(template, collection, this.domainType, this.returnType, count, distinctFields);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,25 +21,100 @@ import reactor.core.publisher.Mono;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.AnyIdReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.WithGetAnyReplicaOptions;
|
||||
|
||||
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
|
||||
|
||||
/**
|
||||
* Find by id from replicas Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveFindFromReplicasByIdOperation {
|
||||
|
||||
/**
|
||||
* Loads a document from a replica.
|
||||
*
|
||||
* @param domainType the entity type to use for the results.
|
||||
*/
|
||||
<T> ReactiveFindFromReplicasById<T> findFromReplicasById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual get execution.
|
||||
*/
|
||||
interface TerminatingFindFromReplicasById<T> extends AnyIdReactive<T> {
|
||||
|
||||
/**
|
||||
* Finds one document based on the given ID.
|
||||
*
|
||||
* @param id the document ID.
|
||||
* @return the entity if found.
|
||||
*/
|
||||
Mono<T> any(String id);
|
||||
|
||||
/**
|
||||
* Finds a list of documents based on the given IDs.
|
||||
*
|
||||
* @param ids the document ID ids.
|
||||
* @return the list of found entities.
|
||||
*/
|
||||
Flux<? extends T> any(Collection<String> ids);
|
||||
|
||||
}
|
||||
|
||||
interface FindFromReplicasByIdWithCollection<T> extends TerminatingFindFromReplicasById<T>, WithCollection<T> {
|
||||
|
||||
TerminatingFindFromReplicasById<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindFromReplicasByIdWithOptions<T> extends TerminatingFindFromReplicasById<T>, WithGetAnyReplicaOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingFindFromReplicasById<T> withOptions(GetAnyReplicaOptions options);
|
||||
}
|
||||
|
||||
interface ReactiveFindFromReplicasById<T> extends FindFromReplicasByIdWithCollection<T> {}
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindFromReplicasByIdInCollection<T> extends FindFromReplicasByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
FindFromReplicasByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface FindFromReplicasByIdInScope<T> extends FindFromReplicasByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
FindFromReplicasByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing get operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to use for the results
|
||||
*/
|
||||
interface ReactiveFindFromReplicasById<T> extends FindFromReplicasByIdInScope<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.java.codec.RawJsonTranscoder;
|
||||
@@ -30,6 +33,7 @@ import com.couchbase.client.java.kv.GetAnyReplicaOptions;
|
||||
public class ReactiveFindFromReplicasByIdOperationSupport implements ReactiveFindFromReplicasByIdOperation {
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReactiveFindFromReplicasByIdOperationSupport.class);
|
||||
|
||||
ReactiveFindFromReplicasByIdOperationSupport(ReactiveCouchbaseTemplate template) {
|
||||
this.template = template;
|
||||
@@ -37,7 +41,8 @@ public class ReactiveFindFromReplicasByIdOperationSupport implements ReactiveFin
|
||||
|
||||
@Override
|
||||
public <T> ReactiveFindFromReplicasById<T> findFromReplicasById(Class<T> domainType) {
|
||||
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, domainType, null, template.support());
|
||||
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, domainType, null, null, null,
|
||||
template.support());
|
||||
}
|
||||
|
||||
static class ReactiveFindFromReplicasByIdSupport<T> implements ReactiveFindFromReplicasById<T> {
|
||||
@@ -45,23 +50,33 @@ public class ReactiveFindFromReplicasByIdOperationSupport implements ReactiveFin
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final Class<?> domainType;
|
||||
private final Class<T> returnType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final GetAnyReplicaOptions options;
|
||||
private final ReactiveTemplateSupport support;
|
||||
|
||||
ReactiveFindFromReplicasByIdSupport(ReactiveCouchbaseTemplate template, Class<?> domainType, Class<T> returnType,
|
||||
String collection, ReactiveTemplateSupport support) {
|
||||
String scope, String collection, GetAnyReplicaOptions options, ReactiveTemplateSupport support) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.returnType = returnType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.support = support;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<T> any(final String id) {
|
||||
return Mono.just(id).flatMap(docId -> {
|
||||
GetAnyReplicaOptions options = getAnyReplicaOptions().transcoder(RawJsonTranscoder.INSTANCE);
|
||||
return template.getCollection(collection).reactive().getAnyReplica(docId, options);
|
||||
GetAnyReplicaOptions garOptions = options != null ? options : getAnyReplicaOptions();
|
||||
if (garOptions.build().transcoder() == null) {
|
||||
garOptions.transcoder(RawJsonTranscoder.INSTANCE);
|
||||
}
|
||||
PseudoArgs<GetAnyReplicaOptions> pArgs = new PseudoArgs<>(template, scope, collection, garOptions);
|
||||
LOG.trace("statement: {} scope: {} collection: {}", "getAnyReplica", pArgs.getScope(), pArgs.getCollection());
|
||||
return template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getCollection(pArgs.getCollection())
|
||||
.reactive().getAnyReplica(docId, pArgs.getOptions());
|
||||
}).flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), returnType))
|
||||
.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
@@ -78,9 +93,24 @@ public class ReactiveFindFromReplicasByIdOperationSupport implements ReactiveFin
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingFindFromReplicasById<T> inCollection(final String collection) {
|
||||
public TerminatingFindFromReplicasById<T> withOptions(final GetAnyReplicaOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options,
|
||||
support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindFromReplicasByIdWithOptions<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType, collection, support);
|
||||
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options,
|
||||
support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FindFromReplicasByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveFindFromReplicasByIdSupport<>(template, domainType, returnType, scope, collection, options,
|
||||
support);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,43 +21,117 @@ import reactor.core.publisher.Mono;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllEntityReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithInsertOptions;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.InsertOptions;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
/**
|
||||
* Insert Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveInsertByIdOperation {
|
||||
|
||||
/**
|
||||
* Insert using the KV service.
|
||||
*
|
||||
* @param domainType the entity type to insert.
|
||||
*/
|
||||
<T> ReactiveInsertById<T> insertById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingInsertById<T> extends OneAndAllEntityReactive<T> {
|
||||
|
||||
/**
|
||||
* Insert one entity.
|
||||
*
|
||||
* @return Inserted entity.
|
||||
*/
|
||||
@Override
|
||||
Mono<T> one(T object);
|
||||
|
||||
/**
|
||||
* Insert a collection of entities.
|
||||
*
|
||||
* @return Inserted entities
|
||||
*/
|
||||
@Override
|
||||
Flux<? extends T> all(Collection<? extends T> objects);
|
||||
|
||||
InsertOptions buildOptions(InsertOptions options, CouchbaseDocument doc);
|
||||
|
||||
}
|
||||
|
||||
interface InsertByIdWithCollection<T> extends TerminatingInsertById<T>, WithCollection<T> {
|
||||
|
||||
TerminatingInsertById<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*/
|
||||
interface InsertByIdWithOptions<T> extends TerminatingInsertById<T>, WithInsertOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution.
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingInsertById<T> withOptions(InsertOptions options);
|
||||
}
|
||||
|
||||
interface InsertByIdWithDurability<T> extends InsertByIdWithCollection<T>, WithDurability<T> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*/
|
||||
interface InsertByIdInCollection<T> extends InsertByIdWithOptions<T>, InCollection<T> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
InsertByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
InsertByIdWithCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*/
|
||||
interface InsertByIdInScope<T> extends InsertByIdInCollection<T>, InScope<T> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
InsertByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
InsertByIdWithCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
interface InsertByIdWithDurability<T> extends InsertByIdInScope<T>, WithDurability<T> {
|
||||
|
||||
@Override
|
||||
InsertByIdInCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
|
||||
@Override
|
||||
InsertByIdInCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
|
||||
}
|
||||
|
||||
interface InsertByIdWithExpiry<T> extends InsertByIdWithDurability<T>, WithExpiry<T> {
|
||||
|
||||
@Override
|
||||
InsertByIdWithDurability<T> withExpiry(Duration expiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing KV insert operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to insert
|
||||
*/
|
||||
interface ReactiveInsertById<T> extends InsertByIdWithExpiry<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ import reactor.core.publisher.Mono;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
@@ -32,6 +34,7 @@ import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOperation {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReactiveInsertByIdOperationSupport.class);
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
|
||||
public ReactiveInsertByIdOperationSupport(final ReactiveCouchbaseTemplate template) {
|
||||
@@ -41,7 +44,7 @@ public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOpe
|
||||
@Override
|
||||
public <T> ReactiveInsertById<T> insertById(final Class<T> domainType) {
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, null, null, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
DurabilityLevel.NONE, null, template.support());
|
||||
}
|
||||
|
||||
@@ -49,19 +52,23 @@ public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOpe
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final InsertOptions options;
|
||||
private final PersistTo persistTo;
|
||||
private final ReplicateTo replicateTo;
|
||||
private final DurabilityLevel durabilityLevel;
|
||||
private final Duration expiry;
|
||||
private final ReactiveTemplateSupport support;
|
||||
|
||||
ReactiveInsertByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType,
|
||||
final String collection, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
ReactiveInsertByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final String scope,
|
||||
final String collection, final InsertOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
final DurabilityLevel durabilityLevel, Duration expiry, ReactiveTemplateSupport support) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.persistTo = persistTo;
|
||||
this.replicateTo = replicateTo;
|
||||
this.durabilityLevel = durabilityLevel;
|
||||
@@ -71,20 +78,23 @@ public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOpe
|
||||
|
||||
@Override
|
||||
public Mono<T> one(T object) {
|
||||
return (Mono<T>) Mono.just(object).flatMap(support::encodeEntity).flatMap(converted ->
|
||||
template.getCollection(collection).reactive()
|
||||
.insert(converted.getId(), converted.export(), buildInsertOptions(converted))
|
||||
.flatMap(result ->
|
||||
support.applyUpdatedId(object, converted.getId())
|
||||
.flatMap(
|
||||
updatedObject -> support.applyUpdatedCas(updatedObject, result.cas())))
|
||||
).onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
} else {
|
||||
return throwable;
|
||||
}
|
||||
});
|
||||
PseudoArgs<InsertOptions> pArgs = new PseudoArgs(template, scope, collection,
|
||||
options != null ? options : InsertOptions.insertOptions());
|
||||
LOG.trace("statement: {} scope: {} collection: {} options: {}", "insertById", pArgs.getScope(),
|
||||
pArgs.getCollection(), pArgs.getOptions());
|
||||
return Mono.just(object).flatMap(support::encodeEntity)
|
||||
.flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
|
||||
.getCollection(pArgs.getCollection()).reactive()
|
||||
.insert(converted.getId(), converted.export(), buildOptions(pArgs.getOptions(), converted))
|
||||
.flatMap(result -> support.applyUpdatedId(object, converted.getId())
|
||||
.flatMap(insertedObject -> (Mono<T>) support.applyUpdatedCas(insertedObject, result.cas()))))
|
||||
.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
} else {
|
||||
return throwable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,8 +102,9 @@ public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOpe
|
||||
return Flux.fromIterable(objects).flatMap(this::one);
|
||||
}
|
||||
|
||||
private InsertOptions buildInsertOptions(CouchbaseDocument doc) { // CouchbaseDocument converted
|
||||
final InsertOptions options = InsertOptions.insertOptions();
|
||||
@Override
|
||||
public InsertOptions buildOptions(InsertOptions options, CouchbaseDocument doc) { // CouchbaseDocument converted
|
||||
options = options != null ? options : InsertOptions.insertOptions();
|
||||
if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) {
|
||||
options.durability(persistTo, replicateTo);
|
||||
} else if (durabilityLevel != DurabilityLevel.NONE) {
|
||||
@@ -108,32 +119,46 @@ public class ReactiveInsertByIdOperationSupport implements ReactiveInsertByIdOpe
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingInsertById<T> inCollection(final String collection) {
|
||||
public TerminatingInsertById<T> withOptions(final InsertOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdWithOptions<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdWithCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
public InsertByIdInCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdWithCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
public InsertByIdInCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
Assert.notNull(persistTo, "PersistTo must not be null.");
|
||||
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsertByIdWithDurability<T> withExpiry(final Duration expiry) {
|
||||
Assert.notNull(expiry, "expiry must not be null.");
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveInsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,45 +20,105 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllIdReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithRemoveOptions;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.RemoveOptions;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
/**
|
||||
* Remove Operations on KV service.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveRemoveByIdOperation {
|
||||
|
||||
/**
|
||||
* Removes a document.
|
||||
*/
|
||||
ReactiveRemoveById removeById();
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingRemoveById extends OneAndAllIdReactive<RemoveResult> {
|
||||
|
||||
/**
|
||||
* Remove one document based on the given ID.
|
||||
*
|
||||
* @param id the document ID.
|
||||
* @return result of the remove
|
||||
*/
|
||||
@Override
|
||||
Mono<RemoveResult> one(String id);
|
||||
|
||||
/**
|
||||
* Remove the documents in the collection.
|
||||
*
|
||||
* @param ids the document IDs.
|
||||
* @return result of the removes.
|
||||
*/
|
||||
@Override
|
||||
Flux<RemoveResult> all(Collection<String> ids);
|
||||
|
||||
}
|
||||
|
||||
interface RemoveByIdWithCollection extends TerminatingRemoveById, WithCollection<RemoveResult> {
|
||||
|
||||
TerminatingRemoveById inCollection(String collection);
|
||||
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*/
|
||||
interface RemoveByIdWithOptions extends TerminatingRemoveById, WithRemoveOptions<RemoveResult> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options options to use for execution
|
||||
*/
|
||||
TerminatingRemoveById withOptions(RemoveOptions options);
|
||||
}
|
||||
|
||||
interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurability<RemoveResult> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*/
|
||||
interface RemoveByIdInCollection extends RemoveByIdWithOptions, InCollection<Object> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
RemoveByIdWithOptions inCollection(String collection);
|
||||
}
|
||||
|
||||
RemoveByIdWithCollection withDurability(DurabilityLevel durabilityLevel);
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*/
|
||||
interface RemoveByIdInScope extends RemoveByIdInCollection, InScope<Object> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
RemoveByIdInCollection inScope(String scope);
|
||||
}
|
||||
|
||||
RemoveByIdWithCollection withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
interface RemoveByIdWithDurability extends RemoveByIdInScope, WithDurability<RemoveResult> {
|
||||
@Override
|
||||
RemoveByIdInCollection withDurability(DurabilityLevel durabilityLevel);
|
||||
@Override
|
||||
RemoveByIdInCollection withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
|
||||
}
|
||||
|
||||
interface RemoveByIdWithCas extends RemoveByIdWithDurability {
|
||||
|
||||
RemoveByIdWithDurability withCas(Long cas);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing remove operations in a fluent way.
|
||||
*/
|
||||
interface ReactiveRemoveById extends RemoveByIdWithCas {}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
@@ -37,22 +38,28 @@ public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOpe
|
||||
|
||||
@Override
|
||||
public ReactiveRemoveById removeById() {
|
||||
return new ReactiveRemoveByIdSupport(template, null, PersistTo.NONE, ReplicateTo.NONE, DurabilityLevel.NONE, null);
|
||||
return new ReactiveRemoveByIdSupport(template, null, null, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
DurabilityLevel.NONE, null);
|
||||
}
|
||||
|
||||
static class ReactiveRemoveByIdSupport implements ReactiveRemoveById {
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final RemoveOptions options;
|
||||
private final PersistTo persistTo;
|
||||
private final ReplicateTo replicateTo;
|
||||
private final DurabilityLevel durabilityLevel;
|
||||
private final Long cas;
|
||||
|
||||
ReactiveRemoveByIdSupport(final ReactiveCouchbaseTemplate template, final String collection,
|
||||
final PersistTo persistTo, final ReplicateTo replicateTo, final DurabilityLevel durabilityLevel, Long cas) {
|
||||
ReactiveRemoveByIdSupport(final ReactiveCouchbaseTemplate template, final String scope, final String collection,
|
||||
final RemoveOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
final DurabilityLevel durabilityLevel, Long cas) {
|
||||
this.template = template;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.persistTo = persistTo;
|
||||
this.replicateTo = replicateTo;
|
||||
this.durabilityLevel = durabilityLevel;
|
||||
@@ -61,8 +68,13 @@ public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOpe
|
||||
|
||||
@Override
|
||||
public Mono<RemoveResult> one(final String id) {
|
||||
return Mono.just(id).flatMap(docId -> template.getCollection(collection).reactive()
|
||||
.remove(id, buildRemoveOptions()).map(r -> RemoveResult.from(docId, r))).onErrorMap(throwable -> {
|
||||
PseudoArgs<RemoveOptions> pArgs = new PseudoArgs(template, scope, collection,
|
||||
options != null ? options : RemoveOptions.removeOptions());
|
||||
return Mono.just(id)
|
||||
.flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
|
||||
.getCollection(pArgs.getCollection()).reactive().remove(id, buildRemoveOptions(pArgs.getOptions()))
|
||||
.map(r -> RemoveResult.from(docId, r)))
|
||||
.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
} else {
|
||||
@@ -76,8 +88,8 @@ public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOpe
|
||||
return Flux.fromIterable(ids).flatMap(this::one);
|
||||
}
|
||||
|
||||
private RemoveOptions buildRemoveOptions() {
|
||||
final RemoveOptions options = RemoveOptions.removeOptions();
|
||||
private RemoveOptions buildRemoveOptions(RemoveOptions options) {
|
||||
options = options != null ? options : RemoveOptions.removeOptions();
|
||||
if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) {
|
||||
options.durability(persistTo, replicateTo);
|
||||
} else if (durabilityLevel != DurabilityLevel.NONE) {
|
||||
@@ -90,28 +102,45 @@ public class ReactiveRemoveByIdOperationSupport implements ReactiveRemoveByIdOpe
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithDurability inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithCollection withDurability(final DurabilityLevel durabilityLevel) {
|
||||
public RemoveByIdInCollection withDurability(final DurabilityLevel durabilityLevel) {
|
||||
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
|
||||
return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null);
|
||||
return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithCollection withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
public RemoveByIdInCollection withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
Assert.notNull(persistTo, "PersistTo must not be null.");
|
||||
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
|
||||
return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, null);
|
||||
return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithDurability withCas(final Long cas) {
|
||||
Assert.notNull(cas, "CAS must not be null.");
|
||||
return new ReactiveRemoveByIdSupport(template, collection, persistTo, replicateTo, durabilityLevel, cas);
|
||||
public RemoveByIdWithDurability inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdInCollection inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingRemoveById withOptions(final RemoveOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByIdWithDurability withCas(Long cas) {
|
||||
return new ReactiveRemoveByIdSupport(template, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, cas);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,20 +19,45 @@ import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.WithConsistency;
|
||||
import org.springframework.data.couchbase.core.support.WithQuery;
|
||||
import org.springframework.data.couchbase.core.support.WithQueryOptions;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* RemoveBy Query Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveRemoveByQueryOperation {
|
||||
|
||||
/**
|
||||
* Remove via the query service.
|
||||
*/
|
||||
<T> ReactiveRemoveByQuery<T> removeByQuery(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingRemoveByQuery<T> {
|
||||
/**
|
||||
* Remove all matching documents.
|
||||
*
|
||||
* @return RemoveResult for each matching document
|
||||
*/
|
||||
Flux<RemoveResult> all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent methods to specify the query
|
||||
*
|
||||
* @param <T> the entity type.
|
||||
*/
|
||||
interface RemoveByQueryWithQuery<T> extends TerminatingRemoveByQuery<T>, WithQuery<RemoveResult> {
|
||||
|
||||
TerminatingRemoveByQuery<T> matching(Query query);
|
||||
@@ -42,25 +67,67 @@ public interface ReactiveRemoveByQueryOperation {
|
||||
}
|
||||
}
|
||||
|
||||
interface RemoveByQueryInCollection<T> extends RemoveByQueryWithQuery<T>, WithCollection<RemoveResult> {
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface RemoveByQueryWithOptions<T> extends RemoveByQueryWithQuery<T>, WithQueryOptions<RemoveResult> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
RemoveByQueryWithQuery<T> withOptions(QueryOptions options);
|
||||
}
|
||||
|
||||
RemoveByQueryWithQuery<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface RemoveByQueryInCollection<T> extends RemoveByQueryWithOptions<T>, InCollection<Object> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
RemoveByQueryWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface RemoveByQueryInScope<T> extends RemoveByQueryInCollection<T>, InScope<Object> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
RemoveByQueryInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
interface RemoveByQueryConsistentWith<T> extends RemoveByQueryInCollection<T> {
|
||||
interface RemoveByQueryConsistentWith<T> extends RemoveByQueryInScope<T> {
|
||||
|
||||
@Deprecated
|
||||
RemoveByQueryInCollection<T> consistentWith(QueryScanConsistency scanConsistency);
|
||||
RemoveByQueryInScope<T> consistentWith(QueryScanConsistency scanConsistency);
|
||||
|
||||
}
|
||||
|
||||
interface RemoveByQueryWithConsistency<T> extends RemoveByQueryConsistentWith<T>, WithConsistency<RemoveResult> {
|
||||
|
||||
@Override
|
||||
RemoveByQueryConsistentWith<T> withConsistency(QueryScanConsistency scanConsistency);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing query operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type.
|
||||
*/
|
||||
interface ReactiveRemoveByQuery<T> extends RemoveByQueryWithConsistency<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,14 +15,17 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.core;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
import org.springframework.data.couchbase.core.support.TemplateUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
@@ -34,13 +37,16 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReactiveRemoveByQueryOperationSupport.class);
|
||||
|
||||
public ReactiveRemoveByQueryOperationSupport(final ReactiveCouchbaseTemplate template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ReactiveRemoveByQuery<T> removeByQuery(Class<T> domainType) {
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, ALL_QUERY, QueryScanConsistency.NOT_BOUNDED, null);
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, ALL_QUERY,null, null,
|
||||
null, null);
|
||||
}
|
||||
|
||||
static class ReactiveRemoveByQuerySupport<T> implements ReactiveRemoveByQuery<T> {
|
||||
@@ -49,24 +55,32 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu
|
||||
private final Class<T> domainType;
|
||||
private final Query query;
|
||||
private final QueryScanConsistency scanConsistency;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final QueryOptions options;
|
||||
|
||||
ReactiveRemoveByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final Query query,
|
||||
final QueryScanConsistency scanConsistency, String collection) {
|
||||
final QueryScanConsistency scanConsistency, String scope, String collection, QueryOptions options) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.query = query;
|
||||
this.scanConsistency = scanConsistency;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<RemoveResult> all() {
|
||||
return Flux.defer(() -> {
|
||||
String statement = assembleDeleteQuery();
|
||||
Mono<ReactiveQueryResult> allResult = this.collection == null
|
||||
? template.getCouchbaseClientFactory().getCluster().reactive().query(statement, buildQueryOptions())
|
||||
: template.getCouchbaseClientFactory().getScope().reactive().query(statement, buildQueryOptions());
|
||||
PseudoArgs<QueryOptions> pArgs = new PseudoArgs<>(template, scope, collection, options);
|
||||
String statement = assembleDeleteQuery(pArgs.getCollection());
|
||||
LOG.trace("statement: {}", statement);
|
||||
Mono<ReactiveQueryResult> allResult = pArgs.getCollection() == null
|
||||
? template.getCouchbaseClientFactory().getCluster().reactive().query(statement,
|
||||
buildQueryOptions(pArgs.getOptions()))
|
||||
: template.getCouchbaseClientFactory().withScope(pArgs.getScope()).getScope().reactive().query(statement,
|
||||
buildQueryOptions(pArgs.getOptions()));
|
||||
return allResult.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
@@ -79,36 +93,53 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu
|
||||
});
|
||||
}
|
||||
|
||||
private QueryOptions buildQueryOptions() {
|
||||
return query.buildQueryOptions(scanConsistency);
|
||||
private QueryOptions buildQueryOptions(QueryOptions options) {
|
||||
return query.buildQueryOptions(options, scanConsistency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingRemoveByQuery<T> matching(final Query query) {
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByQueryWithConsistency<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public RemoveByQueryInCollection<T> consistentWith(final QueryScanConsistency scanConsistency) {
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
|
||||
public RemoveByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, collection);
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
private String assembleDeleteQuery() {
|
||||
private String assembleDeleteQuery(String collection) {
|
||||
return query.toN1qlRemoveString(template, collection, this.domainType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByQueryWithQuery<T> withOptions(final QueryOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoveByQueryInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveRemoveByQuerySupport<>(template, domainType, query, scanConsistency, scope, collection,
|
||||
options);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,35 +21,102 @@ import reactor.core.publisher.Mono;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllEntityReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithReplaceOptions;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplaceOptions;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
/**
|
||||
* ReplaceOperations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveReplaceByIdOperation {
|
||||
|
||||
/**
|
||||
* Replace using the KV service.
|
||||
*
|
||||
* @param domainType the entity type to replace.
|
||||
*/
|
||||
<T> ReactiveReplaceById<T> replaceById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingReplaceById<T> extends OneAndAllEntityReactive<T> {
|
||||
|
||||
/**
|
||||
* Replace one entity.
|
||||
*
|
||||
* @return Replaced entity.
|
||||
*/
|
||||
Mono<T> one(T object);
|
||||
|
||||
/**
|
||||
* Replace a collection of entities.
|
||||
*
|
||||
* @return Replaced entities
|
||||
*/
|
||||
Flux<? extends T> all(Collection<? extends T> objects);
|
||||
|
||||
}
|
||||
|
||||
interface ReplaceByIdWithCollection<T> extends TerminatingReplaceById<T>, WithCollection<T> {
|
||||
|
||||
TerminatingReplaceById<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ReplaceByIdWithOptions<T> extends TerminatingReplaceById<T>, WithReplaceOptions<RemoveResult> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingReplaceById<T> withOptions(ReplaceOptions options);
|
||||
}
|
||||
|
||||
interface ReplaceByIdWithDurability<T> extends ReplaceByIdWithCollection<T>, WithDurability<T> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ReplaceByIdInCollection<T> extends ReplaceByIdWithOptions<T>, InCollection<Object> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
ReplaceByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
ReplaceByIdWithCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface ReplaceByIdInScope<T> extends ReplaceByIdInCollection<T>, InScope<Object> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
ReplaceByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
ReplaceByIdWithCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
interface ReplaceByIdWithDurability<T> extends ReplaceByIdInScope<T>, WithDurability<T> {
|
||||
|
||||
ReplaceByIdInCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
|
||||
ReplaceByIdInCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
|
||||
}
|
||||
|
||||
@@ -58,6 +125,11 @@ public interface ReactiveReplaceByIdOperation {
|
||||
ReplaceByIdWithDurability<T> withExpiry(final Duration expiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing KV replace operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to replace
|
||||
*/
|
||||
interface ReactiveReplaceById<T> extends ReplaceByIdWithExpiry<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@ import reactor.core.publisher.Mono;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.support.PseudoArgs;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
@@ -31,6 +34,7 @@ import com.couchbase.client.java.kv.ReplicateTo;
|
||||
|
||||
public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdOperation {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReactiveReplaceByIdOperationSupport.class);
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
|
||||
public ReactiveReplaceByIdOperationSupport(final ReactiveCouchbaseTemplate template) {
|
||||
@@ -40,7 +44,7 @@ public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdO
|
||||
@Override
|
||||
public <T> ReactiveReplaceById<T> replaceById(final Class<T> domainType) {
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, null, null, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
DurabilityLevel.NONE, null, template.support());
|
||||
}
|
||||
|
||||
@@ -48,19 +52,23 @@ public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdO
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final ReplaceOptions options;
|
||||
private final PersistTo persistTo;
|
||||
private final ReplicateTo replicateTo;
|
||||
private final DurabilityLevel durabilityLevel;
|
||||
private final Duration expiry;
|
||||
private final ReactiveTemplateSupport support;
|
||||
|
||||
ReactiveReplaceByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType,
|
||||
final String collection, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
ReactiveReplaceByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final String scope,
|
||||
final String collection, final ReplaceOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
final DurabilityLevel durabilityLevel, final Duration expiry, ReactiveTemplateSupport support) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.persistTo = persistTo;
|
||||
this.replicateTo = replicateTo;
|
||||
this.durabilityLevel = durabilityLevel;
|
||||
@@ -70,17 +78,21 @@ public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdO
|
||||
|
||||
@Override
|
||||
public Mono<T> one(T object) {
|
||||
return (Mono<T>) Mono.just(object).flatMap(support::encodeEntity).flatMap(converted -> {
|
||||
return template.getCollection(collection).reactive()
|
||||
.replace(converted.getId(), converted.export(), buildReplaceOptions(object, converted))
|
||||
.flatMap(result -> support.applyUpdatedCas(object, result.cas()));
|
||||
}).onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
} else {
|
||||
return throwable;
|
||||
}
|
||||
});
|
||||
PseudoArgs<ReplaceOptions> pArgs = new PseudoArgs<>(template, scope, collection,
|
||||
options != null ? options : ReplaceOptions.replaceOptions());
|
||||
LOG.trace("statement: {} pArgs: {}", "replaceById", pArgs);
|
||||
return Mono.just(object).flatMap(support::encodeEntity).flatMap(converted -> template.getCouchbaseClientFactory()
|
||||
.withScope(pArgs.getScope()).getCollection(pArgs.getCollection()).reactive()
|
||||
.replace(converted.getId(), converted.export(), buildReplaceOptions(pArgs.getOptions(), object, converted))
|
||||
.flatMap(result -> support.applyUpdatedId(object, converted.getId())
|
||||
.flatMap(replacedObject -> (Mono<T>) support.applyUpdatedCas(replacedObject, result.cas()))))
|
||||
.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
} else {
|
||||
return throwable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,8 +100,8 @@ public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdO
|
||||
return Flux.fromIterable(objects).flatMap(this::one);
|
||||
}
|
||||
|
||||
private ReplaceOptions buildReplaceOptions(T object, CouchbaseDocument doc) {
|
||||
final ReplaceOptions options = ReplaceOptions.replaceOptions();
|
||||
private ReplaceOptions buildReplaceOptions(ReplaceOptions options, T object, CouchbaseDocument doc) {
|
||||
options = options != null ? options : ReplaceOptions.replaceOptions();
|
||||
if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) {
|
||||
options.durability(persistTo, replicateTo);
|
||||
} else if (durabilityLevel != DurabilityLevel.NONE) {
|
||||
@@ -106,32 +118,46 @@ public class ReactiveReplaceByIdOperationSupport implements ReactiveReplaceByIdO
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingReplaceById<T> inCollection(final String collection) {
|
||||
public TerminatingReplaceById<T> withOptions(final ReplaceOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdWithDurability<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdWithCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
public ReplaceByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdInCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdWithCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
public ReplaceByIdInCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
Assert.notNull(persistTo, "PersistTo must not be null.");
|
||||
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReplaceByIdWithDurability<T> withExpiry(final Duration expiry) {
|
||||
Assert.notNull(expiry, "expiry must not be null.");
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveReplaceByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,43 +21,118 @@ import reactor.core.publisher.Mono;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.data.couchbase.core.support.InCollection;
|
||||
import org.springframework.data.couchbase.core.support.InScope;
|
||||
import org.springframework.data.couchbase.core.support.OneAndAllEntityReactive;
|
||||
import org.springframework.data.couchbase.core.support.WithCollection;
|
||||
import org.springframework.data.couchbase.core.support.WithUpsertOptions;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
import com.couchbase.client.java.kv.PersistTo;
|
||||
import com.couchbase.client.java.kv.ReplicateTo;
|
||||
import com.couchbase.client.java.kv.UpsertOptions;
|
||||
|
||||
/**
|
||||
* Upsert Operations
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ReactiveUpsertByIdOperation {
|
||||
|
||||
|
||||
/**
|
||||
* Upsert using the KV service.
|
||||
*
|
||||
* @param domainType the entity type to upsert.
|
||||
*/
|
||||
<T> ReactiveUpsertById<T> upsertById(Class<T> domainType);
|
||||
|
||||
/**
|
||||
* Terminating operations invoking the actual execution.
|
||||
*/
|
||||
interface TerminatingUpsertById<T> extends OneAndAllEntityReactive<T> {
|
||||
|
||||
/**
|
||||
* Upsert one entity.
|
||||
*
|
||||
* @return Upserted entity.
|
||||
*/
|
||||
@Override
|
||||
Mono<T> one(T object);
|
||||
|
||||
/**
|
||||
* Insert a collection of entities.
|
||||
*
|
||||
* @return Inserted entities
|
||||
*/
|
||||
@Override
|
||||
Flux<? extends T> all(Collection<? extends T> objects);
|
||||
|
||||
}
|
||||
|
||||
interface UpsertByIdWithCollection<T> extends TerminatingUpsertById<T>, WithCollection<T> {
|
||||
|
||||
TerminatingUpsertById<T> inCollection(String collection);
|
||||
/**
|
||||
* Fluent method to specify options.
|
||||
*
|
||||
* @param <T> the entity type to use.
|
||||
*/
|
||||
interface UpsertByIdWithOptions<T> extends TerminatingUpsertById<T>, WithUpsertOptions<T> {
|
||||
/**
|
||||
* Fluent method to specify options to use for execution
|
||||
*
|
||||
* @param options to use for execution
|
||||
*/
|
||||
@Override
|
||||
TerminatingUpsertById<T> withOptions(UpsertOptions options);
|
||||
}
|
||||
|
||||
interface UpsertByIdWithDurability<T> extends UpsertByIdWithCollection<T>, WithDurability<T> {
|
||||
/**
|
||||
* Fluent method to specify the collection.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface UpsertByIdInCollection<T> extends UpsertByIdWithOptions<T>, InCollection<Object> {
|
||||
/**
|
||||
* With a different collection
|
||||
*
|
||||
* @param collection the collection to use.
|
||||
*/
|
||||
@Override
|
||||
UpsertByIdWithOptions<T> inCollection(String collection);
|
||||
}
|
||||
|
||||
UpsertByIdWithCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
/**
|
||||
* Fluent method to specify the scope.
|
||||
*
|
||||
* @param <T> the entity type to use for the results.
|
||||
*/
|
||||
interface UpsertByIdInScope<T> extends UpsertByIdInCollection<T>, InScope<Object> {
|
||||
/**
|
||||
* With a different scope
|
||||
*
|
||||
* @param scope the scope to use.
|
||||
*/
|
||||
@Override
|
||||
UpsertByIdInCollection<T> inScope(String scope);
|
||||
}
|
||||
|
||||
UpsertByIdWithCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
interface UpsertByIdWithDurability<T> extends UpsertByIdInScope<T>, WithDurability<T> {
|
||||
@Override
|
||||
UpsertByIdInCollection<T> withDurability(DurabilityLevel durabilityLevel);
|
||||
@Override
|
||||
UpsertByIdInCollection<T> withDurability(PersistTo persistTo, ReplicateTo replicateTo);
|
||||
|
||||
}
|
||||
|
||||
interface UpsertByIdWithExpiry<T> extends UpsertByIdWithDurability<T>, WithExpiry<T> {
|
||||
|
||||
@Override
|
||||
UpsertByIdWithDurability<T> withExpiry(Duration expiry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for constructing KV operations in a fluent way.
|
||||
*
|
||||
* @param <T> the entity type to upsert
|
||||
*/
|
||||
interface ReactiveUpsertById<T> extends UpsertByIdWithExpiry<T> {}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.support.PseudoArgs;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.msg.kv.DurabilityLevel;
|
||||
@@ -40,7 +41,7 @@ public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOpe
|
||||
@Override
|
||||
public <T> ReactiveUpsertById<T> upsertById(final Class<T> domainType) {
|
||||
Assert.notNull(domainType, "DomainType must not be null!");
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, null, null, null, PersistTo.NONE, ReplicateTo.NONE,
|
||||
DurabilityLevel.NONE, null, template.support());
|
||||
}
|
||||
|
||||
@@ -48,19 +49,23 @@ public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOpe
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final Class<T> domainType;
|
||||
private final String scope;
|
||||
private final String collection;
|
||||
private final UpsertOptions options;
|
||||
private final PersistTo persistTo;
|
||||
private final ReplicateTo replicateTo;
|
||||
private final DurabilityLevel durabilityLevel;
|
||||
private final Duration expiry;
|
||||
private final ReactiveTemplateSupport support;
|
||||
|
||||
ReactiveUpsertByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType,
|
||||
final String collection, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
ReactiveUpsertByIdSupport(final ReactiveCouchbaseTemplate template, final Class<T> domainType, final String scope,
|
||||
final String collection, final UpsertOptions options, final PersistTo persistTo, final ReplicateTo replicateTo,
|
||||
final DurabilityLevel durabilityLevel, final Duration expiry, ReactiveTemplateSupport support) {
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
this.scope = scope;
|
||||
this.collection = collection;
|
||||
this.options = options;
|
||||
this.persistTo = persistTo;
|
||||
this.replicateTo = replicateTo;
|
||||
this.durabilityLevel = durabilityLevel;
|
||||
@@ -70,18 +75,21 @@ public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOpe
|
||||
|
||||
@Override
|
||||
public Mono<T> one(T object) {
|
||||
return (Mono<T>) Mono.just(object).flatMap(support::encodeEntity).flatMap(converted ->
|
||||
template.getCollection(collection).reactive()
|
||||
.upsert(converted.getId(), converted.export(), buildUpsertOptions(converted)).flatMap(result ->
|
||||
support.applyUpdatedId(object, converted.getId())
|
||||
.flatMap(updatedObject -> support.applyUpdatedCas(updatedObject, result.cas())))
|
||||
).onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
} else {
|
||||
return throwable;
|
||||
}
|
||||
});
|
||||
PseudoArgs<UpsertOptions> pArgs = new PseudoArgs<>(template, scope, collection,
|
||||
options != null ? options : UpsertOptions.upsertOptions());
|
||||
return Mono.just(object).flatMap(support::encodeEntity)
|
||||
.flatMap(converted -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
|
||||
.getCollection(pArgs.getCollection()).reactive()
|
||||
.upsert(converted.getId(), converted.export(), buildUpsertOptions(pArgs.getOptions(), converted))
|
||||
.flatMap(result -> support.applyUpdatedId(object, converted.getId())
|
||||
.flatMap(updatedObject -> (Mono<T>) support.applyUpdatedCas(updatedObject, result.cas()))))
|
||||
.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
} else {
|
||||
return throwable;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,8 +97,8 @@ public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOpe
|
||||
return Flux.fromIterable(objects).flatMap(this::one);
|
||||
}
|
||||
|
||||
private UpsertOptions buildUpsertOptions(CouchbaseDocument doc) {
|
||||
final UpsertOptions options = UpsertOptions.upsertOptions();
|
||||
private UpsertOptions buildUpsertOptions(UpsertOptions options, CouchbaseDocument doc) {
|
||||
options = options != null ? options : UpsertOptions.upsertOptions();
|
||||
if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) {
|
||||
options.durability(persistTo, replicateTo);
|
||||
} else if (durabilityLevel != DurabilityLevel.NONE) {
|
||||
@@ -105,32 +113,46 @@ public class ReactiveUpsertByIdOperationSupport implements ReactiveUpsertByIdOpe
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminatingUpsertById<T> inCollection(final String collection) {
|
||||
public TerminatingUpsertById<T> withOptions(final UpsertOptions options) {
|
||||
Assert.notNull(options, "Options must not be null.");
|
||||
return new ReactiveUpsertByIdSupport(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdWithDurability<T> inCollection(final String collection) {
|
||||
Assert.hasText(collection, "Collection must not be null nor empty.");
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdWithCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
public UpsertByIdInCollection<T> inScope(final String scope) {
|
||||
Assert.hasText(scope, "Scope must not be null nor empty.");
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdInCollection<T> withDurability(final DurabilityLevel durabilityLevel) {
|
||||
Assert.notNull(durabilityLevel, "Durability Level must not be null.");
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdWithCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
public UpsertByIdInCollection<T> withDurability(final PersistTo persistTo, final ReplicateTo replicateTo) {
|
||||
Assert.notNull(persistTo, "PersistTo must not be null.");
|
||||
Assert.notNull(replicateTo, "ReplicateTo must not be null.");
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpsertByIdWithDurability<T> withExpiry(final Duration expiry) {
|
||||
Assert.notNull(expiry, "expiry must not be null.");
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, collection, persistTo, replicateTo, durabilityLevel,
|
||||
expiry, support);
|
||||
return new ReactiveUpsertByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
|
||||
durabilityLevel, expiry, support);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.data.couchbase.core.mapping;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.MappingException;
|
||||
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
@@ -23,12 +25,11 @@ import org.springframework.data.mapping.model.FieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.couchbase.client.core.deps.com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Implements annotated property representations of a given {@link Field} instance.
|
||||
* <p/>
|
||||
@@ -98,13 +99,20 @@ public class BasicCouchbasePersistentProperty extends AnnotationBasedPersistentP
|
||||
// DATACOUCH-145: allows SDK's @Id annotation to be used
|
||||
@Override
|
||||
public boolean isIdProperty() {
|
||||
if (super.isIdProperty()){
|
||||
if (super.isIdProperty()) {
|
||||
return true;
|
||||
}
|
||||
// is field named "id"
|
||||
if(getField() != null && this.getFieldName().toLowerCase(Locale.ROOT).equals("id")){
|
||||
if (getField() != null && this.getFieldName().toLowerCase(Locale.ROOT).equals("id")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isExpirationProperty() {
|
||||
return isExpiration.get();
|
||||
}
|
||||
|
||||
private final Lazy<Boolean> isExpiration = Lazy.of(() -> this.isAnnotationPresent(Expiration.class));
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,4 +31,6 @@ public interface CouchbasePersistentProperty extends PersistentProperty<Couchbas
|
||||
* The field name can be different from the actual property name by using a custom annotation.
|
||||
*/
|
||||
String getFieldName();
|
||||
|
||||
boolean isExpirationProperty();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.mapping;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Annotation to define a field to be substituted for META().expiration in a query
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
|
||||
public @interface Expiration {
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,8 @@ import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
@@ -52,6 +54,7 @@ public class Query {
|
||||
private QueryScanConsistency queryScanConsistency;
|
||||
|
||||
static private final Pattern WHERE_PATTERN = Pattern.compile("\\sWHERE\\s");
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Query.class);
|
||||
|
||||
public Query() {}
|
||||
|
||||
@@ -265,15 +268,15 @@ public class Query {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
|
||||
return toN1qlSelectString(template, null, domainClass, null, isCount, null);
|
||||
}
|
||||
|
||||
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass,
|
||||
boolean isCount) {
|
||||
return toN1qlSelectString(template, collectionName, domainClass, null, isCount, null);
|
||||
}
|
||||
|
||||
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
|
||||
return toN1qlSelectString(template, null, domainClass, null, isCount, null);
|
||||
}
|
||||
|
||||
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass,
|
||||
Class returnClass, boolean isCount, String[] distinctFields) {
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, collectionName, domainClass,
|
||||
@@ -322,8 +325,7 @@ public class Query {
|
||||
* @param scanConsistency
|
||||
* @return QueryOptions
|
||||
*/
|
||||
public QueryOptions buildQueryOptions(QueryScanConsistency scanConsistency) {
|
||||
QueryOptions options = QueryOptions.queryOptions();
|
||||
public QueryOptions buildQueryOptions(QueryOptions options, QueryScanConsistency scanConsistency) {
|
||||
if (options == null) { // add/override what we got from PseudoArgs
|
||||
options = QueryOptions.queryOptions();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take Collection
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface InCollection<T> {
|
||||
/**
|
||||
* Specify collection
|
||||
*
|
||||
* @param collection - collection name
|
||||
*/
|
||||
Object inCollection(String collection);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,11 +16,16 @@
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take Collection
|
||||
* A common interface for all of Insert, Replace, Upsert that take options
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithCollection<T> {
|
||||
Object inCollection(String collectionName);
|
||||
public interface InScope<T> {
|
||||
/**
|
||||
* Specify scope
|
||||
*
|
||||
* @param scope - scope name
|
||||
*/
|
||||
Object inScope(String scope);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.core.io.CollectionIdentifier;
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
|
||||
public class PseudoArgs<OPTS> {
|
||||
private final OPTS options;
|
||||
private final String scopeName;
|
||||
private final String collectionName;
|
||||
|
||||
public PseudoArgs(String scopeName, String collectionName, OPTS options) {
|
||||
this.options = options;
|
||||
this.scopeName = scopeName;
|
||||
this.collectionName = collectionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* return scope, collection and options in following precedence <br>
|
||||
* 1) values from fluent api<br>
|
||||
* 2) values from dynamic proxy (via template threadLocal)<br>
|
||||
* 3) the values from the couchbaseClientFactory<br>
|
||||
*
|
||||
* @param template to hold
|
||||
* @param scope
|
||||
* @param collection
|
||||
* @param options
|
||||
*/
|
||||
public PseudoArgs(ReactiveCouchbaseTemplate template, String scope, String collection, OPTS options) {
|
||||
|
||||
// 1) values from the args (fluent api)
|
||||
|
||||
String scopeForQuery = scope;
|
||||
String collectionForQuery = collection;
|
||||
OPTS optionsForQuery = options;
|
||||
|
||||
// 2) from DynamicProxy via template threadLocal
|
||||
|
||||
scopeForQuery = scopeForQuery != null ? scopeForQuery : getThreadLocalScopeName(template);
|
||||
collectionForQuery = collectionForQuery != null ? collectionForQuery : getThreadLocalCollectionName(template);
|
||||
optionsForQuery = optionsForQuery != null ? optionsForQuery : getThreadLocalOptions(template);
|
||||
|
||||
// if a collection was specified but no scope, use the scope from the clientFactory
|
||||
|
||||
if (collectionForQuery != null && scopeForQuery == null) {
|
||||
scopeForQuery = template.getCouchbaseClientFactory().getScope().name();
|
||||
}
|
||||
|
||||
// specifying scope and collection = _default is not necessary and will fail if server doesn't have collections
|
||||
|
||||
if ((scopeForQuery == null || CollectionIdentifier.DEFAULT_SCOPE.equals(scopeForQuery))
|
||||
&& (collectionForQuery == null || CollectionIdentifier.DEFAULT_COLLECTION.equals(collectionForQuery))) {
|
||||
scopeForQuery = null;
|
||||
collectionForQuery = null;
|
||||
}
|
||||
|
||||
this.scopeName = scopeForQuery;
|
||||
this.collectionName = collectionForQuery;
|
||||
this.options = optionsForQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @@return the options
|
||||
*/
|
||||
public OPTS getOptions() {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @@return the scope name
|
||||
*/
|
||||
public String getScope() {
|
||||
return this.scopeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @@return the collection name
|
||||
*/
|
||||
public String getCollection() {
|
||||
return this.collectionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @@return the options from the ThreadLocal field of the template
|
||||
*/
|
||||
private OPTS getThreadLocalOptions(ReactiveCouchbaseTemplate template) {
|
||||
return template.getPseudoArgs() == null ? null : (OPTS) (template.getPseudoArgs().getOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* @@return the scope name from the ThreadLocal field of the template
|
||||
*/
|
||||
private String getThreadLocalScopeName(ReactiveCouchbaseTemplate template) {
|
||||
return template.getPseudoArgs() == null ? null : template.getPseudoArgs().getScope();
|
||||
}
|
||||
|
||||
/**
|
||||
* @@return the collection name from the ThreadLocal field of the template
|
||||
*/
|
||||
private String getThreadLocalCollectionName(ReactiveCouchbaseTemplate template) {
|
||||
return template.getPseudoArgs() == null ? null : template.getPseudoArgs().getCollection();
|
||||
}
|
||||
}
|
||||
@@ -18,11 +18,16 @@ package org.springframework.data.couchbase.core.support;
|
||||
import com.couchbase.client.java.analytics.AnalyticsScanConsistency;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take consistency
|
||||
* Interface for operations that take AnalyticsScanConsistency
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithAnalyticsConsistency<T> {
|
||||
/**
|
||||
* Specify scan consistency
|
||||
*
|
||||
* @param scanConsistency - scan consistency
|
||||
*/
|
||||
Object withConsistency(AnalyticsScanConsistency scanConsistency);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.analytics.AnalyticsOptions;
|
||||
|
||||
/**
|
||||
* Interface for operations that take AnalyticsOptions
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithAnalyticsOptions<T> {
|
||||
/**
|
||||
* Specify analytics options
|
||||
*
|
||||
* @param options - analytics options
|
||||
*/
|
||||
Object withOptions(AnalyticsOptions options);
|
||||
}
|
||||
@@ -18,11 +18,16 @@ package org.springframework.data.couchbase.core.support;
|
||||
import org.springframework.data.couchbase.core.query.AnalyticsQuery;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take Query
|
||||
* Interface for operations that take AnalyticsQuery
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithAnalyticsQuery<T> {
|
||||
/**
|
||||
* Specify query
|
||||
*
|
||||
* @param query - query
|
||||
*/
|
||||
Object matching(AnalyticsQuery query);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,16 @@ package org.springframework.data.couchbase.core.support;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take consistency
|
||||
* A common interface operations that take scan consistency
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithConsistency<T> {
|
||||
/**
|
||||
* Specify scan consistency
|
||||
*
|
||||
* @param scanConsistency - scan consistency
|
||||
*/
|
||||
Object withConsistency(QueryScanConsistency scanConsistency);
|
||||
}
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take Distinct
|
||||
* Interface for operations that take distinct fields
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithDistinct<T> {
|
||||
/**
|
||||
* Specify distinct field names
|
||||
*
|
||||
* @param distinctFields - distinct fields
|
||||
*/
|
||||
Object distinct(String[] distinctFields);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.kv.ExistsOptions;
|
||||
|
||||
/**
|
||||
* Interface for operations that take ExistsOptions
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithExistsOptions<T> {
|
||||
/**
|
||||
* Specify options
|
||||
*
|
||||
* @param options - exists options
|
||||
*/
|
||||
Object withOptions(ExistsOptions options);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
|
||||
|
||||
/**
|
||||
* A interface for operations that take GetAnyReplicaOptions
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithGetAnyReplicaOptions<T> {
|
||||
/**
|
||||
* Specify options
|
||||
*
|
||||
* @param options - GetAnyReplicaOptions
|
||||
*/
|
||||
Object withOptions(GetAnyReplicaOptions options);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.kv.GetOptions;
|
||||
|
||||
/**
|
||||
* Interface for operations that take GetOptions
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithGetOptions<T> {
|
||||
/**
|
||||
* Specify options
|
||||
*
|
||||
* @param options - get options
|
||||
*/
|
||||
Object withOptions(GetOptions options);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.kv.InsertOptions;
|
||||
|
||||
/**
|
||||
* Interface for operations take insert options
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithInsertOptions<T> {
|
||||
/**
|
||||
* Specify options
|
||||
*
|
||||
* @param options - insert options
|
||||
*/
|
||||
Object withOptions(InsertOptions options);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take options
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithQueryOptions<T> {
|
||||
/**
|
||||
* Specify options
|
||||
*
|
||||
* @param options - query options
|
||||
*/
|
||||
Object withOptions(QueryOptions options);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.kv.RemoveOptions;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take options
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithRemoveOptions<T> {
|
||||
Object withOptions(RemoveOptions options);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.kv.ReplaceOptions;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take options
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithReplaceOptions<T> {
|
||||
Object withOptions(ReplaceOptions options);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.support;
|
||||
|
||||
import com.couchbase.client.java.kv.UpsertOptions;
|
||||
|
||||
/**
|
||||
* A common interface for all of Insert, Replace, Upsert that take options
|
||||
*
|
||||
* @author Michael Reiche
|
||||
* @param <T> - the entity class
|
||||
*/
|
||||
public interface WithUpsertOptions<T> {
|
||||
Object withOptions(UpsertOptions options);
|
||||
}
|
||||
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.repository.query;
|
||||
|
||||
import com.couchbase.client.core.io.CollectionIdentifier;
|
||||
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.ExecutableFindByQueryOperation.ExecutableFindByQuery;
|
||||
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.TerminatingFindByQuery;
|
||||
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;
|
||||
@@ -41,7 +42,7 @@ import org.springframework.util.Assert;
|
||||
public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<CouchbaseOperations>
|
||||
implements RepositoryQuery {
|
||||
|
||||
private final ExecutableFindByQueryOperation.ExecutableFindByQuery<?> findOperationWithProjection;
|
||||
private final ExecutableFindByQuery<?> findOperationWithProjection;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractCouchbaseQuery} from the given {@link ReactiveCouchbaseQueryMethod} and
|
||||
@@ -86,7 +87,7 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
|
||||
ExecutableFindByQuery<?> find = typeToRead == null ? findOperationWithProjection //
|
||||
: findOperationWithProjection; // not yet implemented in core .as(typeToRead);
|
||||
|
||||
String collection = "_default._default";// method.getEntityInformation().getCollectionName(); // not yet implemented
|
||||
String collection = null;
|
||||
|
||||
CouchbaseQueryExecution execution = getExecution(accessor,
|
||||
new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find);
|
||||
@@ -101,7 +102,7 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
|
||||
* @return
|
||||
*/
|
||||
private CouchbaseQueryExecution getExecution(ParameterAccessor accessor, Converter<Object, Object> resultProcessing,
|
||||
ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation) {
|
||||
ExecutableFindByQuery<?> operation) {
|
||||
return new CouchbaseQueryExecution.ResultProcessingExecution(getExecutionToWrap(accessor, operation),
|
||||
resultProcessing);
|
||||
}
|
||||
@@ -114,7 +115,7 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
|
||||
* @return
|
||||
*/
|
||||
private CouchbaseQueryExecution getExecutionToWrap(ParameterAccessor accessor,
|
||||
ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation) {
|
||||
ExecutableFindByQuery<?> operation) {
|
||||
|
||||
if (isDeleteQuery()) {
|
||||
return new DeleteExecution(getOperations(), getQueryMethod());
|
||||
@@ -130,7 +131,7 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
|
||||
return new PagedExecution(operation, accessor.getPageable());
|
||||
} else {
|
||||
return (q, t, c) -> {
|
||||
ExecutableFindByQueryOperation.TerminatingFindByQuery<?> find = operation.matching(q);
|
||||
TerminatingFindByQuery<?> find = operation.matching(q);
|
||||
if (isCountQuery()) {
|
||||
return find.count();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ 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.ExecutableFindByQueryOperation.ExecutableFindByQuery;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.mapping.model.EntityInstantiators;
|
||||
import org.springframework.data.repository.core.EntityMetadata;
|
||||
@@ -47,7 +47,7 @@ public abstract class AbstractCouchbaseQueryBase<CouchbaseOperationsType> implem
|
||||
private final CouchbaseQueryMethod method;
|
||||
private final CouchbaseOperationsType operations;
|
||||
private final EntityInstantiators instantiators;
|
||||
private final ExecutableFindByQueryOperation.ExecutableFindByQuery<?> findOperationWithProjection;
|
||||
private final ExecutableFindByQuery<?> findOperationWithProjection;
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.repository.query;
|
||||
|
||||
import com.couchbase.client.core.io.CollectionIdentifier;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations;
|
||||
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperation;
|
||||
@@ -85,7 +86,7 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu
|
||||
? findOperationWithProjection //
|
||||
: findOperationWithProjection; // note yet implemented in core .as(typeToRead);
|
||||
|
||||
String collection = "_default._default";// method.getEntityInformation().getCollectionName(); // not yet implemented
|
||||
String collection = null;
|
||||
|
||||
ReactiveCouchbaseQueryExecution execution = getExecution(accessor,
|
||||
new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find);
|
||||
|
||||
@@ -19,7 +19,8 @@ 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.ExecutableFindByQueryOperation.TerminatingFindByQuery;
|
||||
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.ExecutableFindByQuery;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
@@ -92,10 +93,10 @@ interface CouchbaseQueryExecution {
|
||||
*/
|
||||
final class SlicedExecution implements CouchbaseQueryExecution {
|
||||
|
||||
private final ExecutableFindByQueryOperation.ExecutableFindByQuery<?> find;
|
||||
private final ExecutableFindByQuery<?> find;
|
||||
private final Pageable pageable;
|
||||
|
||||
public SlicedExecution(ExecutableFindByQueryOperation.ExecutableFindByQuery find, Pageable pageable) {
|
||||
public SlicedExecution(ExecutableFindByQuery find, Pageable pageable) {
|
||||
Assert.notNull(find, "Find must not be null!");
|
||||
Assert.notNull(pageable, "Pageable must not be null!");
|
||||
this.find = find;
|
||||
@@ -123,10 +124,10 @@ interface CouchbaseQueryExecution {
|
||||
*/
|
||||
final class PagedExecution<FindWithQuery> implements CouchbaseQueryExecution {
|
||||
|
||||
private final ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation;
|
||||
private final ExecutableFindByQuery<?> operation;
|
||||
private final Pageable pageable;
|
||||
|
||||
public PagedExecution(ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation, Pageable pageable) {
|
||||
public PagedExecution(ExecutableFindByQuery<?> operation, Pageable pageable) {
|
||||
Assert.notNull(operation, "Operation must not be null!");
|
||||
Assert.notNull(pageable, "Pageable must not be null!");
|
||||
this.operation = operation;
|
||||
@@ -140,7 +141,7 @@ interface CouchbaseQueryExecution {
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
int overallLimit = 0; // query.getLimit();
|
||||
ExecutableFindByQueryOperation.TerminatingFindByQuery<?> matching = operation.matching(query);
|
||||
TerminatingFindByQuery<?> matching = operation.matching(query);
|
||||
// Apply raw pagination
|
||||
query.with(pageable);
|
||||
// Adjust limit if page would exceed the overall limit
|
||||
|
||||
@@ -71,9 +71,23 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
|
||||
return from(part, property, where(addMetaIfRequired(path, property)), iterator);
|
||||
}
|
||||
|
||||
static Converter<? super CouchbasePersistentProperty, String> cvtr = (
|
||||
source) -> new StringBuilder(source.getFieldName().length() + 2).append('`').append(source.getFieldName())
|
||||
.append('`').toString();
|
||||
static Converter<? super CouchbasePersistentProperty, String> cvtr = new MyConverter();
|
||||
|
||||
static class MyConverter implements Converter<CouchbasePersistentProperty, String> {
|
||||
@Override
|
||||
public String convert(CouchbasePersistentProperty source) {
|
||||
if (source.isIdProperty()) {
|
||||
return "META().id";
|
||||
} else if (source.isVersionProperty()) {
|
||||
return "META().cas";
|
||||
} else if (source.isExpirationProperty()) {
|
||||
return "META().expiration";
|
||||
} else {
|
||||
return new StringBuilder(source.getFieldName().length() + 2).append('`').append(source.getFieldName())
|
||||
.append('`').toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueryCriteria and(final Part part, final QueryCriteria base, final Iterator<Object> iterator) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,7 @@
|
||||
package org.springframework.data.couchbase.repository.query;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation;
|
||||
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.ExecutableFindByQuery;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
@@ -63,17 +63,16 @@ public class N1qlRepositoryQueryExecutor {
|
||||
|
||||
// counterpart to ReactiveN1qlRespositoryQueryExecutor,
|
||||
Query query;
|
||||
ExecutableFindByQueryOperation.ExecutableFindByQuery q;
|
||||
ExecutableFindByQuery q;
|
||||
if (queryMethod.hasN1qlAnnotation()) {
|
||||
query = new StringN1qlQueryCreator(accessor, queryMethod, operations.getConverter(), operations.getBucketName(),
|
||||
SPEL_PARSER, evaluationContextProvider, namedQueries).createQuery();
|
||||
} else {
|
||||
final PartTree tree = new PartTree(queryMethod.getName(), domainClass);
|
||||
query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter(), operations.getBucketName())
|
||||
.createQuery();
|
||||
query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter(), operations.getBucketName()).createQuery();
|
||||
}
|
||||
|
||||
ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation = (ExecutableFindByQueryOperation.ExecutableFindByQuery<?>) operations
|
||||
ExecutableFindByQuery<?> operation = (ExecutableFindByQuery<?>) operations
|
||||
.findByQuery(domainClass).withConsistency(buildQueryScanConsistency());
|
||||
if (queryMethod.isCountQuery()) {
|
||||
return operation.matching(query).count();
|
||||
|
||||
@@ -188,8 +188,7 @@ public class SimpleReactiveCouchbaseRepository<T, ID> implements ReactiveCouchba
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteAll() {
|
||||
return operations.removeByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()).all()
|
||||
.then();
|
||||
return operations.removeByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()).all().then();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,6 +31,7 @@ import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
@@ -68,6 +69,7 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
|
||||
couchbaseTemplate.removeByQuery(User.class).all();
|
||||
couchbaseTemplate.removeByQuery(UserAnnotated.class).all();
|
||||
couchbaseTemplate.removeByQuery(UserAnnotated2.class).all();
|
||||
couchbaseTemplate.removeByQuery(UserAnnotated3.class).all();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -93,12 +95,12 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
|
||||
@Test
|
||||
void withDurability()
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
Class clazz = User.class; // for now, just User.class. There is no Durability annotation.
|
||||
Class<?> clazz = User.class; // for now, just User.class. There is no Durability annotation.
|
||||
// insert, replace, upsert
|
||||
for (OneAndAllEntity<User> operator : new OneAndAllEntity[] { couchbaseTemplate.insertById(clazz),
|
||||
couchbaseTemplate.replaceById(clazz), couchbaseTemplate.upsertById(clazz) }) {
|
||||
// create an entity of type clazz
|
||||
Constructor cons = clazz.getConstructor(String.class, String.class, String.class);
|
||||
Constructor<?> cons = clazz.getConstructor(String.class, String.class, String.class);
|
||||
User user = (User) cons.newInstance("" + operator.getClass().getSimpleName() + "_" + clazz.getSimpleName(),
|
||||
"firstname", "lastname");
|
||||
|
||||
@@ -112,7 +114,22 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
|
||||
couchbaseTemplate.insertById(User.class).one(user);
|
||||
}
|
||||
// call to insert/replace/update
|
||||
User returned = (User) operator.one(user);
|
||||
User returned = null;
|
||||
|
||||
// occasionally gives "reactor.core.Exceptions$OverflowException: Could not emit value due to lack of requests"
|
||||
for (int i = 1; i != 5; i++) {
|
||||
try {
|
||||
returned = (User) operator.one(user);
|
||||
break;
|
||||
} catch (Exception ofe) {
|
||||
System.out.println(""+i+" caught: "+ofe);
|
||||
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
if (i == 4) {
|
||||
throw ofe;
|
||||
}
|
||||
sleepSecs(1);
|
||||
}
|
||||
}
|
||||
assertEquals(user, returned);
|
||||
User found = couchbaseTemplate.findById(User.class).one(user.getId());
|
||||
assertEquals(user, found);
|
||||
@@ -210,8 +227,6 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
|
||||
{
|
||||
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
|
||||
User modified = couchbaseTemplate.upsertById(User.class).one(user);
|
||||
System.out.println(reactiveCouchbaseTemplate.support().getCas(user));
|
||||
System.out.println(reactiveCouchbaseTemplate.support().getCas(modified));
|
||||
assertEquals(user, modified);
|
||||
|
||||
// careful now - user and modified are the same object. The object has the new cas (@Version version)
|
||||
@@ -236,8 +251,23 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
|
||||
@Test
|
||||
void insertByIdwithDurability() {
|
||||
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
|
||||
User inserted = couchbaseTemplate.insertById(User.class).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE)
|
||||
.one(user);
|
||||
User inserted = null;
|
||||
|
||||
// occasionally gives "reactor.core.Exceptions$OverflowException: Could not emit value due to lack of requests"
|
||||
for (int i = 1; i != 5; i++) {
|
||||
try {
|
||||
inserted = couchbaseTemplate.insertById(User.class).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE)
|
||||
.one(user);
|
||||
break;
|
||||
} catch (Exception ofe) {
|
||||
System.out.println(""+i+" caught: "+ofe);
|
||||
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
if (i == 4) {
|
||||
throw ofe;
|
||||
}
|
||||
sleepSecs(1);
|
||||
}
|
||||
}
|
||||
assertEquals(user, inserted);
|
||||
assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user));
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ package org.springframework.data.couchbase.core;
|
||||
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.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Arrays;
|
||||
@@ -32,6 +34,7 @@ import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.query.QueryCriteria;
|
||||
@@ -46,30 +49,42 @@ import org.springframework.data.couchbase.domain.UserSubmission;
|
||||
import org.springframework.data.couchbase.domain.UserSubmissionProjected;
|
||||
import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider;
|
||||
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.CollectionAwareIntegrationTests;
|
||||
import org.springframework.data.couchbase.util.IgnoreWhen;
|
||||
|
||||
import com.couchbase.client.core.error.AmbiguousTimeoutException;
|
||||
import com.couchbase.client.core.error.UnambiguousTimeoutException;
|
||||
import com.couchbase.client.java.analytics.AnalyticsOptions;
|
||||
import com.couchbase.client.java.kv.ExistsOptions;
|
||||
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
|
||||
import com.couchbase.client.java.kv.GetOptions;
|
||||
import com.couchbase.client.java.kv.InsertOptions;
|
||||
import com.couchbase.client.java.kv.RemoveOptions;
|
||||
import com.couchbase.client.java.kv.ReplaceOptions;
|
||||
import com.couchbase.client.java.kv.UpsertOptions;
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* Query tests Theses tests rely on a cb server running This class tests collection support with
|
||||
* inCollection(collection) It should be identical to CouchbaseTemplateQueryIntegrationTests except for the setup and
|
||||
* the inCollection(collectionName) calls. Testing without collections could also be done by this class simply by using
|
||||
* scopeName = null and collectionName = null (except for inCollection() checks that the collectionName is not null)
|
||||
* inCollection(collection), inScope(scope) and withOptions(options). Testing without collections could also be done by
|
||||
* this class simply by using scopeName = null and collectionName = null
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
|
||||
class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests {
|
||||
|
||||
Airport vie = new Airport("airports::vie", "vie", "loww");
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeAll() {
|
||||
// first call the super method
|
||||
callSuperBeforeAll(new Object() {});
|
||||
// then do processing for this class
|
||||
// collectionName = null;
|
||||
// scopeName = null;
|
||||
// no-op
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
@@ -87,15 +102,26 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
|
||||
super.beforeEach();
|
||||
// then do processing for this class
|
||||
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
|
||||
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inCollection(collectionName).all();
|
||||
couchbaseTemplate.removeByQuery(Airport.class).inScope(scopeName).inCollection(collectionName).all();
|
||||
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName)
|
||||
.inCollection(collectionName).all();
|
||||
couchbaseTemplate.removeByQuery(Airport.class).inScope(otherScope).inCollection(otherCollection).all();
|
||||
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope)
|
||||
.inCollection(otherCollection).all();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@Override
|
||||
public void afterEach() {
|
||||
// first call the super method
|
||||
// first do processing for this class
|
||||
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
|
||||
// query with REQUEST_PLUS to ensure that the remove has completed.
|
||||
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inCollection(collectionName).all();
|
||||
// then call the super method
|
||||
super.afterEach();
|
||||
// then do processing for this class
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -200,6 +226,9 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
|
||||
.matching(specialUsers).all().collectList().block();
|
||||
assertEquals(1, foundUsersReactive.size());
|
||||
|
||||
couchbaseTemplate.removeByQuery(UserSubmission.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
couchbaseTemplate.removeByQuery(UserSubmission.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -207,16 +236,20 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
|
||||
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
|
||||
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
|
||||
|
||||
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2));
|
||||
couchbaseTemplate.upsertById(User.class).inScope(scopeName).inCollection(collectionName)
|
||||
.all(Arrays.asList(user1, user2));
|
||||
|
||||
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(user1.getId()));
|
||||
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(user2.getId()));
|
||||
assertTrue(couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName).one(user1.getId()));
|
||||
assertTrue(couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName).one(user2.getId()));
|
||||
|
||||
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inCollection(collectionName).all();
|
||||
List<RemoveResult> result = couchbaseTemplate.removeByQuery(User.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
|
||||
assertEquals(2, result.size(), "should have deleted user1 and user2");
|
||||
|
||||
assertNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user1.getId()));
|
||||
assertNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user2.getId()));
|
||||
assertNull(
|
||||
couchbaseTemplate.findById(User.class).inScope(scopeName).inCollection(collectionName).one(user1.getId()));
|
||||
assertNull(
|
||||
couchbaseTemplate.findById(User.class).inScope(scopeName).inCollection(collectionName).one(user2.getId()));
|
||||
|
||||
}
|
||||
|
||||
@@ -315,7 +348,7 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
|
||||
|
||||
// count( distinct icao )
|
||||
// not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
|
||||
long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
|
||||
Long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
|
||||
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count()
|
||||
.block();
|
||||
assertEquals(2, count1);
|
||||
@@ -336,4 +369,387 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* find . -name 'Exec*OperationSupport.java'|awk -F/ '{print $NF}'|sort| awk -F. '{print "* ", NR, ")",$1, ""}'<br>
|
||||
* 1) ExecutableExistsByIdOperationSupport <br>
|
||||
* 2) ExecutableFindByAnalyticsOperationSupport <br>
|
||||
* 3) ExecutableFindByIdOperationSupport <br>
|
||||
* 4) ExecutableFindByQueryOperationSupport <br>
|
||||
* 5) ExecutableFindFromReplicasByIdOperationSupport <br>
|
||||
* 6) ExecutableInsertByIdOperationSupport <br>
|
||||
* 7) ExecutableRemoveByIdOperationSupport <br>
|
||||
* 8) ExecutableRemoveByQueryOperationSupport <br>
|
||||
* 9) ExecutableReplaceByIdOperationSupport <br>
|
||||
* 10)ExecutableUpsertByIdOperationSupport <br>
|
||||
*/
|
||||
@Test
|
||||
public void existsById() { // 1
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
ExistsOptions existsOptions = ExistsOptions.existsOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.one(vie);
|
||||
try {
|
||||
Boolean exists = couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(existsOptions).one(saved.getId());
|
||||
assertTrue(exists, "Airport should exist: " + saved.getId());
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // needs analytics data set
|
||||
public void findByAnalytics() { // 2
|
||||
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.one(vie);
|
||||
try {
|
||||
List<Airport> found = couchbaseTemplate.findByAnalytics(Airport.class).inScope(scopeName)
|
||||
.inCollection(collectionName).withOptions(options).all();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findById() { // 3
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.one(vie);
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(saved.getId());
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByQuery() { // 4
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.one(vie);
|
||||
try {
|
||||
List<Airport> found = couchbaseTemplate.findByQuery(Airport.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).all();
|
||||
assertEquals(saved.getId(), found.get(0).getId());
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromReplicasById() { // 5
|
||||
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.one(vie);
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findFromReplicasById(Airport.class).inScope(scopeName)
|
||||
.inCollection(collectionName).withOptions(options).any(saved.getId());
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertById() { // 6
|
||||
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(vie.withId(UUID.randomUUID().toString()));
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(getOptions).one(saved.getId());
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeById() { // 7
|
||||
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.one(vie);
|
||||
RemoveResult removeResult = couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(saved.getId());
|
||||
assertEquals(saved.getId(), removeResult.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByQuery() { // 8
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.one(vie);
|
||||
List<RemoveResult> removeResults = couchbaseTemplate.removeByQuery(Airport.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all();
|
||||
assertEquals(saved.getId(), removeResults.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceById() { // 9
|
||||
InsertOptions insertOptions = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
|
||||
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(insertOptions).one(vie);
|
||||
Airport replaced = couchbaseTemplate.replaceById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(vie.withIcao("newIcao"));
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(getOptions).one(saved.getId());
|
||||
assertEquals(replaced, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upsertById() { // 10
|
||||
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
|
||||
Airport saved = couchbaseTemplate.upsertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(vie);
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(getOptions).one(saved.getId());
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsByIdOther() { // 1
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
ExistsOptions existsOptions = ExistsOptions.existsOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
try {
|
||||
Boolean exists = couchbaseTemplate.existsById().inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(existsOptions).one(saved.getId());
|
||||
assertTrue(exists, "Airport should exist: " + saved.getId());
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // needs analytics data set
|
||||
public void findByAnalyticsOther() { // 2
|
||||
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
try {
|
||||
List<Airport> found = couchbaseTemplate.findByAnalytics(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).all();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdOther() { // 3
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(saved.getId());
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByQueryOther() { // 4
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
try {
|
||||
List<Airport> found = couchbaseTemplate.findByQuery(Airport.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).all();
|
||||
assertEquals(saved.getId(), found.get(0).getId());
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromReplicasByIdOther() { // 5
|
||||
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findFromReplicasById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).any(saved.getId());
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertByIdOther() { // 6
|
||||
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(vie.withId(UUID.randomUUID().toString()));
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(getOptions).one(saved.getId());
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByIdOther() { // 7
|
||||
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
RemoveResult removeResult = couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(saved.getId());
|
||||
assertEquals(saved.getId(), removeResult.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByQueryOther() { // 8
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
List<RemoveResult> removeResults = couchbaseTemplate.removeByQuery(Airport.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all();
|
||||
assertEquals(saved.getId(), removeResults.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceByIdOther() { // 9
|
||||
InsertOptions insertOptions = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
|
||||
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(insertOptions).one(vie);
|
||||
Airport replaced = couchbaseTemplate.replaceById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(vie.withIcao("newIcao"));
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(getOptions).one(saved.getId());
|
||||
assertEquals(replaced, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upsertByIdOther() { // 10
|
||||
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
|
||||
Airport saved = couchbaseTemplate.upsertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(vie);
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(getOptions).one(saved.getId());
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsByIdOptions() { // 1 - Options
|
||||
ExistsOptions options = ExistsOptions.existsOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(UnambiguousTimeoutException.class, () -> couchbaseTemplate.existsById().inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // needs analytics data set
|
||||
public void findByAnalyticsOptions() { // 2
|
||||
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.findByAnalytics(Airport.class)
|
||||
.inScope(otherScope).inCollection(otherCollection).withOptions(options).all());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdOptions() { // 3
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(UnambiguousTimeoutException.class, () -> couchbaseTemplate.findById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByQueryOptions() { // 4
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class,
|
||||
() -> couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inScope(otherScope).inCollection(otherCollection).withOptions(options).all());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromReplicasByIdOptions() { // 5
|
||||
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofNanos(1000));
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
try {
|
||||
Airport found = couchbaseTemplate.findFromReplicasById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).any(saved.getId());
|
||||
assertNull(found, "should not have found document in short timeout");
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertByIdOptions() { // 6
|
||||
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.insertById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.withId(UUID.randomUUID().toString())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByIdOptions() { // 7 - options
|
||||
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.one(vie);
|
||||
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.removeById().inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.getId()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByQueryOptions() { // 8 - options
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class,
|
||||
() -> couchbaseTemplate.removeByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inScope(otherScope).inCollection(otherCollection).withOptions(options)
|
||||
.matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceByIdOptions() { // 9 - options
|
||||
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.replaceById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.withIcao("newIcao")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upsertByIdOptions() { // 10 - options
|
||||
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.upsertById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ 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 static org.springframework.data.couchbase.core.query.N1QLExpression.i;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -33,13 +31,10 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.core.query.QueryCriteria;
|
||||
import org.springframework.data.couchbase.domain.Address;
|
||||
import org.springframework.data.couchbase.domain.Airport;
|
||||
import org.springframework.data.couchbase.domain.Config;
|
||||
import org.springframework.data.couchbase.domain.Course;
|
||||
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
|
||||
import org.springframework.data.couchbase.domain.Submission;
|
||||
@@ -69,11 +64,15 @@ class CouchbaseTemplateQueryIntegrationTests extends JavaIntegrationTests {
|
||||
@BeforeEach
|
||||
@Override
|
||||
public void beforeEach() {
|
||||
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
|
||||
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
|
||||
reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE);
|
||||
super.beforeEach();
|
||||
// already setup by JavaIntegrationTests.beforeAll()
|
||||
// ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
|
||||
// couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
|
||||
// reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE);
|
||||
// ensure each test starts with clean state
|
||||
|
||||
couchbaseTemplate.removeByQuery(User.class).all();
|
||||
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,13 +107,13 @@ class CouchbaseTemplateQueryIntegrationTests extends JavaIntegrationTests {
|
||||
couchbaseTemplate.findById(User.class).one(user1.getId());
|
||||
reactiveCouchbaseTemplate.findById(User.class).one(user1.getId()).block();
|
||||
} finally {
|
||||
couchbaseTemplate.removeByQuery(User.class).all();
|
||||
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
}
|
||||
|
||||
User usery = couchbaseTemplate.findById(User.class).one("userx");
|
||||
assertNull(usery, "usery should be null");
|
||||
User userz = reactiveCouchbaseTemplate.findById(User.class).one("userx").block();
|
||||
assertNull(userz, "uz should be null");
|
||||
User usery = couchbaseTemplate.findById(User.class).one("user1");
|
||||
assertNull(usery, "user1 should have been deleted");
|
||||
User userz = reactiveCouchbaseTemplate.findById(User.class).one("user2").block();
|
||||
assertNull(userz, "user2 should have been deleted");
|
||||
|
||||
}
|
||||
|
||||
@@ -136,6 +135,8 @@ class CouchbaseTemplateQueryIntegrationTests extends JavaIntegrationTests {
|
||||
@Test
|
||||
void findByMatchingQueryProjected() {
|
||||
|
||||
couchbaseTemplate.removeByQuery(UserSubmission.class).all();
|
||||
|
||||
UserSubmission user = new UserSubmission();
|
||||
user.setId(UUID.randomUUID().toString());
|
||||
user.setUsername("dave");
|
||||
|
||||
@@ -224,7 +224,7 @@ class QueryCriteriaTests {
|
||||
@Test
|
||||
void testIn() {
|
||||
String[] args = new String[] { "gump", "davis" };
|
||||
QueryCriteria c = where(i("name")).in(args);
|
||||
QueryCriteria c = where(i("name")).in((Object)args);
|
||||
assertEquals("`name` in ( [\"gump\",\"davis\"] )", c.export());
|
||||
JsonArray parameters = JsonArray.create();
|
||||
assertEquals("`name` in ( $1 )", c.export(new int[1], parameters, null));
|
||||
@@ -234,7 +234,7 @@ class QueryCriteriaTests {
|
||||
@Test
|
||||
void testNotIn() {
|
||||
String[] args = new String[] { "gump", "davis" };
|
||||
QueryCriteria c = where(i("name")).notIn(args);
|
||||
QueryCriteria c = where(i("name")).notIn((Object)args);
|
||||
assertEquals("not( (`name` in ( [\"gump\",\"davis\"] )) )", c.export());
|
||||
JsonArray parameters = JsonArray.create();
|
||||
assertEquals("not( (`name` in ( $1 )) )", c.export(new int[1], parameters, null));
|
||||
|
||||
@@ -0,0 +1,745 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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 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.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.RemoveResult;
|
||||
import org.springframework.data.couchbase.domain.Address;
|
||||
import org.springframework.data.couchbase.domain.Airport;
|
||||
import org.springframework.data.couchbase.domain.Course;
|
||||
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
|
||||
import org.springframework.data.couchbase.domain.Submission;
|
||||
import org.springframework.data.couchbase.domain.User;
|
||||
import org.springframework.data.couchbase.domain.UserJustLastName;
|
||||
import org.springframework.data.couchbase.domain.UserSubmission;
|
||||
import org.springframework.data.couchbase.domain.UserSubmissionProjected;
|
||||
import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider;
|
||||
import org.springframework.data.couchbase.util.Capabilities;
|
||||
import org.springframework.data.couchbase.util.ClusterType;
|
||||
import org.springframework.data.couchbase.util.CollectionAwareIntegrationTests;
|
||||
import org.springframework.data.couchbase.util.IgnoreWhen;
|
||||
|
||||
import com.couchbase.client.core.error.AmbiguousTimeoutException;
|
||||
import com.couchbase.client.core.error.UnambiguousTimeoutException;
|
||||
import com.couchbase.client.java.analytics.AnalyticsOptions;
|
||||
import com.couchbase.client.java.kv.ExistsOptions;
|
||||
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
|
||||
import com.couchbase.client.java.kv.GetOptions;
|
||||
import com.couchbase.client.java.kv.InsertOptions;
|
||||
import com.couchbase.client.java.kv.RemoveOptions;
|
||||
import com.couchbase.client.java.kv.ReplaceOptions;
|
||||
import com.couchbase.client.java.kv.UpsertOptions;
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* Query tests Theses tests rely on a cb server running This class tests collection support with
|
||||
* inCollection(collection), inScope(scope) and withOptions(options). Testing without collections could also be done by
|
||||
* this class simply by using scopeName = null and collectionName = null
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
|
||||
class ReactiveCouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests {
|
||||
|
||||
Airport vie = new Airport("airports::vie", "vie", "low7");
|
||||
ReactiveCouchbaseTemplate template = reactiveCouchbaseTemplate;
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeAll() {
|
||||
// first call the super method
|
||||
callSuperBeforeAll(new Object() {});
|
||||
// then do processing for this class
|
||||
// no-op
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterAll() {
|
||||
// first do the processing for this class
|
||||
// no-op
|
||||
// then call the super method
|
||||
callSuperAfterAll(new Object() {});
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
public void beforeEach() {
|
||||
// first call the super method
|
||||
super.beforeEach();
|
||||
// then do processing for this class
|
||||
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
|
||||
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inCollection(collectionName).all();
|
||||
couchbaseTemplate.removeByQuery(Airport.class).inScope(scopeName).inCollection(collectionName).all();
|
||||
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName)
|
||||
.inCollection(collectionName).all();
|
||||
couchbaseTemplate.removeByQuery(Airport.class).inScope(otherScope).inCollection(otherCollection).all();
|
||||
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope)
|
||||
.inCollection(otherCollection).all();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@Override
|
||||
public void afterEach() {
|
||||
// first do processing for this class
|
||||
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
|
||||
// query with REQUEST_PLUS to ensure that the remove has completed.
|
||||
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inCollection(collectionName).all();
|
||||
// then call the super method
|
||||
super.afterEach();
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByQueryAll() {
|
||||
try {
|
||||
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
|
||||
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
|
||||
|
||||
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2));
|
||||
|
||||
final List<User> foundUsers = couchbaseTemplate.findByQuery(User.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
|
||||
|
||||
for (User u : foundUsers) {
|
||||
if (!(u.equals(user1) || u.equals(user2))) {
|
||||
// somebody didn't clean up after themselves.
|
||||
couchbaseTemplate.removeById().inCollection(collectionName).one(u.getId());
|
||||
}
|
||||
}
|
||||
assertEquals(2, foundUsers.size());
|
||||
TemporalAccessor auditTime = new AuditingDateTimeProvider().getNow().get();
|
||||
long auditMillis = Instant.from(auditTime).toEpochMilli();
|
||||
String auditUser = new NaiveAuditorAware().getCurrentAuditor().get();
|
||||
|
||||
for (User u : foundUsers) {
|
||||
assertTrue(u.equals(user1) || u.equals(user2));
|
||||
assertEquals(auditUser, u.getCreator());
|
||||
assertEquals(auditMillis, u.getCreatedDate());
|
||||
assertEquals(auditUser, u.getLastModifiedBy());
|
||||
assertEquals(auditMillis, u.getLastModifiedDate());
|
||||
}
|
||||
couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user1.getId());
|
||||
reactiveCouchbaseTemplate.findById(User.class).inCollection(collectionName).one(user1.getId()).block();
|
||||
} finally {
|
||||
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
|
||||
}
|
||||
|
||||
User usery = couchbaseTemplate.findById(User.class).inCollection(collectionName).one("userx");
|
||||
assertNull(usery, "usery should be null");
|
||||
User userz = reactiveCouchbaseTemplate.findById(User.class).inCollection(collectionName).one("userx").block();
|
||||
assertNull(userz, "userz should be null");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByMatchingQuery() {
|
||||
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
|
||||
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
|
||||
User specialUser = new User(UUID.randomUUID().toString(), "special", "special");
|
||||
|
||||
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2, specialUser));
|
||||
|
||||
Query specialUsers = new Query(QueryCriteria.where("firstname").like("special"));
|
||||
final List<User> foundUsers = couchbaseTemplate.findByQuery(User.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).matching(specialUsers).all();
|
||||
|
||||
assertEquals(1, foundUsers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByMatchingQueryProjected() {
|
||||
|
||||
UserSubmission user = new UserSubmission();
|
||||
user.setId(UUID.randomUUID().toString());
|
||||
user.setUsername("dave");
|
||||
user.setRoles(Arrays.asList("role1", "role2"));
|
||||
Address address = new Address();
|
||||
address.setStreet("1234 Olcott Street");
|
||||
user.setAddress(address);
|
||||
user.setSubmissions(
|
||||
Arrays.asList(new Submission(UUID.randomUUID().toString(), user.getId(), "tid", "status", 123)));
|
||||
user.setCourses(Arrays.asList(new Course(UUID.randomUUID().toString(), user.getId(), "581"),
|
||||
new Course(UUID.randomUUID().toString(), user.getId(), "777")));
|
||||
couchbaseTemplate.upsertById(UserSubmission.class).inCollection(collectionName).one(user);
|
||||
|
||||
Query daveUsers = new Query(QueryCriteria.where("username").like("dave"));
|
||||
|
||||
final List<UserSubmissionProjected> foundUserSubmissions = couchbaseTemplate.findByQuery(UserSubmission.class)
|
||||
.as(UserSubmissionProjected.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inCollection(collectionName).matching(daveUsers).all();
|
||||
assertEquals(1, foundUserSubmissions.size());
|
||||
assertEquals(user.getUsername(), foundUserSubmissions.get(0).getUsername());
|
||||
assertEquals(user.getId(), foundUserSubmissions.get(0).getId());
|
||||
assertEquals(user.getCourses(), foundUserSubmissions.get(0).getCourses());
|
||||
assertEquals(user.getAddress(), foundUserSubmissions.get(0).getAddress());
|
||||
|
||||
couchbaseTemplate.removeByQuery(UserSubmission.class).inCollection(collectionName).all();
|
||||
|
||||
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
|
||||
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
|
||||
User specialUser = new User(UUID.randomUUID().toString(), "special", "special");
|
||||
|
||||
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2, specialUser));
|
||||
|
||||
Query specialUsers = new Query(QueryCriteria.where("firstname").like("special"));
|
||||
final List<UserJustLastName> foundUsers = couchbaseTemplate.findByQuery(User.class).as(UserJustLastName.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).matching(specialUsers).all();
|
||||
assertEquals(1, foundUsers.size());
|
||||
|
||||
final List<UserJustLastName> foundUsersReactive = reactiveCouchbaseTemplate.findByQuery(User.class)
|
||||
.as(UserJustLastName.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName)
|
||||
.matching(specialUsers).all().collectList().block();
|
||||
assertEquals(1, foundUsersReactive.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeByQueryAll() {
|
||||
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
|
||||
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
|
||||
|
||||
couchbaseTemplate.upsertById(User.class).inScope(scopeName).inCollection(collectionName)
|
||||
.all(Arrays.asList(user1, user2));
|
||||
|
||||
assertTrue(couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName).one(user1.getId()));
|
||||
assertTrue(couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName).one(user2.getId()));
|
||||
|
||||
List<RemoveResult> result = couchbaseTemplate.removeByQuery(User.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
|
||||
assertEquals(2, result.size(), "should have deleted user1 and user2");
|
||||
|
||||
assertNull(
|
||||
couchbaseTemplate.findById(User.class).inScope(scopeName).inCollection(collectionName).one(user1.getId()));
|
||||
assertNull(
|
||||
couchbaseTemplate.findById(User.class).inScope(scopeName).inCollection(collectionName).one(user2.getId()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void removeByMatchingQuery() {
|
||||
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
|
||||
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
|
||||
User specialUser = new User(UUID.randomUUID().toString(), "special", "special");
|
||||
|
||||
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2, specialUser));
|
||||
|
||||
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(user1.getId()));
|
||||
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(user2.getId()));
|
||||
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(specialUser.getId()));
|
||||
|
||||
Query nonSpecialUsers = new Query(QueryCriteria.where("firstname").notLike("special"));
|
||||
|
||||
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inCollection(collectionName).matching(nonSpecialUsers).all();
|
||||
|
||||
assertNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user1.getId()));
|
||||
assertNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user2.getId()));
|
||||
assertNotNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(specialUser.getId()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void distinct() {
|
||||
String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" };
|
||||
String[] icaos = { "ic0", "ic1", "ic0", "ic1", "ic0", "ic1", "ic0" };
|
||||
|
||||
try {
|
||||
for (int i = 0; i < iatas.length; i++) {
|
||||
Airport airport = new Airport("airports::" + iatas[i], iatas[i] /*iata*/, icaos[i] /* icao */);
|
||||
couchbaseTemplate.insertById(Airport.class).inCollection(collectionName).one(airport);
|
||||
}
|
||||
|
||||
// distinct and count(distinct(...)) calls. use as() and consistentWith to verify fluent api
|
||||
// as the fluent api for Distinct is tricky
|
||||
|
||||
// distinct icao
|
||||
List<Airport> airports1 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
|
||||
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
|
||||
assertEquals(2, airports1.size());
|
||||
|
||||
// distinct all-fields-in-Airport.class
|
||||
List<Airport> airports2 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(Airport.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
|
||||
assertEquals(7, airports2.size());
|
||||
|
||||
// count( distinct { iata, icao } )
|
||||
long count1 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "iata", "icao" })
|
||||
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count();
|
||||
assertEquals(7, count1);
|
||||
|
||||
// count( distinct (all fields in icaoClass)
|
||||
Class icaoClass = (new Object() {
|
||||
String iata;
|
||||
String icao;
|
||||
}).getClass();
|
||||
long count2 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(icaoClass)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count();
|
||||
assertEquals(7, count2);
|
||||
|
||||
} finally {
|
||||
couchbaseTemplate.removeById().inCollection(collectionName)
|
||||
.all(Arrays.stream(iatas).map((iata) -> "airports::" + iata).collect(Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void distinctReactive() {
|
||||
String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" };
|
||||
String[] icaos = { "ic0", "ic1", "ic0", "ic1", "ic0", "ic1", "ic0" };
|
||||
|
||||
try {
|
||||
for (int i = 0; i < iatas.length; i++) {
|
||||
Airport airport = new Airport("airports::" + iatas[i], iatas[i] /*iata*/, icaos[i] /* icao */);
|
||||
reactiveCouchbaseTemplate.insertById(Airport.class).inCollection(collectionName).one(airport).block();
|
||||
}
|
||||
|
||||
// distinct and count(distinct(...)) calls. use as() and consistentWith to verify fluent api
|
||||
// as the fluent api for Distinct is tricky
|
||||
|
||||
// distinct icao
|
||||
List<Airport> airports1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
|
||||
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all()
|
||||
.collectList().block();
|
||||
assertEquals(2, airports1.size());
|
||||
|
||||
// distinct all-fields-in-Airport.class
|
||||
List<Airport> airports2 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {})
|
||||
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all()
|
||||
.collectList().block();
|
||||
assertEquals(7, airports2.size());
|
||||
|
||||
// count( distinct icao )
|
||||
// not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
|
||||
Long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
|
||||
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count()
|
||||
.block();
|
||||
assertEquals(2, count1);
|
||||
|
||||
// count( distinct (all fields in icaoClass) // which only has one field
|
||||
// not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
|
||||
Class icaoClass = (new Object() {
|
||||
String icao;
|
||||
}).getClass();
|
||||
long count2 = (long) reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(icaoClass)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count().block();
|
||||
assertEquals(2, count2);
|
||||
|
||||
} finally {
|
||||
reactiveCouchbaseTemplate.removeById().inCollection(collectionName)
|
||||
.all(Arrays.stream(iatas).map((iata) -> "airports::" + iata).collect(Collectors.toSet())).collectList()
|
||||
.block();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* find . -name 'Exec*OperationSupport.java'|awk -F/ '{print $NF}'|sort| awk -F. '{print "* ", NR, ")",$1, ""}'<br>
|
||||
* 1) ExecutableExistsByIdOperationSupport <br>
|
||||
* 2) ExecutableFindByAnalyticsOperationSupport <br>
|
||||
* 3) ExecutableFindByIdOperationSupport <br>
|
||||
* 4) ExecutableFindByQueryOperationSupport <br>
|
||||
* 5) ExecutableFindFromReplicasByIdOperationSupport <br>
|
||||
* 6) ExecutableInsertByIdOperationSupport <br>
|
||||
* 7) ExecutableRemoveByIdOperationSupport <br>
|
||||
* 8) ExecutableRemoveByQueryOperationSupport <br>
|
||||
* 9) ExecutableReplaceByIdOperationSupport <br>
|
||||
* 10)ExecutableUpsertByIdOperationSupport <br>
|
||||
*/
|
||||
@Test
|
||||
public void existsById() { // 1
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
ExistsOptions existsOptions = ExistsOptions.existsOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("low7")).block();
|
||||
try {
|
||||
Boolean exists = template.existsById().inScope(scopeName).inCollection(collectionName).withOptions(existsOptions)
|
||||
.one(saved.getId()).block();
|
||||
assertTrue(exists, "Airport should exist: " + saved.getId());
|
||||
} finally {
|
||||
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // needs analytics data set
|
||||
public void findByAnalytics() { // 2
|
||||
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("low8")).block();
|
||||
try {
|
||||
List<Airport> found = template.findByAnalytics(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).all().collectList().block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findById() { // 3
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("low9")).block();
|
||||
try {
|
||||
Airport found = template.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(saved.getId()).block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByQuery() { // 4
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("lowa")).block();
|
||||
try {
|
||||
List<Airport> found = template.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inScope(scopeName).inCollection(collectionName).withOptions(options).all().collectList().block();
|
||||
assertEquals(saved.getId(), found.get(0).getId());
|
||||
} finally {
|
||||
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromReplicasById() { // 5
|
||||
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("lowb")).block();
|
||||
try {
|
||||
Airport found = template.findFromReplicasById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).any(saved.getId()).block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertById() { // 6
|
||||
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(vie.withIcao("lowc").withId(UUID.randomUUID().toString())).block();
|
||||
try {
|
||||
Airport found = template.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(getOptions).one(saved.getId()).block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeById() { // 7
|
||||
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("lowd")).block();
|
||||
RemoveResult removeResult = template.removeById().inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(saved.getId()).block();
|
||||
assertEquals(saved.getId(), removeResult.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByQuery() { // 8
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("lowe")).block();
|
||||
List<RemoveResult> removeResults = template.removeByQuery(Airport.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all().collectList()
|
||||
.block();
|
||||
assertEquals(saved.getId(), removeResults.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceById() { // 9
|
||||
InsertOptions insertOptions = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
|
||||
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(insertOptions).one(vie.withIcao("lowe")).block();
|
||||
Airport replaced = template.replaceById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(vie.withIcao("newIcao")).block();
|
||||
try {
|
||||
Airport found = template.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(getOptions).one(saved.getId()).block();
|
||||
assertEquals(replaced, found);
|
||||
} finally {
|
||||
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upsertById() { // 10
|
||||
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
|
||||
Airport saved = template.upsertById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(options).one(vie.withIcao("lowf")).block();
|
||||
try {
|
||||
Airport found = template.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
|
||||
.withOptions(getOptions).one(saved.getId()).block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsByIdOther() { // 1
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
ExistsOptions existsOptions = ExistsOptions.existsOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowg"))
|
||||
.block();
|
||||
try {
|
||||
Boolean exists = template.existsById().inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(existsOptions).one(saved.getId()).block();
|
||||
assertTrue(exists, "Airport should exist: " + saved.getId());
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // needs analytics data set
|
||||
public void findByAnalyticsOther() { // 2
|
||||
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowh"))
|
||||
.block();
|
||||
try {
|
||||
List<Airport> found = template.findByAnalytics(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).all().collectList().block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdOther() { // 3
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowi"))
|
||||
.block();
|
||||
try {
|
||||
Airport found = template.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(saved.getId()).block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByQueryOther() { // 4
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowj"))
|
||||
.block();
|
||||
try {
|
||||
List<Airport> found = template.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inScope(otherScope).inCollection(otherCollection).withOptions(options).all().collectList().block();
|
||||
assertEquals(saved.getId(), found.get(0).getId());
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromReplicasByIdOther() { // 5
|
||||
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowk"))
|
||||
.block();
|
||||
try {
|
||||
Airport found = template.findFromReplicasById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).any(saved.getId()).block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertByIdOther() { // 6
|
||||
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(vie.withIcao("lowl").withId(UUID.randomUUID().toString())).block();
|
||||
try {
|
||||
Airport found = template.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(getOptions).one(saved.getId()).block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByIdOther() { // 7
|
||||
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowm"))
|
||||
.block();
|
||||
RemoveResult removeResult = template.removeById().inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(saved.getId()).block();
|
||||
assertEquals(saved.getId(), removeResult.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByQueryOther() { // 8
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lown"))
|
||||
.block();
|
||||
List<RemoveResult> removeResults = template.removeByQuery(Airport.class)
|
||||
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all().collectList()
|
||||
.block();
|
||||
assertEquals(saved.getId(), removeResults.get(0).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceByIdOther() { // 9
|
||||
InsertOptions insertOptions = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
|
||||
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(insertOptions).one(vie.withIcao("lown")).block();
|
||||
Airport replaced = template.replaceById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(vie.withIcao("newIcao")).block();
|
||||
try {
|
||||
Airport found = template.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(getOptions).one(saved.getId()).block();
|
||||
assertEquals(replaced, found);
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upsertByIdOther() { // 10
|
||||
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofSeconds(10));
|
||||
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
|
||||
|
||||
Airport saved = template.upsertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).one(vie.withIcao("lowo")).block();
|
||||
try {
|
||||
Airport found = template.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(getOptions).one(saved.getId()).block();
|
||||
assertEquals(saved, found);
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existsByIdOptions() { // 1 - Options
|
||||
ExistsOptions options = ExistsOptions.existsOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(UnambiguousTimeoutException.class, () -> template.existsById().inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.getId()).block());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled // needs analytics data set
|
||||
public void findByAnalyticsOptions() { // 2
|
||||
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> template.findByAnalytics(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).all().collectList().block());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByIdOptions() { // 3
|
||||
GetOptions options = GetOptions.getOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(UnambiguousTimeoutException.class, () -> template.findById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.getId()).block());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findByQueryOptions() { // 4
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class,
|
||||
() -> template.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).all().collectList().block());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFromReplicasByIdOptions() { // 5
|
||||
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofNanos(1000));
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie)
|
||||
.block();
|
||||
try {
|
||||
Airport found = template.findFromReplicasById(Airport.class).inScope(otherScope).inCollection(otherCollection)
|
||||
.withOptions(options).any(saved.getId()).block();
|
||||
assertNull(found, "should not have found document in short timeout");
|
||||
} finally {
|
||||
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertByIdOptions() { // 6
|
||||
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> template.insertById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.withId(UUID.randomUUID().toString())).block());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByIdOptions() { // 7 - options
|
||||
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie)
|
||||
.block();
|
||||
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> template.removeById().inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.getId()).block());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeByQueryOptions() { // 8 - options
|
||||
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class,
|
||||
() -> template.removeByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inScope(otherScope).inCollection(otherCollection).withOptions(options)
|
||||
.matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all().collectList().block());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceByIdOptions() { // 9 - options
|
||||
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> template.replaceById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie.withIcao("newIcao")).block());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upsertByIdOptions() { // 10 - options
|
||||
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofNanos(10));
|
||||
assertThrows(AmbiguousTimeoutException.class, () -> template.upsertById(Airport.class).inScope(otherScope)
|
||||
.inCollection(otherCollection).withOptions(options).one(vie).block());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,7 +48,7 @@ import com.couchbase.client.java.json.JacksonTransformers;
|
||||
*/
|
||||
@Configuration
|
||||
@EnableCouchbaseRepositories
|
||||
@EnableCouchbaseAuditing(auditorAwareRef="auditorAwareRef", dateTimeProviderRef="dateTimeProviderRef") // this activates auditing
|
||||
@EnableCouchbaseAuditing(auditorAwareRef = "auditorAwareRef", dateTimeProviderRef = "dateTimeProviderRef")
|
||||
public class Config extends AbstractCouchbaseConfiguration {
|
||||
String bucketname = "travel-sample";
|
||||
String username = "Administrator";
|
||||
@@ -205,4 +205,15 @@ public class Config extends AbstractCouchbaseConfiguration {
|
||||
return "t"; // this will override '_class', is passed in to new CustomMappingCouchbaseConverter
|
||||
}
|
||||
|
||||
static String scopeName = null;
|
||||
|
||||
@Override
|
||||
protected String getScopeName() {
|
||||
return scopeName;
|
||||
}
|
||||
|
||||
public static void setScopeName(String scopeName) {
|
||||
Config.scopeName = scopeName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.data.couchbase.domain;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
|
||||
|
||||
/**
|
||||
* Configuration that uses a scope. This is a separate class as it is difficult to debug if you forget to unset the
|
||||
* scopeName and the config is used for non-collection operations.
|
||||
*
|
||||
* @Author Michael Reiche
|
||||
*/
|
||||
@Configuration
|
||||
@EnableCouchbaseRepositories
|
||||
public class ConfigScoped extends Config {
|
||||
|
||||
static String scopeName = null;
|
||||
|
||||
@Override
|
||||
protected String getScopeName() {
|
||||
return scopeName;
|
||||
}
|
||||
|
||||
public static void setScopeName(String scopeName) {
|
||||
ConfigScoped.scopeName = scopeName;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,21 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.couchbase.domain;
|
||||
|
||||
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
@@ -9,13 +23,16 @@ import org.springframework.data.mapping.context.MappingContext;
|
||||
public class CustomMappingCouchbaseConverter extends MappingCouchbaseConverter {
|
||||
|
||||
/**
|
||||
* this constructer creates a TypeBasedCouchbaseTypeMapper with the specified typeKey
|
||||
* while MappingCouchbaseConverter uses a DefaultCouchbaseTypeMapper
|
||||
* typeMapper = new DefaultCouchbaseTypeMapper(typeKey != null ? typeKey : TYPEKEY_DEFAULT);
|
||||
* this constructer creates a TypeBasedCouchbaseTypeMapper with the specified typeKey while MappingCouchbaseConverter
|
||||
* uses a DefaultCouchbaseTypeMapper typeMapper = new DefaultCouchbaseTypeMapper(typeKey != null ? typeKey :
|
||||
* TYPEKEY_DEFAULT);
|
||||
*
|
||||
* @param mappingContext
|
||||
* @param typeKey - the typeKey to be used (normally "_class")
|
||||
*/
|
||||
public CustomMappingCouchbaseConverter(final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext, final String typeKey) {
|
||||
public CustomMappingCouchbaseConverter(
|
||||
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
|
||||
final String typeKey) {
|
||||
super(mappingContext, typeKey);
|
||||
this.typeMapper = new TypeBasedCouchbaseTypeMapper(typeKey);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
package org.springframework.data.couchbase.domain;
|
||||
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryProfile;
|
||||
import com.couchbase.client.java.query.QueryResult;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
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.RemoveResult;
|
||||
import org.springframework.data.couchbase.util.Capabilities;
|
||||
import org.springframework.data.couchbase.util.ClusterType;
|
||||
import org.springframework.data.couchbase.util.IgnoreWhen;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.ParallelFlux;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
|
||||
import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories;
|
||||
import org.springframework.data.couchbase.util.JavaIntegrationTests;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import com.couchbase.client.java.Collection;
|
||||
import com.couchbase.client.java.ReactiveCollection;
|
||||
import com.couchbase.client.java.json.JsonObject;
|
||||
import com.couchbase.client.java.kv.GetResult;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@SpringJUnitConfig(FluxTest.Config.class)
|
||||
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
|
||||
public class FluxTest extends JavaIntegrationTests {
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeEverything() {
|
||||
/**
|
||||
* The couchbaseTemplate inherited from JavaIntegrationTests uses org.springframework.data.couchbase.domain.Config
|
||||
* It has typeName = 't' (instead of _class). Don't use it.
|
||||
*/
|
||||
ApplicationContext ac = new AnnotationConfigApplicationContext(FluxTest.Config.class);
|
||||
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(BeanNames.COUCHBASE_TEMPLATE);
|
||||
reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(BeanNames.REACTIVE_COUCHBASE_TEMPLATE);
|
||||
collection = couchbaseTemplate.getCouchbaseClientFactory().getBucket().defaultCollection();
|
||||
rCollection = couchbaseTemplate.getCouchbaseClientFactory().getBucket().reactive().defaultCollection();
|
||||
for (String k : keyList) {
|
||||
couchbaseTemplate.getCouchbaseClientFactory().getBucket().defaultCollection().upsert(k,
|
||||
JsonObject.create().put("x", k));
|
||||
}
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterEverthing() {
|
||||
couchbaseTemplate.removeByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@Override
|
||||
public void beforeEach() {
|
||||
super.beforeEach();
|
||||
}
|
||||
|
||||
static List<String> keyList = Arrays.asList("a", "b", "c", "d", "e");
|
||||
static Collection collection;
|
||||
static ReactiveCollection rCollection;
|
||||
@Autowired ReactiveAirportRepository airportRepository; // intellij flags "Could not Autowire", but it runs ok.
|
||||
|
||||
AtomicInteger rCat = new AtomicInteger(0);
|
||||
AtomicInteger rFlat = new AtomicInteger(0);
|
||||
|
||||
@Test
|
||||
public void concatMapCB() throws Exception {
|
||||
System.out.println("Start concatMapCB");
|
||||
System.out.println("\n******** Using concatMap() *********");
|
||||
ParallelFlux<GetResult> concat = Flux.fromIterable(keyList).parallel(2).runOn(Schedulers.parallel())
|
||||
.concatMap(item -> cbGet(item)
|
||||
/* rCollection.get(item) */.doOnSubscribe((x) -> System.out.println(" +" + rCat.incrementAndGet()))
|
||||
.doOnTerminate(() -> System.out.println(" -" + rCat.decrementAndGet())));
|
||||
System.out.println(concat.sequential().collectList().block());
|
||||
}
|
||||
|
||||
@Test
|
||||
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
|
||||
public void cbse() {
|
||||
LinkedList<LinkedList<Airport>> listOfLists = new LinkedList<>();
|
||||
Airport a = new Airport(UUID.randomUUID().toString(), "iata", "lowp");
|
||||
String last = null;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
LinkedList<Airport> list = new LinkedList<>();
|
||||
for (int j = 0; j < 10; j++) {
|
||||
list.add(a.withId(UUID.randomUUID().toString()));
|
||||
last = a.getId();
|
||||
}
|
||||
listOfLists.add(list);
|
||||
}
|
||||
Flux<Object> af = Flux.fromIterable(listOfLists).concatMap(catalogToStore -> Flux.fromIterable(catalogToStore)
|
||||
.parallel(4).runOn(Schedulers.parallel()).concatMap((entity) -> airportRepository.save(entity)));
|
||||
List<Object> saved = af.collectList().block();
|
||||
System.out.println("results.size() : " + saved.size());
|
||||
|
||||
String statement = "select * from `" + /*config().bucketname()*/ "_default" + "` where META().id >= '" + last + "'";
|
||||
System.out.println("statement: " + statement);
|
||||
try {
|
||||
QueryResult qr = couchbaseTemplate.getCouchbaseClientFactory().getScope().query(statement,
|
||||
QueryOptions.queryOptions().profile(QueryProfile.PHASES));
|
||||
List<RemoveResult> rr = couchbaseTemplate.removeByQuery(Airport.class)
|
||||
.withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)).all();
|
||||
System.out.println(qr.metaData().profile().get());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
List<Airport> airports = airportRepository.findAll().collectList().block();
|
||||
assertEquals(0, airports.size(), "should have been all deleted");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flatMapCB() throws Exception {
|
||||
System.out.println("Start flatMapCB");
|
||||
ParallelFlux<GetResult> concat = Flux.fromIterable(keyList).parallel(2).runOn(Schedulers.parallel())
|
||||
.flatMap(item -> cbGet(item) /* rCollection.get(item) */
|
||||
.doOnSubscribe((x) -> System.out.println(" +" + rCat.incrementAndGet()))
|
||||
.doOnTerminate(() -> System.out.println(" -" + rCat.decrementAndGet())));
|
||||
System.out.println(concat.sequential().collectList().block());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flatMapSyncCB() throws Exception {
|
||||
System.out.println("Start flatMapSyncCB");
|
||||
System.out.println("\n******** Using flatSyncMap() *********");
|
||||
ParallelFlux<GetResult> concat = Flux.fromIterable(keyList).parallel(2).runOn(Schedulers.parallel())
|
||||
.flatMap(item -> Flux.just(cbGetSync(item) /* collection.get(item) */));
|
||||
System.out.println(concat.sequential().collectList().block());
|
||||
;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flatMapVsConcatMapCB2() throws Exception {
|
||||
System.out.println("Start flatMapCB2");
|
||||
System.out.println("\n******** Using flatMap() *********");
|
||||
ParallelFlux<GetResult> flat = Flux.fromIterable(keyList).parallel(1).runOn(Schedulers.parallel())
|
||||
.flatMap(item -> rCollection.get(item).doOnSubscribe((x) -> System.out.println(" +" + rCat.incrementAndGet()))
|
||||
.doOnTerminate(() -> System.out.println(" -" + rCat.getAndDecrement())));
|
||||
System.out.println(flat.sequential().collectList().block());
|
||||
System.out.println("Start concatMapCB");
|
||||
System.out.println("\n******** Using concatMap() *********");
|
||||
ParallelFlux<GetResult> concat = Flux.fromIterable(keyList).parallel(2).runOn(Schedulers.parallel())
|
||||
.concatMap(item -> cbGet(item).doOnSubscribe((x) -> System.out.println(" +" + rCat.incrementAndGet()))
|
||||
.doOnTerminate(() -> System.out.println(" -" + rCat.getAndDecrement())));
|
||||
System.out.println(concat.sequential().collectList().block());
|
||||
;
|
||||
}
|
||||
|
||||
static Random r = new Random();
|
||||
|
||||
static void sleep(long sleepMs) {
|
||||
try {
|
||||
int random = Math.abs(r.nextInt() % 1000);
|
||||
Thread.sleep(sleepMs * random);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
AtomicInteger cbCount = new AtomicInteger();
|
||||
|
||||
Mono<GetResult> cbGet(String id) {
|
||||
// System.out.println(" =" + id);
|
||||
return rCollection.get(id);
|
||||
}
|
||||
|
||||
GetResult cbGetSync(String id) {
|
||||
// System.out.println(id + " +" + rCat.incrementAndGet());
|
||||
GetResult result = collection.get(id);
|
||||
// System.out.println(id + " -" + rCat.getAndDecrement());
|
||||
return result;
|
||||
}
|
||||
|
||||
static String tab(int len) {
|
||||
StringBuilder sb = new StringBuilder(len);
|
||||
for (int i = 0; i < len; i++)
|
||||
sb.append(" ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableReactiveCouchbaseRepositories("org.springframework.data.couchbase")
|
||||
static class Config extends AbstractCouchbaseConfiguration {
|
||||
|
||||
@Override
|
||||
public String getConnectionString() {
|
||||
return connectionString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUserName() {
|
||||
return config().adminUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return config().adminPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBucketName() {
|
||||
return bucketName();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,6 @@ package org.springframework.data.couchbase.domain;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.couchbase.client.core.deps.com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedBy;
|
||||
@@ -53,6 +52,7 @@ public class Person extends AbstractEntity {
|
||||
this();
|
||||
setFirstname(firstname);
|
||||
setLastname(lastname);
|
||||
setMiddlename("Nick");
|
||||
}
|
||||
|
||||
public Person(int id, String firstname, String lastname) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,15 +15,16 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.domain;
|
||||
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.data.couchbase.repository.Query;
|
||||
import org.springframework.data.couchbase.repository.ScanConsistency;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* @author Michael Reiche
|
||||
@@ -107,6 +108,9 @@ public interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
void deleteAll();
|
||||
|
||||
@ScanConsistency(query=QueryScanConsistency.REQUEST_PLUS)
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
List<Person> findByAddressStreet(String street);
|
||||
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
List<Person> findByMiddlename(String nickName);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.data.couchbase.domain;
|
||||
|
||||
import org.springframework.data.couchbase.core.RemoveResult;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -30,6 +31,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.couchbase.client.java.json.JsonArray;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
@@ -55,6 +57,15 @@ public interface ReactiveAirportRepository extends ReactiveSortingRepository<Air
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
Flux<Airport> findAllByIata(String iata);
|
||||
|
||||
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}")
|
||||
Flux<Airport> findAllPoliciesByApplicableTypes(String state, JsonArray applicableTypes);
|
||||
|
||||
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} and icao != $1 ORDER BY effectiveDateTime DESC LIMIT 1")
|
||||
Mono<Airport> findPolicySnapshotByPolicyIdAndEffectiveDateTime(String policyId, long effectiveDateTime);
|
||||
|
||||
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} ORDER BY effectiveDateTime DESC")
|
||||
Flux<Airport> findPolicySnapshotAll();
|
||||
|
||||
@Query("#{#n1ql.selectEntity} where iata = $1")
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
Flux<Airport> getAllByIata(String iata);
|
||||
|
||||
@@ -109,7 +109,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
void shouldSaveAndFindAll() {
|
||||
Airport vie = null;
|
||||
try {
|
||||
vie = new Airport("airports::vie", "vie", "loww");
|
||||
vie = new Airport("airports::vie", "vie", "low4");
|
||||
airportRepository.save(vie);
|
||||
List<Airport> all = new ArrayList<>();
|
||||
airportRepository.findAll().forEach(all::add);
|
||||
@@ -133,6 +133,22 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
personRepository.save(person);
|
||||
List<Person> persons = personRepository.findByAddressStreet("Maple");
|
||||
assertEquals(1, persons.size());
|
||||
List<Person> persons2 = personRepository.findByMiddlename("Nick");
|
||||
assertEquals(1, persons2.size());
|
||||
} finally {
|
||||
personRepository.deleteById(person.getId().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void annotatedFieldFind() {
|
||||
Person person = null;
|
||||
try {
|
||||
person = new Person(1, "first", "last");
|
||||
person.setMiddlename("Nick"); // middlename is stored as nickname
|
||||
personRepository.save(person);
|
||||
List<Person> persons2 = personRepository.findByMiddlename("Nick");
|
||||
assertEquals(1, persons2.size());
|
||||
} finally {
|
||||
personRepository.deleteById(person.getId().toString());
|
||||
}
|
||||
@@ -144,7 +160,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
Airport vie = null;
|
||||
Airport xxx = null;
|
||||
try {
|
||||
vie = new Airport("airports::vie", "vie", "loww");
|
||||
vie = new Airport("airports::vie", "vie", "low5");
|
||||
airportRepository.save(vie);
|
||||
xxx = new Airport("airports::xxx", "xxx", "xxxx");
|
||||
airportRepository.save(xxx);
|
||||
@@ -164,7 +180,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
void findBySimpleProperty() {
|
||||
Airport vie = null;
|
||||
try {
|
||||
vie = new Airport("airports::vie", "vie", "loww");
|
||||
vie = new Airport("airports::vie", "vie", "low6");
|
||||
vie = airportRepository.save(vie);
|
||||
List<Airport> airports = airportRepository.findAllByIata("vie");
|
||||
assertEquals(1, airports.size());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,11 +22,15 @@ 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.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -72,7 +76,7 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
|
||||
Airport vie = null;
|
||||
Airport jfk = null;
|
||||
try {
|
||||
vie = new Airport("airports::vie", "vie", "loww");
|
||||
vie = new Airport("airports::vie", "vie", "low1");
|
||||
airportRepository.save(vie).block();
|
||||
jfk = new Airport("airports::jfk", "JFK", "xxxx");
|
||||
airportRepository.save(jfk).block();
|
||||
@@ -92,7 +96,7 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
|
||||
void findBySimpleProperty() {
|
||||
Airport vie = null;
|
||||
try {
|
||||
vie = new Airport("airports::vie", "vie", "loww");
|
||||
vie = new Airport("airports::vie", "vie", "low2");
|
||||
airportRepository.save(vie).block();
|
||||
List<Airport> airports1 = airportRepository.findAllByIata("vie").collectList().block();
|
||||
assertEquals(1, airports1.size());
|
||||
@@ -121,6 +125,37 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
|
||||
userRepository.delete(user).block();
|
||||
}
|
||||
|
||||
@Test
|
||||
void limitTest() {
|
||||
Airport vie = new Airport("airports::vie", "vie", "low3");
|
||||
Airport saved1 = airportRepository.save(vie).block();
|
||||
Airport saved2 = airportRepository.save(vie.withId(UUID.randomUUID().toString())).block();
|
||||
try {
|
||||
airportRepository.findAll().collectList().block(); // findAll has QueryScanConsistency;
|
||||
Mono<Airport> airport = airportRepository.findPolicySnapshotByPolicyIdAndEffectiveDateTime("any", 0);
|
||||
System.out.println("------------------------------");
|
||||
System.out.println(airport.block());
|
||||
System.out.println("------------------------------");
|
||||
Flux<Airport> airports = airportRepository.findPolicySnapshotAll();
|
||||
System.out.println(airports.collectList().block());
|
||||
System.out.println("------------------------------");
|
||||
Mono<Airport> ap = getPolicyByIdAndEffectiveDateTime("x", Instant.now());
|
||||
System.out.println(ap.block());
|
||||
} finally {
|
||||
airportRepository.delete(saved1).block();
|
||||
airportRepository.delete(saved2).block();
|
||||
}
|
||||
}
|
||||
|
||||
public Mono<Airport> getPolicyByIdAndEffectiveDateTime(String policyId, Instant effectiveDateTime) {
|
||||
return airportRepository
|
||||
.findPolicySnapshotByPolicyIdAndEffectiveDateTime(policyId, effectiveDateTime.toEpochMilli())
|
||||
// .map(Airport::getEntity)
|
||||
.doOnError(
|
||||
error -> System.out.println("MSG='Exception happened while retrieving Policy by Id and effectiveDateTime', "
|
||||
+ "policyId={}, effectiveDateTime={}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void count() {
|
||||
Set<String> iatas = new HashSet();
|
||||
|
||||
@@ -32,6 +32,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.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.mapping.context.MappingContext;
|
||||
@@ -75,6 +77,19 @@ class N1qlQueryCreatorTests {
|
||||
assertEquals(query.export(), " WHERE " + where(i("firstname")).is("Oliver").export());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createsQueryFieldAnnotationCorrectly() throws Exception {
|
||||
String input = "findByMiddlename";
|
||||
PartTree tree = new PartTree(input, Person.class);
|
||||
Method method = PersonRepository.class.getMethod(input, String.class);
|
||||
|
||||
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), null, converter,
|
||||
bucketName);
|
||||
Query query = creator.createQuery();
|
||||
|
||||
assertEquals(query.export(), " WHERE " + where(i("nickname")).is("Oliver").export());
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryParametersArray() throws Exception {
|
||||
String input = "findByFirstnameIn";
|
||||
@@ -89,9 +104,9 @@ class N1qlQueryCreatorTests {
|
||||
// Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles"));
|
||||
assertEquals(expected.export(new int[1]), query.export(new int[1]));
|
||||
JsonObject expectedOptions = JsonObject.create();
|
||||
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
|
||||
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
|
||||
JsonObject actualOptions = JsonObject.create();
|
||||
expected.buildQueryOptions(null).build().injectParams(actualOptions);
|
||||
expected.buildQueryOptions(null, null).build().injectParams(actualOptions);
|
||||
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
|
||||
}
|
||||
|
||||
@@ -111,9 +126,9 @@ class N1qlQueryCreatorTests {
|
||||
Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles"));
|
||||
assertEquals(expected.export(new int[1]), query.export(new int[1]));
|
||||
JsonObject expectedOptions = JsonObject.create();
|
||||
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
|
||||
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
|
||||
JsonObject actualOptions = JsonObject.create();
|
||||
expected.buildQueryOptions(null).build().injectParams(actualOptions);
|
||||
expected.buildQueryOptions(null, null).build().injectParams(actualOptions);
|
||||
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
|
||||
}
|
||||
|
||||
@@ -133,9 +148,9 @@ class N1qlQueryCreatorTests {
|
||||
|
||||
assertEquals(expected.export(new int[1]), query.export(new int[1]));
|
||||
JsonObject expectedOptions = JsonObject.create();
|
||||
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
|
||||
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
|
||||
JsonObject actualOptions = JsonObject.create();
|
||||
expected.buildQueryOptions(null).build().injectParams(actualOptions);
|
||||
expected.buildQueryOptions(null, null).build().injectParams(actualOptions);
|
||||
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation;
|
||||
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.ExecutableFindByQuery;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
|
||||
@@ -101,7 +101,7 @@ class StringN1qlQueryCreatorTests extends ClusterAwareIntegrationTests {
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (Exception e) {}
|
||||
ExecutableFindByQueryOperation.ExecutableFindByQuery q = (ExecutableFindByQueryOperation.ExecutableFindByQuery) couchbaseTemplate
|
||||
ExecutableFindByQuery q = (ExecutableFindByQuery) couchbaseTemplate
|
||||
.findByQuery(Airline.class).matching(query);
|
||||
|
||||
Optional<Airline> al = q.one();
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
@@ -113,31 +114,57 @@ public abstract class ClusterAwareIntegrationTests {
|
||||
public void afterEach() {}
|
||||
|
||||
/**
|
||||
* This should probably be the first call in the @BeforeAll method of a test class.
|
||||
* This will call super.beforeAll() when called as callSuperBeforeAll(new Object() {}); this trickery is necessary
|
||||
* because super.beforeAll() cannot be used because it is a static method. it is possible and likely that the
|
||||
* beforeAll() method of should still be called even when a test class defines its own beforeAll() method which would
|
||||
* hide the beforeAll() of the super class.
|
||||
* This trickery is not necessary for before/AfterEach, as those are not static methods
|
||||
* This should probably be the first call in the @BeforeAll method of a test class. This will call super @BeforeAll
|
||||
* methods when called as callSuperBeforeAll(new Object() {}); this trickery is necessary because super.beforeAll()
|
||||
* cannot be used because it is a static method. it is possible and likely that the beforeAll() method of should still
|
||||
* be called even when a test class defines its own beforeAll() method which would hide the beforeAll() of the super
|
||||
* class. This trickery is not necessary for before/AfterEach, as those are not static methods
|
||||
*
|
||||
* @Author Michael Reiche
|
||||
*
|
||||
* @param createdHere - an object from a class defined in the calling class
|
||||
*/
|
||||
public static void callSuperBeforeAll(Object createdHere) {
|
||||
callSuper(createdHere, "beforeAll");
|
||||
callSuper(createdHere, BeforeAll.class);
|
||||
}
|
||||
|
||||
// see comments for callSuperBeforeAll()
|
||||
public static void callSuperAfterAll(Object createdHere) {
|
||||
callSuper(createdHere, "afterAll");
|
||||
callSuper(createdHere, AfterAll.class);
|
||||
}
|
||||
|
||||
private static void callSuper(Object createdHere, String methodName) {
|
||||
private static void callSuper(Object createdHere, Class annotationClass) {
|
||||
try {
|
||||
Method method = createdHere.getClass().getEnclosingClass().getSuperclass().getMethod(methodName);
|
||||
method.invoke(null);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
Class<?> encClass = createdHere.getClass().getEnclosingClass();
|
||||
Class<?> theClass = encClass;
|
||||
Annotation annotation = null;
|
||||
Method invokedSuper = null;
|
||||
if (annotationClass != BeforeAll.class && annotationClass != AfterAll.class) {
|
||||
throw new RuntimeException("can only call super for BeforeAll and AfterAll " + annotationClass);
|
||||
}
|
||||
// look recursively for @BeforeAll or @AfterAll methods
|
||||
// when one is found and executed, do not continue the recursive search
|
||||
// as it is expected that the @BeforeAll or @AfterAll methods call
|
||||
// any super methods explicitly - perhaps using callSuperBeforeAll() or callSuperAfterAll()
|
||||
// Note that if the @BeforeAll and @AfterAll methods have different names, they will be
|
||||
// called twice - once by this callSuper() mechanism and once by junit as the method will not be hidden
|
||||
while ((theClass = theClass.getSuperclass()) != null) {
|
||||
Method[] methods = theClass.getMethods();
|
||||
for (Method m : methods) {
|
||||
annotation = m.getAnnotation(annotationClass);
|
||||
if (annotation != null) {
|
||||
if (annotation != null) {
|
||||
m.invoke(null);
|
||||
invokedSuper = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (invokedSuper != null) { // called method is responsible for calling any super methods
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMP
|
||||
import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -34,7 +36,8 @@ import com.couchbase.client.java.Cluster;
|
||||
import com.couchbase.client.java.ClusterOptions;
|
||||
import com.couchbase.client.java.env.ClusterEnvironment;
|
||||
import com.couchbase.client.java.manager.collection.CollectionManager;
|
||||
import org.springframework.data.couchbase.domain.ConfigScoped;
|
||||
import com.couchbase.client.java.manager.collection.CollectionSpec;
|
||||
import com.couchbase.client.java.manager.collection.ScopeSpec;
|
||||
|
||||
/**
|
||||
* Provides Collection support for integration tests
|
||||
@@ -43,8 +46,10 @@ import org.springframework.data.couchbase.domain.ConfigScoped;
|
||||
*/
|
||||
public class CollectionAwareIntegrationTests extends JavaIntegrationTests {
|
||||
|
||||
public static String scopeName = "scope_" + randomString();
|
||||
public static String collectionName = "collection_" + randomString();
|
||||
public static String scopeName = "my_scope";// + randomString();
|
||||
public static String otherScope = "other_scope";
|
||||
public static String collectionName = "my_collection";// + randomString();
|
||||
public static String otherCollection = "other_collection";// + randomString();
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeAll() {
|
||||
@@ -57,20 +62,27 @@ public class CollectionAwareIntegrationTests extends JavaIntegrationTests {
|
||||
waitForService(bucket, ServiceType.QUERY);
|
||||
waitForQueryIndexerToHaveBucket(cluster, config().bucketname());
|
||||
CollectionManager collectionManager = bucket.collections();
|
||||
if (scopeName != null || collectionName != null) {
|
||||
setupScopeCollection(cluster, scopeName, collectionName, collectionManager);
|
||||
|
||||
setupScopeCollection(cluster, scopeName, collectionName, collectionManager);
|
||||
if (otherScope != null || otherCollection != null) {
|
||||
// afterAll should be undoing the creation of scope etc
|
||||
setupScopeCollection(cluster, otherScope, otherCollection, collectionManager);
|
||||
}
|
||||
|
||||
ConfigScoped.setScopeName(scopeName);
|
||||
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigScoped.class);
|
||||
Config.setScopeName(scopeName);
|
||||
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
|
||||
// the Config class has been modified, these need to be loaded again
|
||||
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
|
||||
reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void afterAll(){
|
||||
System.out.println("CollectionAwareIntegrationTests.afterAll()");
|
||||
ConfigScoped.setScopeName(null);
|
||||
callSuperBeforeAll(new Object() {});
|
||||
public static void afterAll() {
|
||||
Config.setScopeName(null);
|
||||
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
|
||||
// the Config class has been modified, these need to be loaded again
|
||||
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
|
||||
reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE);
|
||||
callSuperAfterAll(new Object() {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors
|
||||
* Copyright 2020-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,15 +39,16 @@ import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.couchbase.client.core.io.CollectionIdentifier;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.data.couchbase.CouchbaseClientFactory;
|
||||
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.domain.Config;
|
||||
|
||||
import com.couchbase.client.core.diagnostics.PingResult;
|
||||
import com.couchbase.client.core.diagnostics.PingState;
|
||||
@@ -80,7 +81,6 @@ import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryResult;
|
||||
import com.couchbase.client.java.search.SearchQuery;
|
||||
import com.couchbase.client.java.search.result.SearchResult;
|
||||
import org.springframework.data.couchbase.domain.Config;
|
||||
|
||||
/**
|
||||
* Extends the {@link ClusterAwareIntegrationTests} with java-client specific code.
|
||||
@@ -91,8 +91,9 @@ import org.springframework.data.couchbase.domain.Config;
|
||||
@Timeout(value = 10, unit = TimeUnit.MINUTES) // Safety timer so tests can't block CI executors
|
||||
public class JavaIntegrationTests extends ClusterAwareIntegrationTests {
|
||||
|
||||
@Autowired static public CouchbaseTemplate couchbaseTemplate;
|
||||
@Autowired static public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
|
||||
// Autowired annotation is not supported on static fields
|
||||
static public CouchbaseTemplate couchbaseTemplate;
|
||||
static public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeAll() {
|
||||
@@ -141,15 +142,28 @@ public class JavaIntegrationTests extends ClusterAwareIntegrationTests {
|
||||
ScopeSpec scopeSpec = ScopeSpec.create(scopeName);
|
||||
CollectionSpec collSpec = CollectionSpec.create(collectionName, scopeName);
|
||||
|
||||
if (!scopeName.equals("_default")) {
|
||||
collectionManager.createScope(scopeName);
|
||||
if (!scopeName.equals(CollectionIdentifier.DEFAULT_SCOPE)) {
|
||||
try {
|
||||
collectionManager.createScope(scopeName);
|
||||
waitUntilCondition(() -> scopeExists(collectionManager, scopeName));
|
||||
ScopeSpec found = collectionManager.getScope(scopeName);
|
||||
assertEquals(scopeSpec, found);
|
||||
} catch (CouchbaseException e) {
|
||||
if (!e.toString().contains("already exists")) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
waitUntilCondition(() -> scopeExists(collectionManager, scopeName));
|
||||
ScopeSpec found = collectionManager.getScope(scopeName);
|
||||
assertEquals(scopeSpec, found);
|
||||
|
||||
collectionManager.createCollection(collSpec);
|
||||
try {
|
||||
collectionManager.createCollection(collSpec);
|
||||
} catch (CouchbaseException e) {
|
||||
if (!e.toString().contains("already exists")) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
waitUntilCondition(() -> collectionExists(collectionManager, collSpec));
|
||||
waitUntilCondition(
|
||||
() -> collectionReady(cluster.bucket(config().bucketname()).scope(scopeName).collection(collectionName)));
|
||||
@@ -258,6 +272,7 @@ public class JavaIntegrationTests extends ClusterAwareIntegrationTests {
|
||||
String collectionName) {
|
||||
CreatePrimaryQueryIndexOptions options = CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions();
|
||||
options.timeout(Duration.ofSeconds(300));
|
||||
options.ignoreIfExists(true);
|
||||
final CreatePrimaryQueryIndexOptions.Built builtOpts = options.build();
|
||||
final String indexName = builtOpts.indexName().orElse(null);
|
||||
|
||||
@@ -335,7 +350,6 @@ public class JavaIntegrationTests extends ClusterAwareIntegrationTests {
|
||||
break;
|
||||
} catch (CouchbaseException | IllegalStateException ex) {
|
||||
// this is a pretty dirty hack to avoid a race where we don't know if the index is ready yet
|
||||
System.out.println("createFtsCollectionIndex: " + i + " " + ex);
|
||||
if (i < (maxTries - 1) && (ex.getMessage().contains("no planPIndexes for indexName")
|
||||
|| ex.getMessage().contains("pindex_consistency mismatched partition")
|
||||
|| ex.getMessage().contains("pindex not available"))) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.util;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.*;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import okhttp3.Credentials;
|
||||
import okhttp3.FormBody;
|
||||
@@ -43,6 +43,7 @@ public class UnmanagedTestCluster extends TestCluster {
|
||||
private final String adminPassword;
|
||||
private final int numReplicas;
|
||||
private volatile String bucketname;
|
||||
private long startTime = System.currentTimeMillis();
|
||||
|
||||
UnmanagedTestCluster(final Properties properties) {
|
||||
seedHost = properties.getProperty("cluster.unmanaged.seed").split(":")[0];
|
||||
@@ -69,8 +70,9 @@ public class UnmanagedTestCluster extends TestCluster {
|
||||
.build())
|
||||
.execute();
|
||||
|
||||
if (postResponse.code() != 202) {
|
||||
throw new Exception("Could not create bucket: " + postResponse + ", Reason: " + postResponse.body().string());
|
||||
String reason = postResponse.body().string();
|
||||
if (postResponse.code() != 202 && !(reason.contains("Bucket with given name already exists"))) {
|
||||
throw new Exception("Could not create bucket: " + postResponse + ", Reason: " + reason);
|
||||
}
|
||||
|
||||
Response getResponse = httpClient
|
||||
@@ -140,10 +142,13 @@ public class UnmanagedTestCluster extends TestCluster {
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
httpClient
|
||||
.newCall(new Request.Builder().header("Authorization", Credentials.basic(adminUsername, adminPassword))
|
||||
.url("http://" + seedHost + ":" + seedPort + "/pools/default/buckets/" + bucketname).delete().build())
|
||||
.execute();
|
||||
if (!bucketname.equals("my_bucket")) {
|
||||
httpClient
|
||||
.newCall(new Request.Builder().header("Authorization", Credentials.basic(adminUsername, adminPassword))
|
||||
.url("http://" + seedHost + ":" + seedPort + "/pools/default/buckets/" + bucketname).delete().build())
|
||||
.execute();
|
||||
}
|
||||
System.out.println("elapsed: " + (System.currentTimeMillis() - startTime));
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,11 @@
|
||||
- log details of the detection of placeholders in N1QL inline queries
|
||||
- log additional debug info during automatic index creation
|
||||
-->
|
||||
|
||||
<looger name="org.springframework.data.couchbase.core" level="debug"/>"
|
||||
<logger name="org.springframework.data.couchbase.repository.query" level="debug"/>
|
||||
<logger name="org.springframework.data.couchbase.repository.query.SpatialViewQueryCreator" level="trace"/>
|
||||
<logger name="org.springframework.data.couchbase.repository.query.StringN1qlBasedQuery" level="trace"/>
|
||||
<logger name="org.springframework.data.couchbase.repository.support.IndexManager" level="debug"/>
|
||||
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
||||
Reference in New Issue
Block a user