From d531dbd791d855cad2b92bd74f4b50d111a47f95 Mon Sep 17 00:00:00 2001 From: mikereiche Date: Thu, 10 Dec 2020 18:04:04 -0800 Subject: [PATCH] DATACOUCH-645 - Support Document(expiryExpression) annotation. 1) get the expiryExpression from the converted CouchbaseDocument. 2) added interfaces for apis on core objects which are common among many objects. For instance withExpiry and withExpirty on all the insert/replace/upsert/remove objects. The definition of an interface simplyfies testing of all the possible combinations - instead of having four (or eight, counting Reactive), there is one interface to test them all. --- .../core/ExecutableInsertByIdOperation.java | 11 +- .../core/ExecutableRemoveByIdOperation.java | 4 +- .../core/ExecutableReplaceByIdOperation.java | 12 +- .../core/ExecutableUpsertByIdOperation.java | 11 +- .../data/couchbase/core/InCollection.java | 27 ++ .../data/couchbase/core/OneAndAll.java | 32 +++ .../couchbase/core/OneAndAllReactive.java | 35 +++ .../core/ReactiveInsertByIdOperation.java | 8 +- .../ReactiveInsertByIdOperationSupport.java | 11 +- .../core/ReactiveRemoveByIdOperation.java | 4 +- .../core/ReactiveReplaceByIdOperation.java | 8 +- .../ReactiveReplaceByIdOperationSupport.java | 13 +- .../core/ReactiveUpsertByIdOperation.java | 8 +- .../ReactiveUpsertByIdOperationSupport.java | 13 +- .../data/couchbase/core/WithDurability.java | 33 +++ .../data/couchbase/core/WithExpiry.java | 29 ++ ...hbaseTemplateKeyValueIntegrationTests.java | 250 +++++++----------- ...ouchbaseTemplateQueryIntegrationTests.java | 12 +- .../data/couchbase/domain/UserAnnotated.java | 11 +- .../data/couchbase/domain/UserAnnotated2.java | 38 +++ ...chbaseRepositoryQueryIntegrationTests.java | 8 +- ...chbaseRepositoryQueryIntegrationTests.java | 8 +- 22 files changed, 357 insertions(+), 229 deletions(-) create mode 100644 src/main/java/org/springframework/data/couchbase/core/InCollection.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/OneAndAll.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/OneAndAllReactive.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/WithDurability.java create mode 100644 src/main/java/org/springframework/data/couchbase/core/WithExpiry.java create mode 100644 src/test/java/org/springframework/data/couchbase/domain/UserAnnotated2.java diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperation.java index 6ab0ea948..761f2e74c 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableInsertByIdOperation.java @@ -26,20 +26,22 @@ public interface ExecutableInsertByIdOperation { ExecutableInsertById insertById(Class domainType); - interface TerminatingInsertById { + interface TerminatingInsertById extends OneAndAll{ + @Override T one(T object); + @Override Collection all(Collection objects); } - interface InsertByIdWithCollection extends TerminatingInsertById { + interface InsertByIdWithCollection extends TerminatingInsertById, InCollection { TerminatingInsertById inCollection(String collection); } - interface InsertByIdWithDurability extends InsertByIdWithCollection { + interface InsertByIdWithDurability extends InsertByIdWithCollection, WithDurability { InsertByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -47,8 +49,9 @@ interface InsertByIdWithDurability extends InsertByIdWithCollection { } - interface InsertByIdWithExpiry extends InsertByIdWithDurability { + interface InsertByIdWithExpiry extends InsertByIdWithDurability, WithExpiry { + @Override InsertByIdWithDurability withExpiry(Duration expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java index 7287f6966..94a6cf682 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableRemoveByIdOperation.java @@ -34,12 +34,12 @@ interface TerminatingRemoveById { } - interface RemoveByIdWithCollection extends TerminatingRemoveById { + interface RemoveByIdWithCollection extends TerminatingRemoveById, InCollection { TerminatingRemoveById inCollection(String collection); } - interface RemoveByIdWithDurability extends RemoveByIdWithCollection { + interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurability { RemoveByIdWithCollection withDurability(DurabilityLevel durabilityLevel); diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java index 98fc758da..b89c7ae89 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableReplaceByIdOperation.java @@ -19,6 +19,7 @@ import java.util.Collection; import com.couchbase.client.core.msg.kv.DurabilityLevel; +import com.couchbase.client.java.kv.IncrementOptions; import com.couchbase.client.java.kv.PersistTo; import com.couchbase.client.java.kv.ReplicateTo; @@ -26,20 +27,22 @@ public interface ExecutableReplaceByIdOperation { ExecutableReplaceById replaceById(Class domainType); - interface TerminatingReplaceById { + interface TerminatingReplaceById extends OneAndAll { + @Override T one(T object); + @Override Collection all(Collection objects); } - interface ReplaceByIdWithCollection extends TerminatingReplaceById { + interface ReplaceByIdWithCollection extends TerminatingReplaceById , InCollection { TerminatingReplaceById inCollection(String collection); } - interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection { + interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection, WithDurability { ReplaceByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -47,8 +50,9 @@ interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection { } - interface ReplaceByIdWithExpiry extends ReplaceByIdWithDurability { + interface ReplaceByIdWithExpiry extends ReplaceByIdWithDurability, WithExpiry { + @Override ReplaceByIdWithDurability withExpiry(final Duration expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperation.java index 3d919a8c2..a21ba69d7 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ExecutableUpsertByIdOperation.java @@ -26,20 +26,22 @@ public interface ExecutableUpsertByIdOperation { ExecutableUpsertById upsertById(Class domainType); - interface TerminatingUpsertById { + interface TerminatingUpsertById extends OneAndAll{ + @Override T one(T object); + @Override Collection all(Collection objects); } - interface UpsertByIdWithCollection extends TerminatingUpsertById { + interface UpsertByIdWithCollection extends TerminatingUpsertById, InCollection { TerminatingUpsertById inCollection(String collection); } - interface UpsertByIdWithDurability extends UpsertByIdWithCollection { + interface UpsertByIdWithDurability extends UpsertByIdWithCollection, WithDurability { UpsertByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -47,8 +49,9 @@ interface UpsertByIdWithDurability extends UpsertByIdWithCollection { } - interface UpsertByIdWithExpiry extends UpsertByIdWithDurability { + interface UpsertByIdWithExpiry extends UpsertByIdWithDurability, WithExpiry { + @Override UpsertByIdWithDurability withExpiry(Duration expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/InCollection.java b/src/main/java/org/springframework/data/couchbase/core/InCollection.java new file mode 100644 index 000000000..d9fb292ef --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/InCollection.java @@ -0,0 +1,27 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +/** + * A common interface for all of Insert, Replace, Upsert that take collection + * + * @author Michael Reiche + * + * @param - the entity class + */ +public interface InCollection { + Object inCollection(String collection); +} diff --git a/src/main/java/org/springframework/data/couchbase/core/OneAndAll.java b/src/main/java/org/springframework/data/couchbase/core/OneAndAll.java new file mode 100644 index 000000000..fd0077ff3 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/OneAndAll.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +import java.util.Collection; + +/** + * A common interface for all of Insert, Replace, Upsert + * + * @author Michael Reiche + * + * @param - the entity class + */ +public interface OneAndAll { + + T one(T object); + + Collection all(Collection objects); +} diff --git a/src/main/java/org/springframework/data/couchbase/core/OneAndAllReactive.java b/src/main/java/org/springframework/data/couchbase/core/OneAndAllReactive.java new file mode 100644 index 000000000..251964ce1 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/OneAndAllReactive.java @@ -0,0 +1,35 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.Collection; + +/** + * A common interface for all of Insert, Replace, Upsert + * + * @author Michael Reiche + * + * @param - the entity class + */ + +public interface OneAndAllReactive { + Mono one(T object); + + Flux all(Collection objects); +} diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java index 54e10163c..4bc9ffa2f 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperation.java @@ -29,7 +29,7 @@ public interface ReactiveInsertByIdOperation { ReactiveInsertById insertById(Class domainType); - interface TerminatingInsertById { + interface TerminatingInsertById extends OneAndAllReactive{ Mono one(T object); @@ -37,12 +37,12 @@ interface TerminatingInsertById { } - interface InsertByIdWithCollection extends TerminatingInsertById { + interface InsertByIdWithCollection extends TerminatingInsertById, InCollection { TerminatingInsertById inCollection(String collection); } - interface InsertByIdWithDurability extends InsertByIdWithCollection { + interface InsertByIdWithDurability extends InsertByIdWithCollection, WithDurability { InsertByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -50,7 +50,7 @@ interface InsertByIdWithDurability extends InsertByIdWithCollection { } - interface InsertByIdWithExpiry extends InsertByIdWithDurability { + interface InsertByIdWithExpiry extends InsertByIdWithDurability, WithExpiry{ InsertByIdWithDurability withExpiry(Duration expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java index 181bbf26e..01a9faa30 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveInsertByIdOperationSupport.java @@ -15,7 +15,6 @@ */ package org.springframework.data.couchbase.core; -import org.springframework.data.couchbase.core.mapping.Document; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -72,7 +71,7 @@ public Mono one(T object) { return Mono.just(object).flatMap(o -> { CouchbaseDocument converted = template.support().encodeEntity(o); return template.getCollection(collection).reactive() - .insert(converted.getId(), converted.export(), buildInsertOptions()).map(result -> { + .insert(converted.getId(), converted.export(), buildInsertOptions(converted)).map(result -> { Object updatedObject = template.support().applyUpdatedId(o, converted.getId()); return (T) template.support().applyUpdatedCas(updatedObject, result.cas()); }); @@ -90,7 +89,7 @@ public Flux all(Collection objects) { return Flux.fromIterable(objects).flatMap(this::one); } - private InsertOptions buildInsertOptions() { + private InsertOptions buildInsertOptions(CouchbaseDocument doc) { // CouchbaseDocument converted final InsertOptions options = InsertOptions.insertOptions(); if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { options.durability(persistTo, replicateTo); @@ -99,10 +98,8 @@ private InsertOptions buildInsertOptions() { } if (expiry != null && !expiry.isZero()) { options.expiry(expiry); - } else if (domainType.isAnnotationPresent(Document.class)) { - Document documentAnn = domainType.getAnnotation(Document.class); - long durationSeconds = documentAnn.expiryUnit().toSeconds(documentAnn.expiry()); - options.expiry(Duration.ofSeconds(durationSeconds)); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); } return options; } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java index d26519af9..d1cc5fa65 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveRemoveByIdOperation.java @@ -36,12 +36,12 @@ interface TerminatingRemoveById { } - interface RemoveByIdWithCollection extends TerminatingRemoveById { + interface RemoveByIdWithCollection extends TerminatingRemoveById, InCollection { TerminatingRemoveById inCollection(String collection); } - interface RemoveByIdWithDurability extends RemoveByIdWithCollection { + interface RemoveByIdWithDurability extends RemoveByIdWithCollection, WithDurability { RemoveByIdWithCollection withDurability(DurabilityLevel durabilityLevel); diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java index acdf825f1..89bf0ca52 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperation.java @@ -29,7 +29,7 @@ public interface ReactiveReplaceByIdOperation { ReactiveReplaceById replaceById(Class domainType); - interface TerminatingReplaceById { + interface TerminatingReplaceById extends OneAndAllReactive { Mono one(T object); @@ -37,12 +37,12 @@ interface TerminatingReplaceById { } - interface ReplaceByIdWithCollection extends TerminatingReplaceById { + interface ReplaceByIdWithCollection extends TerminatingReplaceById, InCollection { TerminatingReplaceById inCollection(String collection); } - interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection { + interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection, WithDurability { ReplaceByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -50,7 +50,7 @@ interface ReplaceByIdWithDurability extends ReplaceByIdWithCollection { } - interface ReplaceByIdWithExpiry extends ReplaceByIdWithDurability { + interface ReplaceByIdWithExpiry extends ReplaceByIdWithDurability, WithExpiry { ReplaceByIdWithDurability withExpiry(final Duration expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java index 787bdb7dd..3c5936227 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveReplaceByIdOperationSupport.java @@ -22,7 +22,6 @@ import java.util.Collection; import org.springframework.data.couchbase.core.mapping.CouchbaseDocument; -import org.springframework.data.couchbase.core.mapping.Document; import org.springframework.util.Assert; import com.couchbase.client.core.msg.kv.DurabilityLevel; @@ -72,8 +71,8 @@ public Mono one(T object) { return Mono.just(object).flatMap(o -> { CouchbaseDocument converted = template.support().encodeEntity(o); return template.getCollection(collection).reactive() - .replace(converted.getId(), converted.export(), buildReplaceOptions(o)).map(result -> - (T)template.support().applyUpdatedCas(o, result.cas())); + .replace(converted.getId(), converted.export(), buildReplaceOptions(o, converted)) + .map(result -> (T) template.support().applyUpdatedCas(o, result.cas())); }).onErrorMap(throwable -> { if (throwable instanceof RuntimeException) { return template.potentiallyConvertRuntimeException((RuntimeException) throwable); @@ -88,7 +87,7 @@ public Flux all(Collection objects) { return Flux.fromIterable(objects).flatMap(this::one); } - private ReplaceOptions buildReplaceOptions(T object) { + private ReplaceOptions buildReplaceOptions(T object, CouchbaseDocument doc) { final ReplaceOptions options = ReplaceOptions.replaceOptions(); if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { options.durability(persistTo, replicateTo); @@ -97,10 +96,8 @@ private ReplaceOptions buildReplaceOptions(T object) { } if (expiry != null && !expiry.isZero()) { options.expiry(expiry); - } else if (domainType.isAnnotationPresent(Document.class)) { - Document documentAnn = domainType.getAnnotation(Document.class); - long durationSeconds = documentAnn.expiryUnit().toSeconds(documentAnn.expiry()); - options.expiry(Duration.ofSeconds(durationSeconds)); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); } long cas = template.support().getCas(object); options.cas(cas); diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperation.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperation.java index da0df85bd..b2c3c700d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperation.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperation.java @@ -29,7 +29,7 @@ public interface ReactiveUpsertByIdOperation { ReactiveUpsertById upsertById(Class domainType); - interface TerminatingUpsertById { + interface TerminatingUpsertById extends OneAndAllReactive{ Mono one(T object); @@ -37,12 +37,12 @@ interface TerminatingUpsertById { } - interface UpsertByIdWithCollection extends TerminatingUpsertById { + interface UpsertByIdWithCollection extends TerminatingUpsertById, InCollection { TerminatingUpsertById inCollection(String collection); } - interface UpsertByIdWithDurability extends UpsertByIdWithCollection { + interface UpsertByIdWithDurability extends UpsertByIdWithCollection, WithDurability { UpsertByIdWithCollection withDurability(DurabilityLevel durabilityLevel); @@ -50,7 +50,7 @@ interface UpsertByIdWithDurability extends UpsertByIdWithCollection { } - interface UpsertByIdWithExpiry extends UpsertByIdWithDurability { + interface UpsertByIdWithExpiry extends UpsertByIdWithDurability, WithExpiry { UpsertByIdWithDurability withExpiry(Duration expiry); } diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java index c4e8fb690..0e18cf2e8 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveUpsertByIdOperationSupport.java @@ -15,9 +15,6 @@ */ package org.springframework.data.couchbase.core; -import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity; -import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty; -import org.springframework.data.couchbase.core.mapping.Document; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -74,7 +71,7 @@ public Mono one(T object) { return Mono.just(object).flatMap(o -> { CouchbaseDocument converted = template.support().encodeEntity(o); return template.getCollection(collection).reactive() - .upsert(converted.getId(), converted.export(), buildUpsertOptions()).map(result -> { + .upsert(converted.getId(), converted.export(), buildUpsertOptions(converted)).map(result -> { Object updatedObject = template.support().applyUpdatedId(o, converted.getId()); return (T) template.support().applyUpdatedCas(updatedObject, result.cas()); }); @@ -92,7 +89,7 @@ public Flux all(Collection objects) { return Flux.fromIterable(objects).flatMap(this::one); } - private UpsertOptions buildUpsertOptions() { + private UpsertOptions buildUpsertOptions(CouchbaseDocument doc) { final UpsertOptions options = UpsertOptions.upsertOptions(); if (persistTo != PersistTo.NONE || replicateTo != ReplicateTo.NONE) { options.durability(persistTo, replicateTo); @@ -101,10 +98,8 @@ private UpsertOptions buildUpsertOptions() { } if (expiry != null && !expiry.isZero()) { options.expiry(expiry); - } else if (domainType.isAnnotationPresent(Document.class)) { - Document documentAnn = domainType.getAnnotation(Document.class); - long durationSeconds = documentAnn.expiryUnit().toSeconds(documentAnn.expiry()); - options.expiry(Duration.ofSeconds(durationSeconds)); + } else if (doc.getExpiration() != 0) { + options.expiry(Duration.ofSeconds(doc.getExpiration())); } return options; } diff --git a/src/main/java/org/springframework/data/couchbase/core/WithDurability.java b/src/main/java/org/springframework/data/couchbase/core/WithDurability.java new file mode 100644 index 000000000..d829120d7 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/WithDurability.java @@ -0,0 +1,33 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +import com.couchbase.client.core.msg.kv.DurabilityLevel; +import com.couchbase.client.java.kv.PersistTo; +import com.couchbase.client.java.kv.ReplicateTo; + +/** + * A common interface for all of Insert, Replace, Upsert that take Durability + * + * @author Michael Reiche + * + * @param - the entity class + */ +public interface WithDurability { + Object withDurability(DurabilityLevel durabilityLevel); + + Object withDurability(PersistTo persistTo, ReplicateTo replicateTo); +} diff --git a/src/main/java/org/springframework/data/couchbase/core/WithExpiry.java b/src/main/java/org/springframework/data/couchbase/core/WithExpiry.java new file mode 100644 index 000000000..7757dd978 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/core/WithExpiry.java @@ -0,0 +1,29 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.couchbase.core; + +import java.time.Duration; + +/** + * A common interface for all of Insert, Replace, Upsert that take expiry + * + * @author Michael Reiche + * + * @param - the entity class + */ +public interface WithExpiry { + Object withExpiry(Duration expiry); +} 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 f95f4ee42..7d245a316 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateKeyValueIntegrationTests.java @@ -27,9 +27,14 @@ import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.time.Duration; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; +import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions;; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -37,14 +42,17 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DataRetrievalFailureException; import org.springframework.dao.DuplicateKeyException; import org.springframework.data.couchbase.CouchbaseClientFactory; import org.springframework.data.couchbase.SimpleCouchbaseClientFactory; +import org.springframework.data.couchbase.core.ExecutableReplaceByIdOperation.ExecutableReplaceById; +import org.springframework.data.couchbase.core.ExecutableRemoveByIdOperation.ExecutableRemoveById; + import org.springframework.data.couchbase.domain.Config; import org.springframework.data.couchbase.domain.PersonValue; 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.util.ClusterAwareIntegrationTests; import org.springframework.data.couchbase.util.ClusterType; import org.springframework.data.couchbase.util.IgnoreWhen; @@ -69,6 +77,8 @@ class CouchbaseTemplateKeyValueIntegrationTests extends ClusterAwareIntegrationT static void beforeAll() { couchbaseClientFactory = new SimpleCouchbaseClientFactory(connectionString(), authenticator(), bucketName()); couchbaseClientFactory.getBucket().waitUntilReady(Duration.ofSeconds(10)); + couchbaseClientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(), + CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true)); } @AfterAll @@ -81,6 +91,9 @@ void beforeEach() { ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE); reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE); + couchbaseTemplate.removeByQuery(User.class).all(); + couchbaseTemplate.removeByQuery(UserAnnotated.class).all(); + couchbaseTemplate.removeByQuery(UserAnnotated2.class).all(); } @Test @@ -103,88 +116,78 @@ void upsertAndFindById() { } @Test - void upsertWithDurability() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - User modified = couchbaseTemplate.upsertById(User.class).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE) - .one(user); - assertEquals(user, modified); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertEquals(user, found); - couchbaseTemplate.removeById().one(user.getId()); - } - - @Test - void upsertWithExpiry() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - User modified = couchbaseTemplate.upsertById(User.class).withExpiry(Duration.ofSeconds(1)).one(user); - assertEquals(user, modified); - sleepSecs(2); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // + void withDurability() + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + Class clazz = User.class; // for now, just User.class. There is no Durability annotation. + // insert, replace, upsert + for (OneAndAll operator : new OneAndAll[] { couchbaseTemplate.insertById(clazz), + couchbaseTemplate.replaceById(clazz), couchbaseTemplate.upsertById(clazz)}) { + // create an entity of type clazz + Constructor cons = clazz.getConstructor(String.class, String.class, String.class); + User user = (User) cons.newInstance("" + operator.getClass().getSimpleName() + "_" + clazz.getSimpleName(), + "firstname", "lastname"); + + if (clazz.equals(User.class)) { // User.java doesn't have an durability annotation + operator = (OneAndAll) ((WithDurability) operator).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE); } - } - } - @Test - void upsertWithExpiryAnnotation() { - UserAnnotated user = new UserAnnotated(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - UserAnnotated modified = couchbaseTemplate.upsertById(UserAnnotated.class).one(user); - assertEquals(user, modified); - sleepSecs(6); + // if replace, we need to insert a document to replace + if (operator instanceof ExecutableReplaceById) { + couchbaseTemplate.insertById(User.class).one(user); + } + // call to insert/replace/update + User returned = (User)operator.one(user); + assertEquals(user, returned); User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // + assertEquals(user, found); + + if (operator instanceof ExecutableReplaceById) { + couchbaseTemplate.removeById().withDurability(PersistTo.ACTIVE, ReplicateTo.NONE).one(user.getId()); + User removed = (User) couchbaseTemplate.findById(user.getClass()).one(user.getId()); + assertNull(removed, "found should have been null as document should be removed"); } } + } @Test - void replaceWithExpiry() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - User modified = couchbaseTemplate.upsertById(User.class).withExpiry(Duration.ofSeconds(1)).one(user); - couchbaseTemplate.replaceById(User.class).withExpiry(Duration.ofSeconds(1)).one(user); - assertEquals(user, modified); - sleepSecs(2); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // + void withExpiryAndExpiryAnnotation() + throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { + // ( withExpiry(), expiry=1, expiryExpression=${myExpiry} ) X ( insert, + // replace, upsert ) + Set users = new HashSet<>(); // set of all documents we will insert + // Entity classes + for (Class clazz : new Class[] { User.class, UserAnnotated.class, UserAnnotated2.class }) { + // insert, replace, upsert + for (OneAndAll operator : new OneAndAll[] { couchbaseTemplate.insertById(clazz), + couchbaseTemplate.replaceById(clazz), couchbaseTemplate.upsertById(clazz) }) { + + // create an entity of type clazz + Constructor cons = clazz.getConstructor(String.class, String.class, String.class); + User user = (User) cons.newInstance("" + operator.getClass().getSimpleName() + "_" + clazz.getSimpleName(), + "firstname", "lastname"); + + if (clazz.equals(User.class)) { // User.java doesn't have an expiry annotation + operator = (OneAndAll) ((WithExpiry) operator).withExpiry(Duration.ofSeconds(1)); + } + + // if replace or remove, we need to insert a document to replace + if (operator instanceof ExecutableReplaceById || operator instanceof ExecutableRemoveById) { + couchbaseTemplate.insertById(User.class).one(user); + } + // call to insert/replace/update + User returned = (User)operator.one(user); + assertEquals(user, returned); + users.add(user); } } - } - - @Test - void replaceWithExpiryAnnotation() { - UserAnnotated user = new UserAnnotated(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - UserAnnotated modified = couchbaseTemplate.upsertById(UserAnnotated.class).one(user); - modified = couchbaseTemplate.replaceById(UserAnnotated.class).one(user); - assertEquals(user, modified); - sleepSecs(6); - User found = couchbaseTemplate.findById(UserAnnotated.class).one(user.getId()); + // check that they are gone after a few seconds. + sleepSecs(4); + for (User user : users) { + User found = (User) couchbaseTemplate.findById(user.getClass()).one(user.getId()); assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // - } } + } @Test @@ -227,10 +230,7 @@ void insertById() { User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); User inserted = couchbaseTemplate.insertById(User.class).one(user); assertEquals(user, inserted); - assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user)); - couchbaseTemplate.removeById().one(user.getId()); - } @Test @@ -239,47 +239,7 @@ void insertByIdwithDurability() { User inserted = couchbaseTemplate.insertById(User.class).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE) .one(user); assertEquals(user, inserted); - assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user)); - couchbaseTemplate.removeById().one(user.getId()); - - } - - @Test - void insertByIdwithExpiry() { - User user = new User(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - User inserted = couchbaseTemplate.insertById(User.class).withExpiry(Duration.ofSeconds(1)).one(user); - assertEquals(user, inserted); - sleepSecs(2); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // ignore - } - } - - } - - @Test - void insertWithExpiryAnnotation() { - UserAnnotated user = new UserAnnotated(UUID.randomUUID().toString(), "firstname", "lastname"); - try { - UserAnnotated inserted = couchbaseTemplate.insertById(UserAnnotated.class).one(user); - assertEquals(user, inserted); - sleepSecs(6); - User found = couchbaseTemplate.findById(User.class).one(user.getId()); - assertNull(found, "found should have been null as document should be expired"); - } finally { - try { - couchbaseTemplate.removeById().one(user.getId()); - } catch (DataRetrievalFailureException e) { - // ignore - } - } } @Test @@ -292,52 +252,44 @@ void existsById() { assertEquals(user, inserted); assertTrue(couchbaseTemplate.existsById().one(id)); - couchbaseTemplate.removeById().one(user.getId()); } @Test @IgnoreWhen(clusterTypes = ClusterType.MOCKED) void saveAndFindImmutableById() { - PersonValue personValue = new PersonValue(null, 123, "f", "l"); - System.out.println("personValue: " + personValue); - // personValue = personValue.withVersion(123); + PersonValue personValue = new PersonValue(UUID.randomUUID().toString(), 123, "f", "l"); PersonValue inserted = null; PersonValue upserted = null; PersonValue replaced = null; - try { + inserted = couchbaseTemplate.insertById(PersonValue.class).one(personValue); + assertNotEquals(0, inserted.getVersion()); + PersonValue foundInserted = couchbaseTemplate.findById(PersonValue.class).one(inserted.getId()); + assertNotNull(foundInserted, "inserted personValue not found"); + assertEquals(inserted, foundInserted); + + // upsert will insert + couchbaseTemplate.removeById().one(inserted.getId()); + upserted = couchbaseTemplate.upsertById(PersonValue.class).one(inserted); + assertNotEquals(0, upserted.getVersion()); + PersonValue foundUpserted = couchbaseTemplate.findById(PersonValue.class).one(upserted.getId()); + assertNotNull(foundUpserted, "upserted personValue not found"); + assertEquals(upserted, foundUpserted); + + // upsert will replace + upserted = couchbaseTemplate.upsertById(PersonValue.class).one(inserted); + assertNotEquals(0, upserted.getVersion()); + PersonValue foundUpserted2 = couchbaseTemplate.findById(PersonValue.class).one(upserted.getId()); + assertNotNull(foundUpserted2, "upserted personValue not found"); + assertEquals(upserted, foundUpserted2); + + replaced = couchbaseTemplate.replaceById(PersonValue.class).one(upserted); + assertNotEquals(0, replaced.getVersion()); + PersonValue foundReplaced = couchbaseTemplate.findById(PersonValue.class).one(replaced.getId()); + assertNotNull(foundReplaced, "replaced personValue not found"); + assertEquals(replaced, foundReplaced); - inserted = couchbaseTemplate.insertById(PersonValue.class).one(personValue); - assertNotEquals(0, inserted.getVersion()); - PersonValue foundInserted = couchbaseTemplate.findById(PersonValue.class).one(inserted.getId()); - assertNotNull(foundInserted, "inserted personValue not found"); - assertEquals(inserted, foundInserted); - - // upsert will be inserted - couchbaseTemplate.removeById().one(inserted.getId()); - upserted = couchbaseTemplate.upsertById(PersonValue.class).one(inserted); - assertNotEquals(0, upserted.getVersion()); - PersonValue foundUpserted = couchbaseTemplate.findById(PersonValue.class).one(upserted.getId()); - assertNotNull(foundUpserted, "upserted personValue not found"); - assertEquals(upserted, foundUpserted); - - // upsert will be replaced - upserted = couchbaseTemplate.upsertById(PersonValue.class).one(inserted); - assertNotEquals(0, upserted.getVersion()); - PersonValue foundUpserted2 = couchbaseTemplate.findById(PersonValue.class).one(upserted.getId()); - assertNotNull(foundUpserted2, "upserted personValue not found"); - assertEquals(upserted, foundUpserted2); - - replaced = couchbaseTemplate.replaceById(PersonValue.class).one(upserted); - assertNotEquals(0, replaced.getVersion()); - PersonValue foundReplaced = couchbaseTemplate.findById(PersonValue.class).one(replaced.getId()); - assertNotNull(foundReplaced, "replaced personValue not found"); - assertEquals(replaced, foundReplaced); - - } finally { - couchbaseTemplate.removeById().one(inserted.getId()); - } } private void sleepSecs(int i) { diff --git a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java index 33c5c0241..15fcb6e62 100644 --- a/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/CouchbaseTemplateQueryIntegrationTests.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.UUID; +import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -65,12 +66,8 @@ class CouchbaseTemplateQueryIntegrationTests extends ClusterAwareIntegrationTest @BeforeAll static void beforeAll() { couchbaseClientFactory = new SimpleCouchbaseClientFactory(connectionString(), authenticator(), bucketName()); - - try { - couchbaseClientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName()); - } catch (IndexExistsException ex) { - // ignore, all good. - } + couchbaseClientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(), + CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true)); } @AfterAll @@ -177,8 +174,7 @@ void removeByMatchingQuery() { Query nonSpecialUsers = new Query(QueryCriteria.where("firstname").notLike("special")); couchbaseTemplate.removeByQuery(User.class).consistentWith(QueryScanConsistency.REQUEST_PLUS) - .matching(nonSpecialUsers) - .all(); + .matching(nonSpecialUsers).all(); assertNull(couchbaseTemplate.findById(User.class).one(user1.getId())); assertNull(couchbaseTemplate.findById(User.class).one(user2.getId())); diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated.java b/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated.java index 30c3b06c6..ef82c2ff3 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated.java +++ b/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated.java @@ -16,19 +16,10 @@ package org.springframework.data.couchbase.domain; -import java.util.Objects; - -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.Id; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.annotation.PersistenceConstructor; -import org.springframework.data.annotation.Version; import org.springframework.data.couchbase.core.mapping.Document; /** - * Annoted User entity for tests + * Annotated User entity for tests * * @author Michael Reiche */ diff --git a/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated2.java b/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated2.java new file mode 100644 index 000000000..72d05a549 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/domain/UserAnnotated2.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 the original author or authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.data.couchbase.domain; + +import java.util.concurrent.TimeUnit; + +import org.springframework.data.couchbase.core.mapping.Document; + +/** + * Annotated User entity for tests + * + * @author Michael Reiche + */ + +@Document(expiryExpression = "${myExpiryExpression}", expiryUnit = TimeUnit.SECONDS) +public class UserAnnotated2 extends User { + static { + System.setProperty("myExpiryExpression", "2"); + } + + public UserAnnotated2(String id, String firstname, String lastname) { + super(id, firstname, lastname); + } +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java index 4c14ce1d2..09bce76e6 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -30,6 +30,7 @@ import java.util.concurrent.Future; import java.util.stream.Collectors; +import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -74,11 +75,8 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr @BeforeEach void beforeEach() { - try { - clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName()); - } catch (IndexExistsException ex) { - // ignore, all good. - } + clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(), + CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true)); } @Test diff --git a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java index 2e0ed241d..66b659fc2 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @@ -70,11 +71,8 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends ClusterAwa @BeforeEach void beforeEach() { - try { - clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName()); - } catch (IndexExistsException ex) { - // ignore, all good. - } + clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(), + CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true)); } @Test