From 1a3546938841ca934c7a0b92ccb1d10061aa979d Mon Sep 17 00:00:00 2001 From: mikereiche Date: Thu, 11 May 2023 15:04:50 -0700 Subject: [PATCH 1/2] Add RangeScan to Template. Closes #1599. --- .../couchbase/core/CouchbaseTemplate.java | 4 + .../core/ExecutableRangeScanOperation.java | 248 ++++++++++++++++++ .../ExecutableRangeScanOperationSupport.java | 166 ++++++++++++ .../core/FluentCouchbaseOperations.java | 3 +- .../core/ReactiveCouchbaseTemplate.java | 6 + .../ReactiveFluentCouchbaseOperations.java | 3 +- .../core/ReactiveRangeScanOperation.java | 248 ++++++++++++++++++ .../ReactiveRangeScanOperationSupport.java | 236 +++++++++++++++++ .../convert/CouchbaseCustomConversions.java | 1 + .../couchbase/core/query/OptionsBuilder.java | 23 ++ .../core/support/ConsistentWith.java | 29 ++ .../data/couchbase/core/support/IdsOnly.java | 27 ++ .../core/support/WithBatchByteLimit.java | 27 ++ .../core/support/WithBatchItemLimit.java | 27 ++ .../couchbase/core/support/WithLimit.java | 27 ++ .../couchbase/core/support/WithSampling.java | 29 ++ .../core/support/WithScanOptions.java | 29 ++ .../couchbase/core/support/WithScanSort.java | 29 ++ .../data/couchbase/core/support/WithSeed.java | 27 ++ ...hbaseTemplateKeyValueIntegrationTests.java | 137 +++++++++- 20 files changed, 1316 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperation.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperationSupport.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperation.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperationSupport.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/ConsistentWith.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/IdsOnly.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithBatchByteLimit.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithBatchItemLimit.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithLimit.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithSampling.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithScanOptions.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithScanSort.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithSeed.java diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index 4bca6e330..74ce3a99d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -158,6 +158,10 @@ public ExecutableRemoveByQuery removeByQuery(Class domainType) { return new ExecutableRemoveByQueryOperationSupport(this).removeByQuery(domainType); } + @Override + public ExecutableRangeScan rangeScan(Class domainType) { + return new ExecutableRangeScanOperationSupport(this).rangeScan(domainType); + } @Override public String getBucketName() { return clientFactory.getBucket().name(); diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperation.java new file mode 100644 index 000000000..576755ca9 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperation.java @@ -0,0 +1,248 @@ +/* + * 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. + * 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; + +import java.util.stream.Stream; + +import org.springframework.data.couchbase.core.support.ConsistentWith; +import org.springframework.data.couchbase.core.support.InCollection; +import org.springframework.data.couchbase.core.support.InScope; +import org.springframework.data.couchbase.core.support.WithBatchByteLimit; +import org.springframework.data.couchbase.core.support.WithBatchItemLimit; +import org.springframework.data.couchbase.core.support.IdsOnly; +import org.springframework.data.couchbase.core.support.WithLimit; +import org.springframework.data.couchbase.core.support.WithSampling; +import org.springframework.data.couchbase.core.support.WithScanOptions; +import org.springframework.data.couchbase.core.support.WithScanSort; + +import com.couchbase.client.java.kv.MutationState; +import com.couchbase.client.java.kv.ScanOptions; +import com.couchbase.client.java.kv.ScanSort; +import org.springframework.data.couchbase.core.support.WithSeed; + +/** + * Get Operations + * + * @author Michael Reiche + */ +public interface ExecutableRangeScanOperation { + + /** + * Loads a document from a bucket. + * + * @param domainType the entity type to use for the results. + */ + ExecutableRangeScan rangeScan(Class domainType); + + /** + * Terminating operations invoking the actual execution. + * + * @param the entity type to use for the results. + */ + interface TerminatingRangeScan /*extends OneAndAllId*/ { + + /** + * Range Scan + * + * @param upper + * @param lower + * @return the list of found entities. + */ + Stream rangeScan(String lower, String upper); + + /** + * Range Scan Ids + * + * @param upper + * @param lower + * @return the list of found entities. + */ + Stream rangeScanIds(String lower, String upper); + + } + + /** + * Fluent method to specify options. + * + * @param the entity type to use for the results. + */ + interface RangeScanWithOptions extends TerminatingRangeScan, WithScanOptions { + /** + * Fluent method to specify options to use for execution + * + * @param options options to use for execution + */ + @Override + TerminatingRangeScan withOptions(ScanOptions options); + } + + /** + * Fluent method to specify the collection. + * + * @param the entity type to use for the results. + */ + interface RangeScanInCollection extends RangeScanWithOptions, InCollection { + /** + * With a different collection + * + * @param collection the collection to use. + */ + @Override + RangeScanWithOptions inCollection(String collection); + } + + /** + * Fluent method to specify the scope. + * + * @param the entity type to use for the results. + */ + interface RangeScanInScope extends RangeScanInCollection, InScope { + /** + * With a different scope + * + * @param scope the scope to use. + */ + @Override + RangeScanInCollection inScope(String scope); + } + + interface RangeScanWithSampling extends RangeScanInScope, WithSampling { + /** + * sampling + * + * @param isSampling + */ + @Override + RangeScanInScope withSampling(Boolean isSampling); + } + + interface RangeScanWithSort extends RangeScanWithSampling, WithScanSort { + /** + * sort + * + * @param sort + */ + @Override + RangeScanWithSampling withSort(ScanSort sort); + } + + /** + * Fluent method to specify scan consistency. Scan consistency may also come from an annotation. + * + * @param the entity type to use for the results. + */ + interface RangeScanConsistentWith extends RangeScanWithSort, ConsistentWith { + + /** + * Allows to override the default scan consistency. + * + * @param mutationState the custom scan consistency to use for this query. + */ + @Override + RangeScanWithSort consistentWith(MutationState mutationState); + } + + /** + * Fluent method to specify a return type different than the the entity type to use for the results. + * + * @param the entity type to use for the results. + */ + interface RangeScanWithProjection extends RangeScanConsistentWith { + + /** + * Define the target type fields should be mapped to.
+ * 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 ExecutableFindByQueryOperation.FindByQueryWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + RangeScanConsistentWith as(Class returnType); + } + + interface RangeScanIdsOnly extends RangeScanWithProjection, IdsOnly { + + /** + * determines if result are just ids or ids plus contents + * + * @param idsOnly must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanWithProjection idsOnly(Boolean idsOnly); + } + + interface RangeScanWithLimit extends RangeScanIdsOnly, WithLimit { + + /** + * determines if result are just ids or ids plus contents + * + * @param limit must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanIdsOnly withLimit(Long limit); + } + + interface RangeScanWithSeed extends RangeScanWithLimit, WithSeed { + + /** + * determines if result are just ids or ids plus contents + * + * @param seed must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanWithLimit withSeed(Long seed); + } + + interface RangeScanWithBatchItemLimit extends RangeScanWithSeed, WithBatchItemLimit { + + /** + * determines if result are just ids or ids plus contents + * + * @param batchByteLimit must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanWithSeed withBatchItemLimit(Integer batchByteLimit); + } + + interface RangeScanWithBatchByteLimit extends RangeScanWithBatchItemLimit, WithBatchByteLimit { + + /** + * determines if result are just ids or ids plus contents + * + * @param batchByteLimit must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanWithBatchByteLimit withBatchByteLimit(Integer batchByteLimit); + } + + /** + * Provides methods for constructing query operations in a fluent way. + * + * @param the entity type to use for the results + */ + interface ExecutableRangeScan extends RangeScanWithBatchByteLimit {} + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperationSupport.java new file mode 100644 index 000000000..3e3438980 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperationSupport.java @@ -0,0 +1,166 @@ +/* + * Copyright 2012-2022 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; + +import java.util.stream.Stream; + +import org.springframework.data.couchbase.core.ReactiveRangeScanOperationSupport.ReactiveRangeScanSupport; +import org.springframework.data.couchbase.core.query.OptionsBuilder; +import org.springframework.util.Assert; + +import com.couchbase.client.java.kv.MutationState; +import com.couchbase.client.java.kv.ScanOptions; +import com.couchbase.client.java.kv.ScanSort; + +public class ExecutableRangeScanOperationSupport implements ExecutableRangeScanOperation { + + private final CouchbaseTemplate template; + + ExecutableRangeScanOperationSupport(CouchbaseTemplate template) { + this.template = template; + } + + @Override + public ExecutableRangeScan rangeScan(Class domainType) { + return new ExecutableRangeScanSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType), + OptionsBuilder.getCollectionFrom(domainType), null, null, null, null, null, null, null, null, null); + } + + static class ExecutableRangeScanSupport implements ExecutableRangeScan { + + private final CouchbaseTemplate template; + private final Class domainType; + private final String scope; + private final String collection; + private final ScanOptions options; + private final Boolean isSamplingScan; + private final ScanSort sort; + private final MutationState mutationState; + private final Boolean withContent; + private final Long limit; + private final Long seed; + private final Integer batchItemLimit; + private final Integer batchByteLimit; + private final ReactiveRangeScanSupport reactiveSupport; + + ExecutableRangeScanSupport(CouchbaseTemplate template, Class domainType, String scope, String collection, + ScanOptions options, Boolean isSamplingScan, ScanSort sort, MutationState mutationState, Boolean withContent, + Long seed, Long limit, Integer batchItemLimit, Integer batchByteLimit) { + this.template = template; + this.domainType = domainType; + this.scope = scope; + this.collection = collection; + this.options = options; + this.isSamplingScan = isSamplingScan; + this.sort = sort; + this.mutationState = mutationState; + this.withContent = withContent; + this.limit = limit; + this.seed = seed; + this.batchItemLimit = batchItemLimit; + this.batchByteLimit = batchByteLimit; + this.reactiveSupport = new ReactiveRangeScanSupport<>(template.reactive(), domainType, scope, collection, options, + isSamplingScan, sort, mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit, + new NonReactiveSupportWrapper(template.support())); + } + + @Override + public TerminatingRangeScan withOptions(final ScanOptions options) { + Assert.notNull(options, "Options must not be null."); + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanWithOptions inCollection(final String collection) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, + collection != null ? collection : this.collection, options, isSamplingScan, sort, mutationState, withContent, + limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanInCollection inScope(final String scope) { + return new ExecutableRangeScanSupport<>(template, domainType, scope != null ? scope : this.scope, collection, + options, isSamplingScan, sort, mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanInScope withSampling(Boolean isSamplingScan) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanWithSampling withSort(ScanSort sort) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanWithSort consistentWith(MutationState mutationState) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanConsistentWith as(Class returnType) { + return new ExecutableRangeScanSupport<>(template, returnType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanWithProjection idsOnly(Boolean withContent) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanIdsOnly withLimit(Long limit) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanWithLimit withSeed(Long seed) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanWithSeed withBatchItemLimit(Integer batchItemLimit) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public RangeScanWithBatchByteLimit withBatchByteLimit(Integer batchByteLimit) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + } + + @Override + public Stream rangeScan(String lower, String upper) { + return reactiveSupport.rangeScan(lower, upper).toStream(); + } + + @Override + public Stream rangeScanIds(String lower, String upper) { + return reactiveSupport.rangeScanIds(lower, upper).toStream(); + } + + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/FluentCouchbaseOperations.java b/src/main/java/org/springframework/data/couchbase/core/FluentCouchbaseOperations.java index eee5dc0bd..701588941 100644 --- a/src/main/java/org/springframework/data/couchbase/core/FluentCouchbaseOperations.java +++ b/src/main/java/org/springframework/data/couchbase/core/FluentCouchbaseOperations.java @@ -22,4 +22,5 @@ public interface FluentCouchbaseOperations extends ExecutableUpsertByIdOperation, ExecutableInsertByIdOperation, ExecutableReplaceByIdOperation, ExecutableFindByIdOperation, ExecutableFindFromReplicasByIdOperation, ExecutableFindByQueryOperation, ExecutableFindByAnalyticsOperation, ExecutableExistsByIdOperation, - ExecutableRemoveByIdOperation, ExecutableRemoveByQueryOperation, ExecutableMutateInByIdOperation {} + ExecutableRemoveByIdOperation, ExecutableRemoveByQueryOperation, ExecutableRangeScanOperation, + ExecutableRangeScanOperation {} diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java index 37a40120e..55852d988 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java @@ -190,6 +190,12 @@ public ReactiveMutateInById mutateInById(Class domainType) { return new ReactiveMutateInByIdOperationSupport(this).mutateInById(domainType); } + @Override + public ReactiveRangeScan rangeScan(Class domainType) { + return new ReactiveRangeScanOperationSupport(this).rangeScan(domainType); + } + + @Override public String getBucketName() { return clientFactory.getBucket().name(); diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFluentCouchbaseOperations.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFluentCouchbaseOperations.java index d7652642c..76ed4072d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFluentCouchbaseOperations.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFluentCouchbaseOperations.java @@ -22,4 +22,5 @@ public interface ReactiveFluentCouchbaseOperations extends ReactiveUpsertByIdOperation, ReactiveInsertByIdOperation, ReactiveReplaceByIdOperation, ReactiveFindByIdOperation, ReactiveExistsByIdOperation, ReactiveFindByAnalyticsOperation, ReactiveFindFromReplicasByIdOperation, ReactiveFindByQueryOperation, - ReactiveRemoveByIdOperation, ReactiveRemoveByQueryOperation, ReactiveMutateInByIdOperation {} + ReactiveRemoveByIdOperation, ReactiveRemoveByQueryOperation, ReactiveRangeScanOperation, + ReactiveRangeScanOperation {} diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperation.java new file mode 100644 index 000000000..fd6263832 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperation.java @@ -0,0 +1,248 @@ +/* + * 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. + * 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; + +import org.springframework.data.couchbase.core.support.WithLimit; +import org.springframework.data.couchbase.core.support.WithSampling; +import org.springframework.data.couchbase.core.support.WithSeed; +import reactor.core.publisher.Flux; + +import org.springframework.data.couchbase.core.support.ConsistentWith; +import org.springframework.data.couchbase.core.support.InCollection; +import org.springframework.data.couchbase.core.support.InScope; +import org.springframework.data.couchbase.core.support.WithBatchByteLimit; +import org.springframework.data.couchbase.core.support.WithBatchItemLimit; +import org.springframework.data.couchbase.core.support.IdsOnly; +import org.springframework.data.couchbase.core.support.WithScanOptions; +import org.springframework.data.couchbase.core.support.WithScanSort; + +import com.couchbase.client.java.kv.MutationState; +import com.couchbase.client.java.kv.ScanOptions; +import com.couchbase.client.java.kv.ScanSort; + +/** + * Get Operations + * + * @author Christoph Strobl + * @since 2.0 + */ +public interface ReactiveRangeScanOperation { + + /** + * Loads a document from a bucket. + * + * @param domainType the entity type to use for the results. + */ + ReactiveRangeScan rangeScan(Class domainType); + + /** + * Terminating operations invoking the actual execution. + * + * @param the entity type to use for the results. + */ + interface TerminatingRangeScan /*extends OneAndAllId*/ { + + /** + * Finds a list of documents based on the given IDs. + * + * @param lower the lower bound + * @param upper the upper bound + * @return the list of found entities. + */ + Flux rangeScan(String lower, String upper); + + /** + * Finds a list of documents based on the given IDs. + * + * @param lower the lower bound + * @param upper the upper bound + * @return the list of found entities. + */ + Flux rangeScanIds(String lower, String upper); + } + + /** + * Fluent method to specify options. + * + * @param the entity type to use for the results. + */ + interface RangeScanWithOptions extends TerminatingRangeScan, WithScanOptions { + /** + * Fluent method to specify options to use for execution + * + * @param options options to use for execution + */ + @Override + TerminatingRangeScan withOptions(ScanOptions options); + } + + /** + * Fluent method to specify the collection. + * + * @param the entity type to use for the results. + */ + interface RangeScanInCollection extends RangeScanWithOptions, InCollection { + /** + * With a different collection + * + * @param collection the collection to use. + */ + @Override + RangeScanWithOptions inCollection(String collection); + } + + /** + * Fluent method to specify the scope. + * + * @param the entity type to use for the results. + */ + interface RangeScanInScope extends RangeScanInCollection, InScope { + /** + * With a different scope + * + * @param scope the scope to use. + */ + @Override + RangeScanInCollection inScope(String scope); + } + + interface RangeScanWithSampling extends RangeScanInScope, WithSampling { + /** + * sampling + * + * @param isSampling + */ + @Override + RangeScanInScope withSampling(Boolean isSampling); + } + + interface RangeScanWithSort extends RangeScanWithSampling, WithScanSort { + /** + * sort + * + * @param sort + */ + @Override + RangeScanWithSampling withSort(ScanSort sort); + } + + /** + * Fluent method to specify scan consistency. Scan consistency may also come from an annotation. + * + * @param the entity type to use for the results. + */ + interface RangeScanConsistentWith extends RangeScanWithSort, ConsistentWith { + + /** + * Allows to override the default scan consistency. + * + * @param mutationState the custom scan consistency to use for this query. + */ + @Override + RangeScanWithSort consistentWith(MutationState mutationState); + } + + /** + * Fluent method to specify a return type different than the the entity type to use for the results. + * + * @param the entity type to use for the results. + */ + interface RangeScanWithProjection extends RangeScanConsistentWith { + + /** + * Define the target type fields should be mapped to.
+ * 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 ReactiveFindByQueryOperation.FindByQueryWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + RangeScanConsistentWith as(Class returnType); + } + + interface RangeScanIdsOnly extends RangeScanWithProjection, IdsOnly { + + /** + * determines if result are just ids or ids plus contents + * + * @param idsOnly must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanWithProjection idsOnly(Boolean idsOnly); + } + + interface RangeScanWithLimit extends RangeScanIdsOnly, WithLimit { + + /** + * determines if result are just ids or ids plus contents + * + * @param limit must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanIdsOnly withLimit(Long limit); + } + + interface RangeScanWithSeed extends RangeScanWithLimit, WithSeed { + + /** + * determines if result are just ids or ids plus contents + * + * @param seed must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanWithLimit withSeed(Long seed); + } + + interface RangeScanWithBatchItemLimit extends RangeScanWithSeed, WithBatchItemLimit { + + /** + * determines if result are just ids or ids plus contents + * + * @param batchByteLimit must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanWithSeed withBatchItemLimit(Integer batchByteLimit); + } + + interface RangeScanWithBatchByteLimit extends RangeScanWithBatchItemLimit, WithBatchByteLimit { + + /** + * determines if result are just ids or ids plus contents + * + * @param batchByteLimit must not be {@literal null}. + * @return new instance of {@link RangeScanWithProjection}. + * @throws IllegalArgumentException if returnType is {@literal null}. + */ + @Override + RangeScanWithBatchByteLimit withBatchByteLimit(Integer batchByteLimit); + } + + /** + * Provides methods for constructing query operations in a fluent way. + * + * @param the entity type to use for the results + */ + interface ReactiveRangeScan extends RangeScanWithBatchByteLimit {} + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperationSupport.java new file mode 100644 index 000000000..112e9721c --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperationSupport.java @@ -0,0 +1,236 @@ +/* + * Copyright 2012-2022 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; + +import reactor.core.publisher.Flux; + +import java.nio.charset.StandardCharsets; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.couchbase.core.query.OptionsBuilder; +import org.springframework.data.couchbase.core.support.PseudoArgs; +import org.springframework.util.Assert; + +import com.couchbase.client.java.ReactiveCollection; +import com.couchbase.client.java.kv.MutationState; +import com.couchbase.client.java.kv.ScanOptions; +import com.couchbase.client.java.kv.ScanSort; +import com.couchbase.client.java.kv.ScanTerm; +import com.couchbase.client.java.kv.ScanType; + +public class ReactiveRangeScanOperationSupport implements ReactiveRangeScanOperation { + + private final ReactiveCouchbaseTemplate template; + private static final Logger LOG = LoggerFactory.getLogger(ReactiveRangeScanOperationSupport.class); + + ReactiveRangeScanOperationSupport(ReactiveCouchbaseTemplate template) { + this.template = template; + } + + @Override + public ReactiveRangeScan rangeScan(Class domainType) { + return new ReactiveRangeScanSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType), + OptionsBuilder.getCollectionFrom(domainType), null, null, null, null, null, null, null, null, null, + template.support()); + } + + static class ReactiveRangeScanSupport implements ReactiveRangeScan { + + private final ReactiveCouchbaseTemplate template; + private final Class domainType; + private final String scope; + private final String collection; + private final ScanOptions options; + private final Boolean isSamplingScan; + private final ScanSort sort; + private final MutationState mutationState; + private final Boolean idsOnly; + private final Long limit; + private final Long seed; + private final Integer batchItemLimit; + private final Integer batchByteLimit; + private final ReactiveTemplateSupport support; + + ReactiveRangeScanSupport(ReactiveCouchbaseTemplate template, Class domainType, String scope, String collection, + ScanOptions options, Boolean isSamplingScan, ScanSort sort, MutationState mutationState, Boolean idsOnly, + Long limit, Long seed, Integer batchItemLimit, Integer batchByteLimit, ReactiveTemplateSupport support) { + this.template = template; + this.domainType = domainType; + this.scope = scope; + this.collection = collection; + this.isSamplingScan = isSamplingScan; + this.options = options; + this.sort = sort; + this.mutationState = mutationState; + this.idsOnly = idsOnly; + this.limit = limit; + this.seed = seed; + this.batchItemLimit = batchItemLimit; + this.batchByteLimit = batchByteLimit; + this.support = support; + } + + @Override + public TerminatingRangeScan withOptions(final ScanOptions options) { + Assert.notNull(options, "Options must not be null."); + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanWithOptions inCollection(final String collection) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, + collection != null ? collection : this.collection, options, isSamplingScan, sort, mutationState, idsOnly, + limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanInCollection inScope(final String scope) { + return new ReactiveRangeScanSupport<>(template, domainType, scope != null ? scope : this.scope, collection, + options, isSamplingScan, sort, mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanInScope withSampling(Boolean isSamplingScan) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanWithSampling withSort(ScanSort sort) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanWithSort consistentWith(MutationState mutationState) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanConsistentWith as(Class returnType) { + return new ReactiveRangeScanSupport<>(template, returnType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanWithProjection idsOnly(Boolean idsOnly) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanIdsOnly withLimit(Long limit) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanWithLimit withSeed(Long seed) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanWithSeed withBatchItemLimit(Integer batchItemLimit) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public RangeScanWithBatchByteLimit withBatchByteLimit(Integer batchByteLimit) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, + mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + } + + @Override + public Flux rangeScan(String lower, String upper) { + + PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options, domainType); + if (LOG.isDebugEnabled()) { + LOG.debug("rangeScan lower={} upper={} {}", lower, upper, pArgs); + } + ReactiveCollection rc = template.getCouchbaseClientFactory().withScope(pArgs.getScope()) + .getCollection(pArgs.getCollection()).reactive(); + + ScanTerm lowerTerm = ScanTerm.minimum(); + ScanTerm upperTerm = ScanTerm.maximum(); + if (lower != null) { + lowerTerm = ScanTerm.inclusive(lower); + } + if (upper != null) { + upperTerm = ScanTerm.inclusive(upper); + } + + ScanType scanType = isSamplingScan ? ScanType.samplingScan(limit != null ? limit : 2, seed != null ? seed : 0) + : ScanType.rangeScan(lowerTerm, upperTerm); + Flux reactiveEntities = TransactionalSupport.verifyNotInTransaction("rangeScan") + .thenMany(rc.scan(scanType, buildScanOptions(pArgs.getOptions(), idsOnly)) + .flatMap(result -> support.decodeEntity(result.id(), + new String(result.contentAsBytes(), StandardCharsets.UTF_8), result.cas(), domainType, + pArgs.getScope(), pArgs.getCollection(), null, null))); + + return reactiveEntities.onErrorMap(throwable -> { + if (throwable instanceof RuntimeException) { + return template.potentiallyConvertRuntimeException((RuntimeException) throwable); + } else { + return throwable; + } + }); + + } + + @Override + public Flux rangeScanIds(String lower, String upper) { + PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options, domainType); + if (LOG.isDebugEnabled()) { + LOG.debug("rangeScan lower={} upper={} {}", lower, upper, pArgs); + } + ReactiveCollection rc = template.getCouchbaseClientFactory().withScope(pArgs.getScope()) + .getCollection(pArgs.getCollection()).reactive(); + + ScanTerm lowerTerm = ScanTerm.minimum(); + ScanTerm upperTerm = ScanTerm.maximum(); + if (lower != null) { + lowerTerm = ScanTerm.inclusive(lower); + } + if (upper != null) { + upperTerm = ScanTerm.inclusive(upper); + } + + ScanType scanType = isSamplingScan ? ScanType.samplingScan(limit, seed) + : ScanType.rangeScan(lowerTerm, upperTerm); + Flux reactiveEntities = TransactionalSupport.verifyNotInTransaction("rangeScanIds") + .thenMany(rc.scan(scanType, buildScanOptions(pArgs.getOptions(), true)).map(result -> result.id())); + + return reactiveEntities.onErrorMap(throwable -> { + if (throwable instanceof RuntimeException) { + return template.potentiallyConvertRuntimeException((RuntimeException) throwable); + } else { + return throwable; + } + }); + + } + + private ScanOptions buildScanOptions(ScanOptions options, Boolean idsOnly) { + return OptionsBuilder.buildScanOptions(options, sort, idsOnly, mutationState, batchByteLimit, batchItemLimit); + } + } + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java index 84a8762ee..d167452d6 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java @@ -31,6 +31,7 @@ import java.util.Set; import java.util.function.Consumer; +import com.fasterxml.jackson.annotation.JsonValue; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.core.convert.converter.GenericConverter; diff --git a/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java b/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java index e7f297bc2..ddb93ecff 100644 --- a/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java +++ b/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java @@ -51,11 +51,14 @@ import com.couchbase.client.java.json.JsonObject; import com.couchbase.client.java.kv.ExistsOptions; import com.couchbase.client.java.kv.InsertOptions; +import com.couchbase.client.java.kv.MutationState; import com.couchbase.client.java.kv.PersistTo; import com.couchbase.client.java.kv.MutateInOptions; import com.couchbase.client.java.kv.RemoveOptions; import com.couchbase.client.java.kv.ReplaceOptions; import com.couchbase.client.java.kv.ReplicateTo; +import com.couchbase.client.java.kv.ScanOptions; +import com.couchbase.client.java.kv.ScanSort; import com.couchbase.client.java.kv.UpsertOptions; import com.couchbase.client.java.query.QueryOptions; import com.couchbase.client.java.query.QueryScanConsistency; @@ -545,4 +548,24 @@ public static String annotationString(Class annotation return annotationString(annotation, "value", defaultValue, elements); } + public static ScanOptions buildScanOptions(ScanOptions options, ScanSort sort, Boolean idsOnly, + MutationState mutationState, Integer batchByteLimit, Integer batchItemLimit) { + options = options != null ? options : ScanOptions.scanOptions(); + if (sort != null) { + options.sort(sort); + } + if (idsOnly != null) { + options.idsOnly(idsOnly); + } + if (mutationState != null) { + options.consistentWith(mutationState); + } + if (batchByteLimit != null) { + options.batchByteLimit(batchByteLimit); + } + if (batchItemLimit != null) { + options.batchItemLimit(batchItemLimit); + } + return options; + } } diff --git a/src/main/java/org/springframework/data/couchbase/core/support/ConsistentWith.java b/src/main/java/org/springframework/data/couchbase/core/support/ConsistentWith.java new file mode 100644 index 000000000..c1ea65cae --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/ConsistentWith.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +import com.couchbase.client.java.kv.MutationState; + +/** + * A common interface for those that support withOptions() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface ConsistentWith { + Object consistentWith(MutationState mutationState); + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/IdsOnly.java b/src/main/java/org/springframework/data/couchbase/core/support/IdsOnly.java new file mode 100644 index 000000000..443aafc3d --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/IdsOnly.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +/** + * A common interface for those that support withContent() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface IdsOnly { + Object idsOnly(Boolean withContent); + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithBatchByteLimit.java b/src/main/java/org/springframework/data/couchbase/core/support/WithBatchByteLimit.java new file mode 100644 index 000000000..adde3ca95 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithBatchByteLimit.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +/** + * A common interface for those that support withBatchByteLimit() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface WithBatchByteLimit { + Object withBatchByteLimit(Integer batchByteLimit); + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithBatchItemLimit.java b/src/main/java/org/springframework/data/couchbase/core/support/WithBatchItemLimit.java new file mode 100644 index 000000000..7d1b5ba8b --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithBatchItemLimit.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +/** + * A common interface for those that support withBatchItemLimit() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface WithBatchItemLimit { + Object withBatchItemLimit(Integer batchItemLimit); + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithLimit.java b/src/main/java/org/springframework/data/couchbase/core/support/WithLimit.java new file mode 100644 index 000000000..0fc2a587d --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithLimit.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +/** + * A common interface for those that support withLimit() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface WithLimit { + Object withLimit(Long limit); + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithSampling.java b/src/main/java/org/springframework/data/couchbase/core/support/WithSampling.java new file mode 100644 index 000000000..5c608da36 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithSampling.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +import com.couchbase.client.java.kv.ScanSort; + +/** + * A common interface for those that support withSampling() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface WithSampling { + Object withSampling(Boolean isSampling); + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithScanOptions.java b/src/main/java/org/springframework/data/couchbase/core/support/WithScanOptions.java new file mode 100644 index 000000000..3d48de696 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithScanOptions.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +import com.couchbase.client.java.kv.ScanOptions; + +/** + * A common interface for those that support withOptions() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface WithScanOptions { + Object withOptions(ScanOptions expiry); + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithScanSort.java b/src/main/java/org/springframework/data/couchbase/core/support/WithScanSort.java new file mode 100644 index 000000000..0c97ca1e8 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithScanSort.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +import com.couchbase.client.java.kv.ScanSort; + +/** + * A common interface for those that support withOptions() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface WithScanSort { + Object withSort(ScanSort expiry); + +} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithSeed.java b/src/main/java/org/springframework/data/couchbase/core/support/WithSeed.java new file mode 100644 index 000000000..debe51fc4 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithSeed.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020-2021 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core.support; + +/** + * A common interface for those that support withSeed() + * + * @author Michael Reiche + * @param - the entity class + */ +public interface WithSeed { + Object withSeed(Long seed); + +} diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java index e7915bba5..eaa3f3f9a 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java @@ -27,7 +27,14 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.time.Duration; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Stream; import com.couchbase.client.core.error.TimeoutException; import com.couchbase.client.core.msg.kv.DurabilityLevel; @@ -41,8 +48,20 @@ import org.springframework.data.couchbase.core.ExecutableFindByIdOperation.ExecutableFindById; import org.springframework.data.couchbase.core.ExecutableRemoveByIdOperation.ExecutableRemoveById; import org.springframework.data.couchbase.core.ExecutableReplaceByIdOperation.ExecutableReplaceById; -import org.springframework.data.couchbase.core.support.*; -import org.springframework.data.couchbase.domain.*; +import org.springframework.data.couchbase.core.support.OneAndAllEntity; +import org.springframework.data.couchbase.core.support.OneAndAllId; +import org.springframework.data.couchbase.core.support.WithDurability; +import org.springframework.data.couchbase.core.support.WithExpiry; +import org.springframework.data.couchbase.domain.Address; +import org.springframework.data.couchbase.domain.Config; +import org.springframework.data.couchbase.domain.NaiveAuditorAware; +import org.springframework.data.couchbase.domain.PersonValue; +import org.springframework.data.couchbase.domain.Submission; +import org.springframework.data.couchbase.domain.User; +import org.springframework.data.couchbase.domain.UserAnnotated; +import org.springframework.data.couchbase.domain.UserAnnotated2; +import org.springframework.data.couchbase.domain.UserAnnotated3; +import org.springframework.data.couchbase.domain.UserSubmission; import org.springframework.data.couchbase.util.ClusterType; import org.springframework.data.couchbase.util.IgnoreWhen; import org.springframework.data.couchbase.util.JavaIntegrationTests; @@ -50,8 +69,12 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import com.couchbase.client.core.error.CouchbaseException; +import com.couchbase.client.core.msg.kv.MutationToken; +import com.couchbase.client.java.json.JsonObject; +import com.couchbase.client.java.kv.MutationState; import com.couchbase.client.java.kv.PersistTo; import com.couchbase.client.java.kv.ReplicateTo; +import com.couchbase.client.java.kv.ScanSort; import com.couchbase.client.java.query.QueryOptions; import com.couchbase.client.java.query.QueryScanConsistency; @@ -1051,11 +1074,6 @@ void insertById() { assertEquals(user, inserted); User found = couchbaseTemplate.findById(User.class).one(user.getId()); assertEquals(inserted, found); - System.err.println("inserted: "+inserted); - System.err.println("found: "+found); - System.err.println("found:jsonNode "+found.jsonNode.toPrettyString()); - System.err.println("found:jsonObject "+found.jsonObject.toString()); - System.err.println("found:jsonArray "+found.jsonArray.toString()); assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user)); couchbaseTemplate.removeById(User.class).one(user.getId()); } @@ -1209,6 +1227,109 @@ void saveAndFindImmutableById() { couchbaseTemplate.removeById(PersonValue.class).one(replaced.getId()); } + @Test + void rangeScan() { + String id = "A"; + String lower = null; + String upper = null; + for (int i = 0; i < 10; i++) { + if (lower == null) { + lower = "" + i; + } + User inserted = couchbaseTemplate.insertById(User.class).one(new User("" + i, "fn_" + i, "ln_" + i)); + upper = "" + i; + } + MutationToken mt = couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection() + .upsert(id, JsonObject.create().put("id", id)).mutationToken().get(); + Stream users = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)).withSort(ScanSort.ASCENDING).rangeScan(lower, + upper); + for (User u : users.toList()) { + System.err.print(u); + System.err.println(","); + assertTrue(u.getId().compareTo(lower) >= 0 && u.getId().compareTo(upper) <= 0); + couchbaseTemplate.removeById(User.class).one(u.getId()); + } + couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection().remove(id); + } + + @Test + void rangeScanId() { + String id = "A"; + String lower = null; + String upper = null; + for (int i = 0; i < 10; i++) { + if (lower == null) { + lower = "" + i; + } + User inserted = couchbaseTemplate.insertById(User.class).one(new User("" + i, "fn_" + i, "ln_" + i)); + upper = "" + i; + } + MutationToken mt = couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection() + .upsert(id, JsonObject.create().put("id", id)).mutationToken().get(); + Stream userIds = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)) + .withSort(ScanSort.ASCENDING).rangeScanIds(lower, upper); + for (String userId : userIds.toList()) { + System.err.print(userId); + System.err.println(","); + assertTrue(userId.compareTo(lower) >= 0 && userId.compareTo(upper) <= 0); + couchbaseTemplate.removeById(User.class).one(userId); + } + couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection().remove(id); + + } + + @Test + void sampleScan() { + String id = "A"; + String lower = null; + String upper = null; + for (int i = 0; i < 10; i++) { + if (lower == null) { + lower = "" + i; + } + User inserted = couchbaseTemplate.insertById(User.class).one(new User("" + i, "fn_" + i, "ln_" + i)); + upper = "" + i; + } + MutationToken mt = couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection() + .upsert(id, JsonObject.create().put("id", id)).mutationToken().get(); + Stream users = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)).withSort(ScanSort.ASCENDING).withSampling(true).rangeScan(lower, + upper); + for (User u : users.toList()) { + System.err.print(u); + System.err.println(","); + assertTrue(u.getId().compareTo(lower) >= 0 && u.getId().compareTo(upper) <= 0); + couchbaseTemplate.removeById(User.class).one(u.getId()); + } + couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection().remove(id); + } + + @Test + void sampleScanId() { + String id = "A"; + String lower = null; + String upper = null; + for (int i = 0; i < 10; i++) { + if (lower == null) { + lower = "" + i; + } + User inserted = couchbaseTemplate.insertById(User.class).one(new User("" + i, "fn_" + i, "ln_" + i)); + upper = "" + i; + } + MutationToken mt = couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection() + .upsert(id, JsonObject.create().put("id", id)).mutationToken().get(); + Stream userIds = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)) + .withSort(ScanSort.ASCENDING).rangeScanIds(lower, upper); + for (String userId : userIds.toList()) { + System.err.print(userId); + System.err.println(","); + assertTrue(userId.compareTo(lower) >= 0 && userId.compareTo(upper) <= 0); + couchbaseTemplate.removeById(User.class).one(userId); + } + couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection().remove(id); + + } + + private void sleepSecs(int i) { try { Thread.sleep(i * 1000); From 53063e2a1fb04e0a37cb7cb83870328e993bc9d1 Mon Sep 17 00:00:00 2001 From: mikereiche Date: Thu, 11 May 2023 18:33:10 -0700 Subject: [PATCH 2/2] Add RangeScan support. Closes #1599. --- .../couchbase/core/CouchbaseTemplate.java | 1 + .../core/ExecutableRangeScanOperation.java | 85 ++++------- .../ExecutableRangeScanOperationSupport.java | 95 +++++------- .../core/FluentCouchbaseOperations.java | 2 +- .../core/ReactiveCouchbaseTemplate.java | 1 - .../ReactiveFluentCouchbaseOperations.java | 2 +- .../core/ReactiveRangeScanOperation.java | 86 ++++------- .../ReactiveRangeScanOperationSupport.java | 136 +++++++++--------- .../convert/CouchbaseCustomConversions.java | 1 - .../couchbase/core/query/OptionsBuilder.java | 15 +- .../core/support/ConsistentWith.java | 2 +- .../data/couchbase/core/support/IdsOnly.java | 27 ---- .../core/support/WithBatchByteLimit.java | 3 +- .../core/support/WithBatchItemLimit.java | 3 +- .../couchbase/core/support/WithLimit.java | 27 ---- .../couchbase/core/support/WithSampling.java | 29 ---- .../core/support/WithScanOptions.java | 3 +- .../couchbase/core/support/WithScanSort.java | 7 +- .../data/couchbase/core/support/WithSeed.java | 27 ---- ...hbaseTemplateKeyValueIntegrationTests.java | 38 +++-- 20 files changed, 189 insertions(+), 401 deletions(-) delete mode 100644 src/main/java/org/springframework/data/couchbase/core/support/IdsOnly.java delete mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithLimit.java delete mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithSampling.java delete mode 100644 src/main/java/org/springframework/data/couchbase/core/support/WithSeed.java diff --git a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java index 74ce3a99d..c0965a6b9 100644 --- a/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/CouchbaseTemplate.java @@ -162,6 +162,7 @@ public ExecutableRemoveByQuery removeByQuery(Class domainType) { public ExecutableRangeScan rangeScan(Class domainType) { return new ExecutableRangeScanOperationSupport(this).rangeScan(domainType); } + @Override public String getBucketName() { return clientFactory.getBucket().name(); diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperation.java index 576755ca9..ed101df6e 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors + * Copyright 2012-2023 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,11 @@ import org.springframework.data.couchbase.core.support.InScope; import org.springframework.data.couchbase.core.support.WithBatchByteLimit; import org.springframework.data.couchbase.core.support.WithBatchItemLimit; -import org.springframework.data.couchbase.core.support.IdsOnly; -import org.springframework.data.couchbase.core.support.WithLimit; -import org.springframework.data.couchbase.core.support.WithSampling; import org.springframework.data.couchbase.core.support.WithScanOptions; import org.springframework.data.couchbase.core.support.WithScanSort; import com.couchbase.client.java.kv.MutationState; import com.couchbase.client.java.kv.ScanOptions; -import com.couchbase.client.java.kv.ScanSort; -import org.springframework.data.couchbase.core.support.WithSeed; /** * Get Operations @@ -68,10 +63,27 @@ interface TerminatingRangeScan /*extends OneAndAllId*/ { * * @param upper * @param lower - * @return the list of found entities. + * @return the list of found keys. */ Stream rangeScanIds(String lower, String upper); + /** + * Range Scan + * + * @param limit + * @param seed + * @return the list of found entities. + */ + Stream samplingScan(Long limit, Long... seed); + + /** + * Range Scan Ids + * + * @param limit + * @param seed + * @return the list of keys + */ + Stream samplingScanIds(Long limit, Long... seed); } /** @@ -119,24 +131,16 @@ interface RangeScanInScope extends RangeScanInCollection, InScope { RangeScanInCollection inScope(String scope); } - interface RangeScanWithSampling extends RangeScanInScope, WithSampling { - /** - * sampling - * - * @param isSampling - */ - @Override - RangeScanInScope withSampling(Boolean isSampling); - } - interface RangeScanWithSort extends RangeScanWithSampling, WithScanSort { + + interface RangeScanWithSort extends RangeScanInScope, WithScanSort { /** * sort * * @param sort */ @Override - RangeScanWithSampling withSort(ScanSort sort); + RangeScanInScope withSort(Object sort); } /** @@ -173,46 +177,7 @@ interface RangeScanWithProjection extends RangeScanConsistentWith { RangeScanConsistentWith as(Class returnType); } - interface RangeScanIdsOnly extends RangeScanWithProjection, IdsOnly { - - /** - * determines if result are just ids or ids plus contents - * - * @param idsOnly must not be {@literal null}. - * @return new instance of {@link RangeScanWithProjection}. - * @throws IllegalArgumentException if returnType is {@literal null}. - */ - @Override - RangeScanWithProjection idsOnly(Boolean idsOnly); - } - - interface RangeScanWithLimit extends RangeScanIdsOnly, WithLimit { - - /** - * determines if result are just ids or ids plus contents - * - * @param limit must not be {@literal null}. - * @return new instance of {@link RangeScanWithProjection}. - * @throws IllegalArgumentException if returnType is {@literal null}. - */ - @Override - RangeScanIdsOnly withLimit(Long limit); - } - - interface RangeScanWithSeed extends RangeScanWithLimit, WithSeed { - - /** - * determines if result are just ids or ids plus contents - * - * @param seed must not be {@literal null}. - * @return new instance of {@link RangeScanWithProjection}. - * @throws IllegalArgumentException if returnType is {@literal null}. - */ - @Override - RangeScanWithLimit withSeed(Long seed); - } - - interface RangeScanWithBatchItemLimit extends RangeScanWithSeed, WithBatchItemLimit { + interface RangeScanWithBatchItemLimit extends RangeScanWithProjection, WithBatchItemLimit { /** * determines if result are just ids or ids plus contents @@ -222,7 +187,7 @@ interface RangeScanWithBatchItemLimit extends RangeScanWithSeed, WithBatch * @throws IllegalArgumentException if returnType is {@literal null}. */ @Override - RangeScanWithSeed withBatchItemLimit(Integer batchByteLimit); + RangeScanWithProjection withBatchItemLimit(Integer batchByteLimit); } interface RangeScanWithBatchByteLimit extends RangeScanWithBatchItemLimit, WithBatchByteLimit { @@ -235,7 +200,7 @@ interface RangeScanWithBatchByteLimit extends RangeScanWithBatchItemLimit, * @throws IllegalArgumentException if returnType is {@literal null}. */ @Override - RangeScanWithBatchByteLimit withBatchByteLimit(Integer batchByteLimit); + RangeScanWithBatchItemLimit withBatchByteLimit(Integer batchByteLimit); } /** diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperationSupport.java index 3e3438980..2e795dec5 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRangeScanOperationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors + * Copyright 2012-2023 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,7 +23,6 @@ import com.couchbase.client.java.kv.MutationState; import com.couchbase.client.java.kv.ScanOptions; -import com.couchbase.client.java.kv.ScanSort; public class ExecutableRangeScanOperationSupport implements ExecutableRangeScanOperation { @@ -36,7 +35,7 @@ public class ExecutableRangeScanOperationSupport implements ExecutableRangeScanO @Override public ExecutableRangeScan rangeScan(Class domainType) { return new ExecutableRangeScanSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType), - OptionsBuilder.getCollectionFrom(domainType), null, null, null, null, null, null, null, null, null); + OptionsBuilder.getCollectionFrom(domainType), null, null, null, null, null); } static class ExecutableRangeScanSupport implements ExecutableRangeScan { @@ -46,121 +45,99 @@ static class ExecutableRangeScanSupport implements ExecutableRangeScan { private final String scope; private final String collection; private final ScanOptions options; - private final Boolean isSamplingScan; - private final ScanSort sort; + private final Object sort; private final MutationState mutationState; - private final Boolean withContent; - private final Long limit; - private final Long seed; private final Integer batchItemLimit; private final Integer batchByteLimit; private final ReactiveRangeScanSupport reactiveSupport; ExecutableRangeScanSupport(CouchbaseTemplate template, Class domainType, String scope, String collection, - ScanOptions options, Boolean isSamplingScan, ScanSort sort, MutationState mutationState, Boolean withContent, - Long seed, Long limit, Integer batchItemLimit, Integer batchByteLimit) { + ScanOptions options, Object sort, MutationState mutationState, + Integer batchItemLimit, Integer batchByteLimit) { this.template = template; this.domainType = domainType; this.scope = scope; this.collection = collection; this.options = options; - this.isSamplingScan = isSamplingScan; this.sort = sort; this.mutationState = mutationState; - this.withContent = withContent; - this.limit = limit; - this.seed = seed; this.batchItemLimit = batchItemLimit; this.batchByteLimit = batchByteLimit; this.reactiveSupport = new ReactiveRangeScanSupport<>(template.reactive(), domainType, scope, collection, options, - isSamplingScan, sort, mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit, + sort, mutationState, batchItemLimit, batchByteLimit, new NonReactiveSupportWrapper(template.support())); } @Override public TerminatingRangeScan withOptions(final ScanOptions options) { Assert.notNull(options, "Options must not be null."); - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit); } @Override public RangeScanWithOptions inCollection(final String collection) { return new ExecutableRangeScanSupport<>(template, domainType, scope, - collection != null ? collection : this.collection, options, isSamplingScan, sort, mutationState, withContent, - limit, seed, batchItemLimit, batchByteLimit); + collection != null ? collection : this.collection, options, sort, mutationState, + batchItemLimit, batchByteLimit); } @Override public RangeScanInCollection inScope(final String scope) { return new ExecutableRangeScanSupport<>(template, domainType, scope != null ? scope : this.scope, collection, - options, isSamplingScan, sort, mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + options, sort, mutationState, batchItemLimit, batchByteLimit); } @Override - public RangeScanInScope withSampling(Boolean isSamplingScan) { - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); - } - - @Override - public RangeScanWithSampling withSort(ScanSort sort) { - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + public RangeScanInScope withSort(Object sort) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit); } @Override public RangeScanWithSort consistentWith(MutationState mutationState) { - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit); } @Override public RangeScanConsistentWith as(Class returnType) { - return new ExecutableRangeScanSupport<>(template, returnType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); - } - - @Override - public RangeScanWithProjection idsOnly(Boolean withContent) { - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); - } - - @Override - public RangeScanIdsOnly withLimit(Long limit) { - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + return new ExecutableRangeScanSupport<>(template, returnType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit); } @Override - public RangeScanWithLimit withSeed(Long seed) { - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + public RangeScanWithProjection withBatchItemLimit(Integer batchItemLimit) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit); } @Override - public RangeScanWithSeed withBatchItemLimit(Integer batchItemLimit) { - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); - } - - @Override - public RangeScanWithBatchByteLimit withBatchByteLimit(Integer batchByteLimit) { - return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, withContent, limit, seed, batchItemLimit, batchByteLimit); + public RangeScanWithBatchItemLimit withBatchByteLimit(Integer batchByteLimit) { + return new ExecutableRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit); } @Override public Stream rangeScan(String lower, String upper) { - return reactiveSupport.rangeScan(lower, upper).toStream(); + return reactiveSupport.rangeScan(lower, upper, false, null, null).toStream(); } @Override public Stream rangeScanIds(String lower, String upper) { - return reactiveSupport.rangeScanIds(lower, upper).toStream(); + return reactiveSupport.rangeScanIds(lower, upper, false, null, null).toStream(); } + @Override + public Stream samplingScan(Long limit, Long... seed) { + return reactiveSupport.sampleScan(limit, seed).toStream(); + } + + @Override + public Stream samplingScanIds(Long limit, Long... seed) { + return reactiveSupport.sampleScanIds(limit, seed).toStream(); + } + } } diff --git a/src/main/java/org/springframework/data/couchbase/core/FluentCouchbaseOperations.java b/src/main/java/org/springframework/data/couchbase/core/FluentCouchbaseOperations.java index 701588941..55b9bd139 100644 --- a/src/main/java/org/springframework/data/couchbase/core/FluentCouchbaseOperations.java +++ b/src/main/java/org/springframework/data/couchbase/core/FluentCouchbaseOperations.java @@ -22,5 +22,5 @@ public interface FluentCouchbaseOperations extends ExecutableUpsertByIdOperation, ExecutableInsertByIdOperation, ExecutableReplaceByIdOperation, ExecutableFindByIdOperation, ExecutableFindFromReplicasByIdOperation, ExecutableFindByQueryOperation, ExecutableFindByAnalyticsOperation, ExecutableExistsByIdOperation, - ExecutableRemoveByIdOperation, ExecutableRemoveByQueryOperation, ExecutableRangeScanOperation, + ExecutableRemoveByIdOperation, ExecutableRemoveByQueryOperation, ExecutableMutateInByIdOperation, ExecutableRangeScanOperation {} diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java index 55852d988..d11245af0 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveCouchbaseTemplate.java @@ -195,7 +195,6 @@ public ReactiveRangeScan rangeScan(Class domainType) { return new ReactiveRangeScanOperationSupport(this).rangeScan(domainType); } - @Override public String getBucketName() { return clientFactory.getBucket().name(); diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFluentCouchbaseOperations.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFluentCouchbaseOperations.java index 76ed4072d..d2394dbc9 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFluentCouchbaseOperations.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFluentCouchbaseOperations.java @@ -22,5 +22,5 @@ public interface ReactiveFluentCouchbaseOperations extends ReactiveUpsertByIdOperation, ReactiveInsertByIdOperation, ReactiveReplaceByIdOperation, ReactiveFindByIdOperation, ReactiveExistsByIdOperation, ReactiveFindByAnalyticsOperation, ReactiveFindFromReplicasByIdOperation, ReactiveFindByQueryOperation, - ReactiveRemoveByIdOperation, ReactiveRemoveByQueryOperation, ReactiveRangeScanOperation, + ReactiveRemoveByIdOperation, ReactiveRemoveByQueryOperation, ReactiveMutateInByIdOperation, ReactiveRangeScanOperation {} diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperation.java index fd6263832..e8ff902bc 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors + * Copyright 2012-2023 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,9 +15,6 @@ */ package org.springframework.data.couchbase.core; -import org.springframework.data.couchbase.core.support.WithLimit; -import org.springframework.data.couchbase.core.support.WithSampling; -import org.springframework.data.couchbase.core.support.WithSeed; import reactor.core.publisher.Flux; import org.springframework.data.couchbase.core.support.ConsistentWith; @@ -25,13 +22,11 @@ import org.springframework.data.couchbase.core.support.InScope; import org.springframework.data.couchbase.core.support.WithBatchByteLimit; import org.springframework.data.couchbase.core.support.WithBatchItemLimit; -import org.springframework.data.couchbase.core.support.IdsOnly; import org.springframework.data.couchbase.core.support.WithScanOptions; import org.springframework.data.couchbase.core.support.WithScanSort; import com.couchbase.client.java.kv.MutationState; import com.couchbase.client.java.kv.ScanOptions; -import com.couchbase.client.java.kv.ScanSort; /** * Get Operations @@ -69,9 +64,27 @@ interface TerminatingRangeScan /*extends OneAndAllId*/ { * * @param lower the lower bound * @param upper the upper bound - * @return the list of found entities. + * @return the list of ids. */ Flux rangeScanIds(String lower, String upper); + + /** + * Finds a list of documents based on the given IDs. + * + * @param limit + * @param seed + * @return the list of found entities. + */ + Flux sampleScan(Long limit, Long... seed); + + /** + * Finds a list of documents based on the given IDs. + * + * @param limit + * @param seed + * @return the list of ids. + */ + Flux sampleScanIds(Long limit, Long... seed); } /** @@ -119,24 +132,14 @@ interface RangeScanInScope extends RangeScanInCollection, InScope { RangeScanInCollection inScope(String scope); } - interface RangeScanWithSampling extends RangeScanInScope, WithSampling { - /** - * sampling - * - * @param isSampling - */ - @Override - RangeScanInScope withSampling(Boolean isSampling); - } - - interface RangeScanWithSort extends RangeScanWithSampling, WithScanSort { + interface RangeScanWithSort extends RangeScanInScope, WithScanSort { /** * sort * * @param sort */ @Override - RangeScanWithSampling withSort(ScanSort sort); + RangeScanInScope withSort(Object sort); } /** @@ -173,46 +176,7 @@ interface RangeScanWithProjection extends RangeScanConsistentWith { RangeScanConsistentWith as(Class returnType); } - interface RangeScanIdsOnly extends RangeScanWithProjection, IdsOnly { - - /** - * determines if result are just ids or ids plus contents - * - * @param idsOnly must not be {@literal null}. - * @return new instance of {@link RangeScanWithProjection}. - * @throws IllegalArgumentException if returnType is {@literal null}. - */ - @Override - RangeScanWithProjection idsOnly(Boolean idsOnly); - } - - interface RangeScanWithLimit extends RangeScanIdsOnly, WithLimit { - - /** - * determines if result are just ids or ids plus contents - * - * @param limit must not be {@literal null}. - * @return new instance of {@link RangeScanWithProjection}. - * @throws IllegalArgumentException if returnType is {@literal null}. - */ - @Override - RangeScanIdsOnly withLimit(Long limit); - } - - interface RangeScanWithSeed extends RangeScanWithLimit, WithSeed { - - /** - * determines if result are just ids or ids plus contents - * - * @param seed must not be {@literal null}. - * @return new instance of {@link RangeScanWithProjection}. - * @throws IllegalArgumentException if returnType is {@literal null}. - */ - @Override - RangeScanWithLimit withSeed(Long seed); - } - - interface RangeScanWithBatchItemLimit extends RangeScanWithSeed, WithBatchItemLimit { + interface RangeScanWithBatchItemLimit extends RangeScanWithProjection, WithBatchItemLimit { /** * determines if result are just ids or ids plus contents @@ -222,7 +186,7 @@ interface RangeScanWithBatchItemLimit extends RangeScanWithSeed, WithBatch * @throws IllegalArgumentException if returnType is {@literal null}. */ @Override - RangeScanWithSeed withBatchItemLimit(Integer batchByteLimit); + RangeScanWithProjection withBatchItemLimit(Integer batchByteLimit); } interface RangeScanWithBatchByteLimit extends RangeScanWithBatchItemLimit, WithBatchByteLimit { @@ -235,7 +199,7 @@ interface RangeScanWithBatchByteLimit extends RangeScanWithBatchItemLimit, * @throws IllegalArgumentException if returnType is {@literal null}. */ @Override - RangeScanWithBatchByteLimit withBatchByteLimit(Integer batchByteLimit); + RangeScanWithBatchItemLimit withBatchByteLimit(Integer batchByteLimit); } /** diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperationSupport.java index 112e9721c..1a13a824b 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRangeScanOperationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 the original author or authors + * Copyright 2012-2023 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. @@ -28,7 +28,6 @@ import com.couchbase.client.java.ReactiveCollection; import com.couchbase.client.java.kv.MutationState; import com.couchbase.client.java.kv.ScanOptions; -import com.couchbase.client.java.kv.ScanSort; import com.couchbase.client.java.kv.ScanTerm; import com.couchbase.client.java.kv.ScanType; @@ -44,7 +43,7 @@ public class ReactiveRangeScanOperationSupport implements ReactiveRangeScanOpera @Override public ReactiveRangeScan rangeScan(Class domainType) { return new ReactiveRangeScanSupport<>(template, domainType, OptionsBuilder.getScopeFrom(domainType), - OptionsBuilder.getCollectionFrom(domainType), null, null, null, null, null, null, null, null, null, + OptionsBuilder.getCollectionFrom(domainType), null, null, null, null, null, template.support()); } @@ -55,30 +54,22 @@ static class ReactiveRangeScanSupport implements ReactiveRangeScan { private final String scope; private final String collection; private final ScanOptions options; - private final Boolean isSamplingScan; - private final ScanSort sort; + private final Object sort; private final MutationState mutationState; - private final Boolean idsOnly; - private final Long limit; - private final Long seed; private final Integer batchItemLimit; private final Integer batchByteLimit; private final ReactiveTemplateSupport support; ReactiveRangeScanSupport(ReactiveCouchbaseTemplate template, Class domainType, String scope, String collection, - ScanOptions options, Boolean isSamplingScan, ScanSort sort, MutationState mutationState, Boolean idsOnly, - Long limit, Long seed, Integer batchItemLimit, Integer batchByteLimit, ReactiveTemplateSupport support) { + ScanOptions options, Object sort, MutationState mutationState, + Integer batchItemLimit, Integer batchByteLimit, ReactiveTemplateSupport support) { this.template = template; this.domainType = domainType; this.scope = scope; this.collection = collection; - this.isSamplingScan = isSamplingScan; this.options = options; this.sort = sort; this.mutationState = mutationState; - this.idsOnly = idsOnly; - this.limit = limit; - this.seed = seed; this.batchItemLimit = batchItemLimit; this.batchByteLimit = batchByteLimit; this.support = support; @@ -87,79 +78,65 @@ static class ReactiveRangeScanSupport implements ReactiveRangeScan { @Override public TerminatingRangeScan withOptions(final ScanOptions options) { Assert.notNull(options, "Options must not be null."); - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit, support); } @Override public RangeScanWithOptions inCollection(final String collection) { return new ReactiveRangeScanSupport<>(template, domainType, scope, - collection != null ? collection : this.collection, options, isSamplingScan, sort, mutationState, idsOnly, - limit, seed, batchItemLimit, batchByteLimit, support); + collection != null ? collection : this.collection, options, sort, mutationState, + batchItemLimit, batchByteLimit, support); } @Override public RangeScanInCollection inScope(final String scope) { return new ReactiveRangeScanSupport<>(template, domainType, scope != null ? scope : this.scope, collection, - options, isSamplingScan, sort, mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + options, sort, mutationState, batchItemLimit, batchByteLimit, support); } @Override - public RangeScanInScope withSampling(Boolean isSamplingScan) { - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); - } - - @Override - public RangeScanWithSampling withSort(ScanSort sort) { - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + public RangeScanInScope withSort(Object sort) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit, support); } @Override public RangeScanWithSort consistentWith(MutationState mutationState) { - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit, support); } @Override public RangeScanConsistentWith as(Class returnType) { - return new ReactiveRangeScanSupport<>(template, returnType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + return new ReactiveRangeScanSupport<>(template, returnType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit, support); } @Override - public RangeScanWithProjection idsOnly(Boolean idsOnly) { - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + public RangeScanWithProjection withBatchItemLimit(Integer batchItemLimit) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit, support); } @Override - public RangeScanIdsOnly withLimit(Long limit) { - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + public RangeScanWithBatchItemLimit withBatchByteLimit(Integer batchByteLimit) { + return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, sort, + mutationState, batchItemLimit, batchByteLimit, support); } @Override - public RangeScanWithLimit withSeed(Long seed) { - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + public Flux rangeScan(String lower, String upper) { + return rangeScan(lower, upper, false, null, null); } @Override - public RangeScanWithSeed withBatchItemLimit(Integer batchItemLimit) { - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); + public Flux sampleScan(Long limit, Long... seed) { + return rangeScan(null, null, true, limit, seed!= null && seed.length > 0 ? seed[0] : null); } - @Override - public RangeScanWithBatchByteLimit withBatchByteLimit(Integer batchByteLimit) { - return new ReactiveRangeScanSupport<>(template, domainType, scope, collection, options, isSamplingScan, sort, - mutationState, idsOnly, limit, seed, batchItemLimit, batchByteLimit, support); - } - @Override - public Flux rangeScan(String lower, String upper) { + Flux rangeScan(String lower, String upper, boolean isSamplingScan, Long limit, Long seed) { PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options, domainType); if (LOG.isDebugEnabled()) { @@ -168,19 +145,23 @@ public Flux rangeScan(String lower, String upper) { ReactiveCollection rc = template.getCouchbaseClientFactory().withScope(pArgs.getScope()) .getCollection(pArgs.getCollection()).reactive(); - ScanTerm lowerTerm = ScanTerm.minimum(); - ScanTerm upperTerm = ScanTerm.maximum(); - if (lower != null) { - lowerTerm = ScanTerm.inclusive(lower); - } - if (upper != null) { - upperTerm = ScanTerm.inclusive(upper); + ScanType scanType = null; + if(isSamplingScan){ + scanType = ScanType.samplingScan(limit, seed != null ? seed : 0); + } else { + ScanTerm lowerTerm = ScanTerm.minimum(); + ScanTerm upperTerm = ScanTerm.maximum(); + if (lower != null) { + lowerTerm = ScanTerm.inclusive(lower); + } + if (upper != null) { + upperTerm = ScanTerm.inclusive(upper); + } + scanType = ScanType.rangeScan(lowerTerm, upperTerm); } - ScanType scanType = isSamplingScan ? ScanType.samplingScan(limit != null ? limit : 2, seed != null ? seed : 0) - : ScanType.rangeScan(lowerTerm, upperTerm); Flux reactiveEntities = TransactionalSupport.verifyNotInTransaction("rangeScan") - .thenMany(rc.scan(scanType, buildScanOptions(pArgs.getOptions(), idsOnly)) + .thenMany(rc.scan(scanType, buildScanOptions(pArgs.getOptions(), false)) .flatMap(result -> support.decodeEntity(result.id(), new String(result.contentAsBytes(), StandardCharsets.UTF_8), result.cas(), domainType, pArgs.getScope(), pArgs.getCollection(), null, null))); @@ -196,7 +177,16 @@ public Flux rangeScan(String lower, String upper) { } @Override - public Flux rangeScanIds(String lower, String upper) { + public Flux rangeScanIds(String upper, String lower) { + return rangeScanIds(upper, lower, false, null, null); + } + + @Override + public Flux sampleScanIds(Long limit, Long... seed) { + return rangeScanIds(null, null, true, limit, seed!= null && seed.length > 0 ? seed[0] : null); + } + + Flux rangeScanIds(String lower, String upper, boolean isSamplingScan, Long limit, Long seed) { PseudoArgs pArgs = new PseudoArgs<>(template, scope, collection, options, domainType); if (LOG.isDebugEnabled()) { LOG.debug("rangeScan lower={} upper={} {}", lower, upper, pArgs); @@ -204,17 +194,21 @@ public Flux rangeScanIds(String lower, String upper) { ReactiveCollection rc = template.getCouchbaseClientFactory().withScope(pArgs.getScope()) .getCollection(pArgs.getCollection()).reactive(); - ScanTerm lowerTerm = ScanTerm.minimum(); - ScanTerm upperTerm = ScanTerm.maximum(); - if (lower != null) { - lowerTerm = ScanTerm.inclusive(lower); - } - if (upper != null) { - upperTerm = ScanTerm.inclusive(upper); + ScanType scanType = null; + if(isSamplingScan){ + scanType = ScanType.samplingScan(limit, seed != null ? seed : 0); + } else { + ScanTerm lowerTerm = ScanTerm.minimum(); + ScanTerm upperTerm = ScanTerm.maximum(); + if (lower != null) { + lowerTerm = ScanTerm.inclusive(lower); + } + if (upper != null) { + upperTerm = ScanTerm.inclusive(upper); + } + scanType = ScanType.rangeScan(lowerTerm, upperTerm); } - ScanType scanType = isSamplingScan ? ScanType.samplingScan(limit, seed) - : ScanType.rangeScan(lowerTerm, upperTerm); Flux reactiveEntities = TransactionalSupport.verifyNotInTransaction("rangeScanIds") .thenMany(rc.scan(scanType, buildScanOptions(pArgs.getOptions(), true)).map(result -> result.id())); diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java index d167452d6..84a8762ee 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/CouchbaseCustomConversions.java @@ -31,7 +31,6 @@ import java.util.Set; import java.util.function.Consumer; -import com.fasterxml.jackson.annotation.JsonValue; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.core.convert.converter.GenericConverter; diff --git a/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java b/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java index ddb93ecff..58923a031 100644 --- a/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java +++ b/src/main/java/org/springframework/data/couchbase/core/query/OptionsBuilder.java @@ -15,7 +15,6 @@ */ package org.springframework.data.couchbase.core.query; -import static com.couchbase.client.core.util.Validators.notNull; import static org.springframework.data.couchbase.core.query.Meta.MetaKey.RETRY_STRATEGY; import static org.springframework.data.couchbase.core.query.Meta.MetaKey.SCAN_CONSISTENCY; import static org.springframework.data.couchbase.core.query.Meta.MetaKey.TIMEOUT; @@ -25,13 +24,9 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.time.Duration; -import java.util.HashMap; import java.util.Map; import java.util.Optional; -import com.couchbase.client.core.api.query.CoreQueryScanConsistency; -import com.couchbase.client.core.classic.query.ClassicCoreQueryOps; -import com.couchbase.client.core.error.InvalidArgumentException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.AnnotatedElementUtils; @@ -44,6 +39,9 @@ import org.springframework.data.couchbase.repository.Scope; import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod; +import com.couchbase.client.core.api.query.CoreQueryScanConsistency; +import com.couchbase.client.core.classic.query.ClassicCoreQueryOps; +import com.couchbase.client.core.error.InvalidArgumentException; import com.couchbase.client.core.io.CollectionIdentifier; import com.couchbase.client.core.msg.kv.DurabilityLevel; import com.couchbase.client.core.retry.RetryStrategy; @@ -51,14 +49,13 @@ import com.couchbase.client.java.json.JsonObject; import com.couchbase.client.java.kv.ExistsOptions; import com.couchbase.client.java.kv.InsertOptions; +import com.couchbase.client.java.kv.MutateInOptions; import com.couchbase.client.java.kv.MutationState; import com.couchbase.client.java.kv.PersistTo; -import com.couchbase.client.java.kv.MutateInOptions; import com.couchbase.client.java.kv.RemoveOptions; import com.couchbase.client.java.kv.ReplaceOptions; import com.couchbase.client.java.kv.ReplicateTo; import com.couchbase.client.java.kv.ScanOptions; -import com.couchbase.client.java.kv.ScanSort; import com.couchbase.client.java.kv.UpsertOptions; import com.couchbase.client.java.query.QueryOptions; import com.couchbase.client.java.query.QueryScanConsistency; @@ -548,11 +545,11 @@ public static String annotationString(Class annotation return annotationString(annotation, "value", defaultValue, elements); } - public static ScanOptions buildScanOptions(ScanOptions options, ScanSort sort, Boolean idsOnly, + public static ScanOptions buildScanOptions(ScanOptions options, Object sort, Boolean idsOnly, MutationState mutationState, Integer batchByteLimit, Integer batchItemLimit) { options = options != null ? options : ScanOptions.scanOptions(); if (sort != null) { - options.sort(sort); + //options.sort(sort); } if (idsOnly != null) { options.idsOnly(idsOnly); diff --git a/src/main/java/org/springframework/data/couchbase/core/support/ConsistentWith.java b/src/main/java/org/springframework/data/couchbase/core/support/ConsistentWith.java index c1ea65cae..715c13ac9 100644 --- a/src/main/java/org/springframework/data/couchbase/core/support/ConsistentWith.java +++ b/src/main/java/org/springframework/data/couchbase/core/support/ConsistentWith.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors + * Copyright 2020-2023 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. diff --git a/src/main/java/org/springframework/data/couchbase/core/support/IdsOnly.java b/src/main/java/org/springframework/data/couchbase/core/support/IdsOnly.java deleted file mode 100644 index 443aafc3d..000000000 --- a/src/main/java/org/springframework/data/couchbase/core/support/IdsOnly.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.core.support; - -/** - * A common interface for those that support withContent() - * - * @author Michael Reiche - * @param - the entity class - */ -public interface IdsOnly { - Object idsOnly(Boolean withContent); - -} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithBatchByteLimit.java b/src/main/java/org/springframework/data/couchbase/core/support/WithBatchByteLimit.java index adde3ca95..d197f994a 100644 --- a/src/main/java/org/springframework/data/couchbase/core/support/WithBatchByteLimit.java +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithBatchByteLimit.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors + * Copyright 2020-2023 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,5 +23,4 @@ */ public interface WithBatchByteLimit { Object withBatchByteLimit(Integer batchByteLimit); - } diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithBatchItemLimit.java b/src/main/java/org/springframework/data/couchbase/core/support/WithBatchItemLimit.java index 7d1b5ba8b..a81661f68 100644 --- a/src/main/java/org/springframework/data/couchbase/core/support/WithBatchItemLimit.java +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithBatchItemLimit.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors + * Copyright 2020-2023 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,5 +23,4 @@ */ public interface WithBatchItemLimit { Object withBatchItemLimit(Integer batchItemLimit); - } diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithLimit.java b/src/main/java/org/springframework/data/couchbase/core/support/WithLimit.java deleted file mode 100644 index 0fc2a587d..000000000 --- a/src/main/java/org/springframework/data/couchbase/core/support/WithLimit.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.core.support; - -/** - * A common interface for those that support withLimit() - * - * @author Michael Reiche - * @param - the entity class - */ -public interface WithLimit { - Object withLimit(Long limit); - -} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithSampling.java b/src/main/java/org/springframework/data/couchbase/core/support/WithSampling.java deleted file mode 100644 index 5c608da36..000000000 --- a/src/main/java/org/springframework/data/couchbase/core/support/WithSampling.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.core.support; - -import com.couchbase.client.java.kv.ScanSort; - -/** - * A common interface for those that support withSampling() - * - * @author Michael Reiche - * @param - the entity class - */ -public interface WithSampling { - Object withSampling(Boolean isSampling); - -} diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithScanOptions.java b/src/main/java/org/springframework/data/couchbase/core/support/WithScanOptions.java index 3d48de696..4a69360e1 100644 --- a/src/main/java/org/springframework/data/couchbase/core/support/WithScanOptions.java +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithScanOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors + * Copyright 2020-2023 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. @@ -25,5 +25,4 @@ */ public interface WithScanOptions { Object withOptions(ScanOptions expiry); - } diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithScanSort.java b/src/main/java/org/springframework/data/couchbase/core/support/WithScanSort.java index 0c97ca1e8..5fe70e2a7 100644 --- a/src/main/java/org/springframework/data/couchbase/core/support/WithScanSort.java +++ b/src/main/java/org/springframework/data/couchbase/core/support/WithScanSort.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the original author or authors + * Copyright 2020-2023 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.support; -import com.couchbase.client.java.kv.ScanSort; - /** * A common interface for those that support withOptions() * @@ -24,6 +22,5 @@ * @param - the entity class */ public interface WithScanSort { - Object withSort(ScanSort expiry); - + Object withSort(Object expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/support/WithSeed.java b/src/main/java/org/springframework/data/couchbase/core/support/WithSeed.java deleted file mode 100644 index debe51fc4..000000000 --- a/src/main/java/org/springframework/data/couchbase/core/support/WithSeed.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.couchbase.core.support; - -/** - * A common interface for those that support withSeed() - * - * @author Michael Reiche - * @param - the entity class - */ -public interface WithSeed { - Object withSeed(Long seed); - -} diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java index eaa3f3f9a..40d1cf2c4 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java @@ -29,6 +29,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -36,9 +37,6 @@ import java.util.UUID; import java.util.stream.Stream; -import com.couchbase.client.core.error.TimeoutException; -import com.couchbase.client.core.msg.kv.DurabilityLevel; -import com.couchbase.client.core.retry.RetryReason; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -54,6 +52,7 @@ import org.springframework.data.couchbase.core.support.WithExpiry; import org.springframework.data.couchbase.domain.Address; import org.springframework.data.couchbase.domain.Config; +import org.springframework.data.couchbase.domain.MutableUser; import org.springframework.data.couchbase.domain.NaiveAuditorAware; import org.springframework.data.couchbase.domain.PersonValue; import org.springframework.data.couchbase.domain.Submission; @@ -61,6 +60,11 @@ import org.springframework.data.couchbase.domain.UserAnnotated; import org.springframework.data.couchbase.domain.UserAnnotated2; import org.springframework.data.couchbase.domain.UserAnnotated3; +import org.springframework.data.couchbase.domain.UserAnnotatedDurability; +import org.springframework.data.couchbase.domain.UserAnnotatedDurabilityExpression; +import org.springframework.data.couchbase.domain.UserAnnotatedPersistTo; +import org.springframework.data.couchbase.domain.UserAnnotatedReplicateTo; +import org.springframework.data.couchbase.domain.UserAnnotatedTouchOnRead; import org.springframework.data.couchbase.domain.UserSubmission; import org.springframework.data.couchbase.util.ClusterType; import org.springframework.data.couchbase.util.IgnoreWhen; @@ -69,12 +73,14 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import com.couchbase.client.core.error.CouchbaseException; +import com.couchbase.client.core.error.TimeoutException; +import com.couchbase.client.core.msg.kv.DurabilityLevel; import com.couchbase.client.core.msg.kv.MutationToken; +import com.couchbase.client.core.retry.RetryReason; import com.couchbase.client.java.json.JsonObject; import com.couchbase.client.java.kv.MutationState; import com.couchbase.client.java.kv.PersistTo; import com.couchbase.client.java.kv.ReplicateTo; -import com.couchbase.client.java.kv.ScanSort; import com.couchbase.client.java.query.QueryOptions; import com.couchbase.client.java.query.QueryScanConsistency; @@ -1241,8 +1247,8 @@ void rangeScan() { } MutationToken mt = couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection() .upsert(id, JsonObject.create().put("id", id)).mutationToken().get(); - Stream users = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)).withSort(ScanSort.ASCENDING).rangeScan(lower, - upper); + Stream users = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)) + /*.withSort(ScanSort.ASCENDING)*/.rangeScan(lower, upper); for (User u : users.toList()) { System.err.print(u); System.err.println(","); @@ -1267,7 +1273,7 @@ void rangeScanId() { MutationToken mt = couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection() .upsert(id, JsonObject.create().put("id", id)).mutationToken().get(); Stream userIds = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)) - .withSort(ScanSort.ASCENDING).rangeScanIds(lower, upper); + /*.withSort(ScanSort.ASCENDING)*/.rangeScanIds(lower, upper); for (String userId : userIds.toList()) { System.err.print(userId); System.err.println(","); @@ -1292,13 +1298,15 @@ void sampleScan() { } MutationToken mt = couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection() .upsert(id, JsonObject.create().put("id", id)).mutationToken().get(); - Stream users = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)).withSort(ScanSort.ASCENDING).withSampling(true).rangeScan(lower, - upper); - for (User u : users.toList()) { + Stream users = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)) + /*.withSort(ScanSort.ASCENDING)*/.samplingScan(5l, null); + List usersList = users.toList(); + assertEquals(5, usersList.size(), "number in sample"); + for (User u : usersList) { System.err.print(u); System.err.println(","); - assertTrue(u.getId().compareTo(lower) >= 0 && u.getId().compareTo(upper) <= 0); - couchbaseTemplate.removeById(User.class).one(u.getId()); + // assertTrue(u.getId().compareTo(lower) >= 0 && u.getId().compareTo(upper) <= 0); + //couchbaseTemplate.removeById(User.class).one(u.getId()); } couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection().remove(id); } @@ -1318,12 +1326,12 @@ void sampleScanId() { MutationToken mt = couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection() .upsert(id, JsonObject.create().put("id", id)).mutationToken().get(); Stream userIds = couchbaseTemplate.rangeScan(User.class).consistentWith(MutationState.from(mt)) - .withSort(ScanSort.ASCENDING).rangeScanIds(lower, upper); + /*.withSort(ScanSort.ASCENDING)*/.samplingScanIds(5l); for (String userId : userIds.toList()) { System.err.print(userId); System.err.println(","); - assertTrue(userId.compareTo(lower) >= 0 && userId.compareTo(upper) <= 0); - couchbaseTemplate.removeById(User.class).one(userId); + //assertTrue(userId.compareTo(lower) >= 0 && userId.compareTo(upper) <= 0); + //couchbaseTemplate.removeById(User.class).one(userId); } couchbaseTemplate.getCouchbaseClientFactory().getDefaultCollection().remove(id);