Scope and collection API for template. (#1133)

Scope and Collection API for template.

Closes #963.
Original pull request: #1071.
This commit is contained in:
Michael Reiche
2021-06-02 09:38:02 -04:00
committed by GitHub
parent 5cf142d5f2
commit 43889e6a9c
92 changed files with 4630 additions and 749 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@ import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import com.couchbase.client.java.query.QueryScanConsistency;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.dao.DataIntegrityViolationException;
@@ -68,6 +69,7 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
couchbaseTemplate.removeByQuery(User.class).all();
couchbaseTemplate.removeByQuery(UserAnnotated.class).all();
couchbaseTemplate.removeByQuery(UserAnnotated2.class).all();
couchbaseTemplate.removeByQuery(UserAnnotated3.class).all();
}
@Test
@@ -93,12 +95,12 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
@Test
void withDurability()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = User.class; // for now, just User.class. There is no Durability annotation.
Class<?> clazz = User.class; // for now, just User.class. There is no Durability annotation.
// insert, replace, upsert
for (OneAndAllEntity<User> operator : new OneAndAllEntity[] { couchbaseTemplate.insertById(clazz),
couchbaseTemplate.replaceById(clazz), couchbaseTemplate.upsertById(clazz) }) {
// create an entity of type clazz
Constructor cons = clazz.getConstructor(String.class, String.class, String.class);
Constructor<?> cons = clazz.getConstructor(String.class, String.class, String.class);
User user = (User) cons.newInstance("" + operator.getClass().getSimpleName() + "_" + clazz.getSimpleName(),
"firstname", "lastname");
@@ -112,7 +114,22 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
couchbaseTemplate.insertById(User.class).one(user);
}
// call to insert/replace/update
User returned = (User) operator.one(user);
User returned = null;
// occasionally gives "reactor.core.Exceptions$OverflowException: Could not emit value due to lack of requests"
for (int i = 1; i != 5; i++) {
try {
returned = (User) operator.one(user);
break;
} catch (Exception ofe) {
System.out.println(""+i+" caught: "+ofe);
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
if (i == 4) {
throw ofe;
}
sleepSecs(1);
}
}
assertEquals(user, returned);
User found = couchbaseTemplate.findById(User.class).one(user.getId());
assertEquals(user, found);
@@ -210,8 +227,6 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
{
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
User modified = couchbaseTemplate.upsertById(User.class).one(user);
System.out.println(reactiveCouchbaseTemplate.support().getCas(user));
System.out.println(reactiveCouchbaseTemplate.support().getCas(modified));
assertEquals(user, modified);
// careful now - user and modified are the same object. The object has the new cas (@Version version)
@@ -236,8 +251,23 @@ class CouchbaseTemplateKeyValueIntegrationTests extends JavaIntegrationTests {
@Test
void insertByIdwithDurability() {
User user = new User(UUID.randomUUID().toString(), "firstname", "lastname");
User inserted = couchbaseTemplate.insertById(User.class).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE)
.one(user);
User inserted = null;
// occasionally gives "reactor.core.Exceptions$OverflowException: Could not emit value due to lack of requests"
for (int i = 1; i != 5; i++) {
try {
inserted = couchbaseTemplate.insertById(User.class).withDurability(PersistTo.ACTIVE, ReplicateTo.NONE)
.one(user);
break;
} catch (Exception ofe) {
System.out.println(""+i+" caught: "+ofe);
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
if (i == 4) {
throw ofe;
}
sleepSecs(1);
}
}
assertEquals(user, inserted);
assertThrows(DuplicateKeyException.class, () -> couchbaseTemplate.insertById(User.class).one(user));
}

View File

@@ -19,8 +19,10 @@ package org.springframework.data.couchbase.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
@@ -32,6 +34,7 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteria;
@@ -46,30 +49,42 @@ import org.springframework.data.couchbase.domain.UserSubmission;
import org.springframework.data.couchbase.domain.UserSubmissionProjected;
import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider;
import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.CollectionAwareIntegrationTests;
import org.springframework.data.couchbase.util.IgnoreWhen;
import com.couchbase.client.core.error.AmbiguousTimeoutException;
import com.couchbase.client.core.error.UnambiguousTimeoutException;
import com.couchbase.client.java.analytics.AnalyticsOptions;
import com.couchbase.client.java.kv.ExistsOptions;
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
import com.couchbase.client.java.kv.GetOptions;
import com.couchbase.client.java.kv.InsertOptions;
import com.couchbase.client.java.kv.RemoveOptions;
import com.couchbase.client.java.kv.ReplaceOptions;
import com.couchbase.client.java.kv.UpsertOptions;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* Query tests Theses tests rely on a cb server running This class tests collection support with
* inCollection(collection) It should be identical to CouchbaseTemplateQueryIntegrationTests except for the setup and
* the inCollection(collectionName) calls. Testing without collections could also be done by this class simply by using
* scopeName = null and collectionName = null (except for inCollection() checks that the collectionName is not null)
* inCollection(collection), inScope(scope) and withOptions(options). Testing without collections could also be done by
* this class simply by using scopeName = null and collectionName = null
*
* @author Michael Reiche
*/
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests {
Airport vie = new Airport("airports::vie", "vie", "loww");
@BeforeAll
public static void beforeAll() {
// first call the super method
callSuperBeforeAll(new Object() {});
// then do processing for this class
// collectionName = null;
// scopeName = null;
// no-op
}
@AfterAll
@@ -87,15 +102,26 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
super.beforeEach();
// then do processing for this class
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inCollection(collectionName).all();
couchbaseTemplate.removeByQuery(Airport.class).inScope(scopeName).inCollection(collectionName).all();
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName)
.inCollection(collectionName).all();
couchbaseTemplate.removeByQuery(Airport.class).inScope(otherScope).inCollection(otherCollection).all();
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope)
.inCollection(otherCollection).all();
}
@AfterEach
@Override
public void afterEach() {
// first call the super method
// first do processing for this class
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
// query with REQUEST_PLUS to ensure that the remove has completed.
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inCollection(collectionName).all();
// then call the super method
super.afterEach();
// then do processing for this class
// no-op
}
@Test
@@ -200,6 +226,9 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
.matching(specialUsers).all().collectList().block();
assertEquals(1, foundUsersReactive.size());
couchbaseTemplate.removeByQuery(UserSubmission.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
couchbaseTemplate.removeByQuery(UserSubmission.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
}
@Test
@@ -207,16 +236,20 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2));
couchbaseTemplate.upsertById(User.class).inScope(scopeName).inCollection(collectionName)
.all(Arrays.asList(user1, user2));
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(user1.getId()));
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(user2.getId()));
assertTrue(couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName).one(user1.getId()));
assertTrue(couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName).one(user2.getId()));
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inCollection(collectionName).all();
List<RemoveResult> result = couchbaseTemplate.removeByQuery(User.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
assertEquals(2, result.size(), "should have deleted user1 and user2");
assertNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user1.getId()));
assertNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user2.getId()));
assertNull(
couchbaseTemplate.findById(User.class).inScope(scopeName).inCollection(collectionName).one(user1.getId()));
assertNull(
couchbaseTemplate.findById(User.class).inScope(scopeName).inCollection(collectionName).one(user2.getId()));
}
@@ -315,7 +348,7 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
// count( distinct icao )
// not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
Long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count()
.block();
assertEquals(2, count1);
@@ -336,4 +369,387 @@ class CouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIn
}
}
/**
* find . -name 'Exec*OperationSupport.java'|awk -F/ '{print $NF}'|sort| awk -F. '{print "* ", NR, ")",$1, ""}'<br>
* 1) ExecutableExistsByIdOperationSupport <br>
* 2) ExecutableFindByAnalyticsOperationSupport <br>
* 3) ExecutableFindByIdOperationSupport <br>
* 4) ExecutableFindByQueryOperationSupport <br>
* 5) ExecutableFindFromReplicasByIdOperationSupport <br>
* 6) ExecutableInsertByIdOperationSupport <br>
* 7) ExecutableRemoveByIdOperationSupport <br>
* 8) ExecutableRemoveByQueryOperationSupport <br>
* 9) ExecutableReplaceByIdOperationSupport <br>
* 10)ExecutableUpsertByIdOperationSupport <br>
*/
@Test
public void existsById() { // 1
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
ExistsOptions existsOptions = ExistsOptions.existsOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.one(vie);
try {
Boolean exists = couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName)
.withOptions(existsOptions).one(saved.getId());
assertTrue(exists, "Airport should exist: " + saved.getId());
} finally {
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
}
}
@Test
@Disabled // needs analytics data set
public void findByAnalytics() { // 2
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.one(vie);
try {
List<Airport> found = couchbaseTemplate.findByAnalytics(Airport.class).inScope(scopeName)
.inCollection(collectionName).withOptions(options).all();
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
}
}
@Test
public void findById() { // 3
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.one(vie);
try {
Airport found = couchbaseTemplate.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(saved.getId());
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
}
}
@Test
public void findByQuery() { // 4
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.one(vie);
try {
List<Airport> found = couchbaseTemplate.findByQuery(Airport.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName)
.withOptions(options).all();
assertEquals(saved.getId(), found.get(0).getId());
} finally {
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
}
}
@Test
public void findFromReplicasById() { // 5
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.one(vie);
try {
Airport found = couchbaseTemplate.findFromReplicasById(Airport.class).inScope(scopeName)
.inCollection(collectionName).withOptions(options).any(saved.getId());
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
}
}
@Test
public void insertById() { // 6
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(vie.withId(UUID.randomUUID().toString()));
try {
Airport found = couchbaseTemplate.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(getOptions).one(saved.getId());
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
}
}
@Test
public void removeById() { // 7
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.one(vie);
RemoveResult removeResult = couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(saved.getId());
assertEquals(saved.getId(), removeResult.getId());
}
@Test
public void removeByQuery() { // 8
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.one(vie);
List<RemoveResult> removeResults = couchbaseTemplate.removeByQuery(Airport.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName)
.withOptions(options).matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all();
assertEquals(saved.getId(), removeResults.get(0).getId());
}
@Test
public void replaceById() { // 9
InsertOptions insertOptions = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(insertOptions).one(vie);
Airport replaced = couchbaseTemplate.replaceById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(vie.withIcao("newIcao"));
try {
Airport found = couchbaseTemplate.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(getOptions).one(saved.getId());
assertEquals(replaced, found);
} finally {
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
}
}
@Test
public void upsertById() { // 10
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.upsertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(vie);
try {
Airport found = couchbaseTemplate.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(getOptions).one(saved.getId());
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId());
}
}
@Test
public void existsByIdOther() { // 1
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
ExistsOptions existsOptions = ExistsOptions.existsOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
try {
Boolean exists = couchbaseTemplate.existsById().inScope(otherScope).inCollection(otherCollection)
.withOptions(existsOptions).one(saved.getId());
assertTrue(exists, "Airport should exist: " + saved.getId());
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
@Disabled // needs analytics data set
public void findByAnalyticsOther() { // 2
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
try {
List<Airport> found = couchbaseTemplate.findByAnalytics(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).all();
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
public void findByIdOther() { // 3
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
try {
Airport found = couchbaseTemplate.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(saved.getId());
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
public void findByQueryOther() { // 4
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
try {
List<Airport> found = couchbaseTemplate.findByQuery(Airport.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).all();
assertEquals(saved.getId(), found.get(0).getId());
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
public void findFromReplicasByIdOther() { // 5
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
try {
Airport found = couchbaseTemplate.findFromReplicasById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).any(saved.getId());
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
public void insertByIdOther() { // 6
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(vie.withId(UUID.randomUUID().toString()));
try {
Airport found = couchbaseTemplate.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(getOptions).one(saved.getId());
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
public void removeByIdOther() { // 7
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
RemoveResult removeResult = couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(saved.getId());
assertEquals(saved.getId(), removeResult.getId());
}
@Test
public void removeByQueryOther() { // 8
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
List<RemoveResult> removeResults = couchbaseTemplate.removeByQuery(Airport.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all();
assertEquals(saved.getId(), removeResults.get(0).getId());
}
@Test
public void replaceByIdOther() { // 9
InsertOptions insertOptions = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(insertOptions).one(vie);
Airport replaced = couchbaseTemplate.replaceById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(vie.withIcao("newIcao"));
try {
Airport found = couchbaseTemplate.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(getOptions).one(saved.getId());
assertEquals(replaced, found);
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
public void upsertByIdOther() { // 10
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = couchbaseTemplate.upsertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(vie);
try {
Airport found = couchbaseTemplate.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(getOptions).one(saved.getId());
assertEquals(saved, found);
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
public void existsByIdOptions() { // 1 - Options
ExistsOptions options = ExistsOptions.existsOptions().timeout(Duration.ofNanos(10));
assertThrows(UnambiguousTimeoutException.class, () -> couchbaseTemplate.existsById().inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.getId()));
}
@Test
@Disabled // needs analytics data set
public void findByAnalyticsOptions() { // 2
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.findByAnalytics(Airport.class)
.inScope(otherScope).inCollection(otherCollection).withOptions(options).all());
}
@Test
public void findByIdOptions() { // 3
GetOptions options = GetOptions.getOptions().timeout(Duration.ofNanos(10));
assertThrows(UnambiguousTimeoutException.class, () -> couchbaseTemplate.findById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.getId()));
}
@Test
public void findByQueryOptions() { // 4
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class,
() -> couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inScope(otherScope).inCollection(otherCollection).withOptions(options).all());
}
@Test
public void findFromReplicasByIdOptions() { // 5
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofNanos(1000));
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
try {
Airport found = couchbaseTemplate.findFromReplicasById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).any(saved.getId());
assertNull(found, "should not have found document in short timeout");
} finally {
couchbaseTemplate.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId());
}
}
@Test
public void insertByIdOptions() { // 6
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.insertById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.withId(UUID.randomUUID().toString())));
}
@Test
public void removeByIdOptions() { // 7 - options
Airport saved = couchbaseTemplate.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.one(vie);
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.removeById().inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.getId()));
}
@Test
public void removeByQueryOptions() { // 8 - options
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class,
() -> couchbaseTemplate.removeByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inScope(otherScope).inCollection(otherCollection).withOptions(options)
.matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all());
}
@Test
public void replaceByIdOptions() { // 9 - options
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.replaceById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.withIcao("newIcao")));
}
@Test
public void upsertByIdOptions() { // 10 - options
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> couchbaseTemplate.upsertById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie));
}
}

View File

@@ -20,8 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE;
import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
import java.time.Instant;
@@ -33,13 +31,10 @@ import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteria;
import org.springframework.data.couchbase.domain.Address;
import org.springframework.data.couchbase.domain.Airport;
import org.springframework.data.couchbase.domain.Config;
import org.springframework.data.couchbase.domain.Course;
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
import org.springframework.data.couchbase.domain.Submission;
@@ -69,11 +64,15 @@ class CouchbaseTemplateQueryIntegrationTests extends JavaIntegrationTests {
@BeforeEach
@Override
public void beforeEach() {
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE);
super.beforeEach();
// already setup by JavaIntegrationTests.beforeAll()
// ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
// reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE);
// ensure each test starts with clean state
couchbaseTemplate.removeByQuery(User.class).all();
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
}
@Test
@@ -108,13 +107,13 @@ class CouchbaseTemplateQueryIntegrationTests extends JavaIntegrationTests {
couchbaseTemplate.findById(User.class).one(user1.getId());
reactiveCouchbaseTemplate.findById(User.class).one(user1.getId()).block();
} finally {
couchbaseTemplate.removeByQuery(User.class).all();
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
}
User usery = couchbaseTemplate.findById(User.class).one("userx");
assertNull(usery, "usery should be null");
User userz = reactiveCouchbaseTemplate.findById(User.class).one("userx").block();
assertNull(userz, "uz should be null");
User usery = couchbaseTemplate.findById(User.class).one("user1");
assertNull(usery, "user1 should have been deleted");
User userz = reactiveCouchbaseTemplate.findById(User.class).one("user2").block();
assertNull(userz, "user2 should have been deleted");
}
@@ -136,6 +135,8 @@ class CouchbaseTemplateQueryIntegrationTests extends JavaIntegrationTests {
@Test
void findByMatchingQueryProjected() {
couchbaseTemplate.removeByQuery(UserSubmission.class).all();
UserSubmission user = new UserSubmission();
user.setId(UUID.randomUUID().toString());
user.setUsername("dave");

View File

@@ -224,7 +224,7 @@ class QueryCriteriaTests {
@Test
void testIn() {
String[] args = new String[] { "gump", "davis" };
QueryCriteria c = where(i("name")).in(args);
QueryCriteria c = where(i("name")).in((Object)args);
assertEquals("`name` in ( [\"gump\",\"davis\"] )", c.export());
JsonArray parameters = JsonArray.create();
assertEquals("`name` in ( $1 )", c.export(new int[1], parameters, null));
@@ -234,7 +234,7 @@ class QueryCriteriaTests {
@Test
void testNotIn() {
String[] args = new String[] { "gump", "davis" };
QueryCriteria c = where(i("name")).notIn(args);
QueryCriteria c = where(i("name")).notIn((Object)args);
assertEquals("not( (`name` in ( [\"gump\",\"davis\"] )) )", c.export());
JsonArray parameters = JsonArray.create();
assertEquals("not( (`name` in ( $1 )) )", c.export(new int[1], parameters, null));

View File

@@ -0,0 +1,745 @@
/*
* Copyright 2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core.query;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.RemoveResult;
import org.springframework.data.couchbase.domain.Address;
import org.springframework.data.couchbase.domain.Airport;
import org.springframework.data.couchbase.domain.Course;
import org.springframework.data.couchbase.domain.NaiveAuditorAware;
import org.springframework.data.couchbase.domain.Submission;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserJustLastName;
import org.springframework.data.couchbase.domain.UserSubmission;
import org.springframework.data.couchbase.domain.UserSubmissionProjected;
import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider;
import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.CollectionAwareIntegrationTests;
import org.springframework.data.couchbase.util.IgnoreWhen;
import com.couchbase.client.core.error.AmbiguousTimeoutException;
import com.couchbase.client.core.error.UnambiguousTimeoutException;
import com.couchbase.client.java.analytics.AnalyticsOptions;
import com.couchbase.client.java.kv.ExistsOptions;
import com.couchbase.client.java.kv.GetAnyReplicaOptions;
import com.couchbase.client.java.kv.GetOptions;
import com.couchbase.client.java.kv.InsertOptions;
import com.couchbase.client.java.kv.RemoveOptions;
import com.couchbase.client.java.kv.ReplaceOptions;
import com.couchbase.client.java.kv.UpsertOptions;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* Query tests Theses tests rely on a cb server running This class tests collection support with
* inCollection(collection), inScope(scope) and withOptions(options). Testing without collections could also be done by
* this class simply by using scopeName = null and collectionName = null
*
* @author Michael Reiche
*/
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
class ReactiveCouchbaseTemplateQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests {
Airport vie = new Airport("airports::vie", "vie", "low7");
ReactiveCouchbaseTemplate template = reactiveCouchbaseTemplate;
@BeforeAll
public static void beforeAll() {
// first call the super method
callSuperBeforeAll(new Object() {});
// then do processing for this class
// no-op
}
@AfterAll
public static void afterAll() {
// first do the processing for this class
// no-op
// then call the super method
callSuperAfterAll(new Object() {});
}
@BeforeEach
@Override
public void beforeEach() {
// first call the super method
super.beforeEach();
// then do processing for this class
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inCollection(collectionName).all();
couchbaseTemplate.removeByQuery(Airport.class).inScope(scopeName).inCollection(collectionName).all();
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName)
.inCollection(collectionName).all();
couchbaseTemplate.removeByQuery(Airport.class).inScope(otherScope).inCollection(otherCollection).all();
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope)
.inCollection(otherCollection).all();
}
@AfterEach
@Override
public void afterEach() {
// first do processing for this class
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
// query with REQUEST_PLUS to ensure that the remove has completed.
couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inCollection(collectionName).all();
// then call the super method
super.afterEach();
}
@Test
void findByQueryAll() {
try {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2));
final List<User> foundUsers = couchbaseTemplate.findByQuery(User.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
for (User u : foundUsers) {
if (!(u.equals(user1) || u.equals(user2))) {
// somebody didn't clean up after themselves.
couchbaseTemplate.removeById().inCollection(collectionName).one(u.getId());
}
}
assertEquals(2, foundUsers.size());
TemporalAccessor auditTime = new AuditingDateTimeProvider().getNow().get();
long auditMillis = Instant.from(auditTime).toEpochMilli();
String auditUser = new NaiveAuditorAware().getCurrentAuditor().get();
for (User u : foundUsers) {
assertTrue(u.equals(user1) || u.equals(user2));
assertEquals(auditUser, u.getCreator());
assertEquals(auditMillis, u.getCreatedDate());
assertEquals(auditUser, u.getLastModifiedBy());
assertEquals(auditMillis, u.getLastModifiedDate());
}
couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user1.getId());
reactiveCouchbaseTemplate.findById(User.class).inCollection(collectionName).one(user1.getId()).block();
} finally {
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
}
User usery = couchbaseTemplate.findById(User.class).inCollection(collectionName).one("userx");
assertNull(usery, "usery should be null");
User userz = reactiveCouchbaseTemplate.findById(User.class).inCollection(collectionName).one("userx").block();
assertNull(userz, "userz should be null");
}
@Test
void findByMatchingQuery() {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
User specialUser = new User(UUID.randomUUID().toString(), "special", "special");
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2, specialUser));
Query specialUsers = new Query(QueryCriteria.where("firstname").like("special"));
final List<User> foundUsers = couchbaseTemplate.findByQuery(User.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).matching(specialUsers).all();
assertEquals(1, foundUsers.size());
}
@Test
void findByMatchingQueryProjected() {
UserSubmission user = new UserSubmission();
user.setId(UUID.randomUUID().toString());
user.setUsername("dave");
user.setRoles(Arrays.asList("role1", "role2"));
Address address = new Address();
address.setStreet("1234 Olcott Street");
user.setAddress(address);
user.setSubmissions(
Arrays.asList(new Submission(UUID.randomUUID().toString(), user.getId(), "tid", "status", 123)));
user.setCourses(Arrays.asList(new Course(UUID.randomUUID().toString(), user.getId(), "581"),
new Course(UUID.randomUUID().toString(), user.getId(), "777")));
couchbaseTemplate.upsertById(UserSubmission.class).inCollection(collectionName).one(user);
Query daveUsers = new Query(QueryCriteria.where("username").like("dave"));
final List<UserSubmissionProjected> foundUserSubmissions = couchbaseTemplate.findByQuery(UserSubmission.class)
.as(UserSubmissionProjected.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inCollection(collectionName).matching(daveUsers).all();
assertEquals(1, foundUserSubmissions.size());
assertEquals(user.getUsername(), foundUserSubmissions.get(0).getUsername());
assertEquals(user.getId(), foundUserSubmissions.get(0).getId());
assertEquals(user.getCourses(), foundUserSubmissions.get(0).getCourses());
assertEquals(user.getAddress(), foundUserSubmissions.get(0).getAddress());
couchbaseTemplate.removeByQuery(UserSubmission.class).inCollection(collectionName).all();
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
User specialUser = new User(UUID.randomUUID().toString(), "special", "special");
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2, specialUser));
Query specialUsers = new Query(QueryCriteria.where("firstname").like("special"));
final List<UserJustLastName> foundUsers = couchbaseTemplate.findByQuery(User.class).as(UserJustLastName.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).matching(specialUsers).all();
assertEquals(1, foundUsers.size());
final List<UserJustLastName> foundUsersReactive = reactiveCouchbaseTemplate.findByQuery(User.class)
.as(UserJustLastName.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName)
.matching(specialUsers).all().collectList().block();
assertEquals(1, foundUsersReactive.size());
}
@Test
void removeByQueryAll() {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
couchbaseTemplate.upsertById(User.class).inScope(scopeName).inCollection(collectionName)
.all(Arrays.asList(user1, user2));
assertTrue(couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName).one(user1.getId()));
assertTrue(couchbaseTemplate.existsById().inScope(scopeName).inCollection(collectionName).one(user2.getId()));
List<RemoveResult> result = couchbaseTemplate.removeByQuery(User.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
assertEquals(2, result.size(), "should have deleted user1 and user2");
assertNull(
couchbaseTemplate.findById(User.class).inScope(scopeName).inCollection(collectionName).one(user1.getId()));
assertNull(
couchbaseTemplate.findById(User.class).inScope(scopeName).inCollection(collectionName).one(user2.getId()));
}
@Test
void removeByMatchingQuery() {
User user1 = new User(UUID.randomUUID().toString(), "user1", "user1");
User user2 = new User(UUID.randomUUID().toString(), "user2", "user2");
User specialUser = new User(UUID.randomUUID().toString(), "special", "special");
couchbaseTemplate.upsertById(User.class).inCollection(collectionName).all(Arrays.asList(user1, user2, specialUser));
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(user1.getId()));
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(user2.getId()));
assertTrue(couchbaseTemplate.existsById().inCollection(collectionName).one(specialUser.getId()));
Query nonSpecialUsers = new Query(QueryCriteria.where("firstname").notLike("special"));
couchbaseTemplate.removeByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inCollection(collectionName).matching(nonSpecialUsers).all();
assertNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user1.getId()));
assertNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(user2.getId()));
assertNotNull(couchbaseTemplate.findById(User.class).inCollection(collectionName).one(specialUser.getId()));
}
@Test
void distinct() {
String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" };
String[] icaos = { "ic0", "ic1", "ic0", "ic1", "ic0", "ic1", "ic0" };
try {
for (int i = 0; i < iatas.length; i++) {
Airport airport = new Airport("airports::" + iatas[i], iatas[i] /*iata*/, icaos[i] /* icao */);
couchbaseTemplate.insertById(Airport.class).inCollection(collectionName).one(airport);
}
// distinct and count(distinct(...)) calls. use as() and consistentWith to verify fluent api
// as the fluent api for Distinct is tricky
// distinct icao
List<Airport> airports1 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
assertEquals(2, airports1.size());
// distinct all-fields-in-Airport.class
List<Airport> airports2 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(Airport.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all();
assertEquals(7, airports2.size());
// count( distinct { iata, icao } )
long count1 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "iata", "icao" })
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count();
assertEquals(7, count1);
// count( distinct (all fields in icaoClass)
Class icaoClass = (new Object() {
String iata;
String icao;
}).getClass();
long count2 = couchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(icaoClass)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count();
assertEquals(7, count2);
} finally {
couchbaseTemplate.removeById().inCollection(collectionName)
.all(Arrays.stream(iatas).map((iata) -> "airports::" + iata).collect(Collectors.toSet()));
}
}
@Test
void distinctReactive() {
String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" };
String[] icaos = { "ic0", "ic1", "ic0", "ic1", "ic0", "ic1", "ic0" };
try {
for (int i = 0; i < iatas.length; i++) {
Airport airport = new Airport("airports::" + iatas[i], iatas[i] /*iata*/, icaos[i] /* icao */);
reactiveCouchbaseTemplate.insertById(Airport.class).inCollection(collectionName).one(airport).block();
}
// distinct and count(distinct(...)) calls. use as() and consistentWith to verify fluent api
// as the fluent api for Distinct is tricky
// distinct icao
List<Airport> airports1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all()
.collectList().block();
assertEquals(2, airports1.size());
// distinct all-fields-in-Airport.class
List<Airport> airports2 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {})
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).all()
.collectList().block();
assertEquals(7, airports2.size());
// count( distinct icao )
// not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
Long count1 = reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] { "icao" })
.as(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count()
.block();
assertEquals(2, count1);
// count( distinct (all fields in icaoClass) // which only has one field
// not currently possible to have multiple fields in COUNT(DISTINCT field1, field2, ... ) due to MB43475
Class icaoClass = (new Object() {
String icao;
}).getClass();
long count2 = (long) reactiveCouchbaseTemplate.findByQuery(Airport.class).distinct(new String[] {}).as(icaoClass)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inCollection(collectionName).count().block();
assertEquals(2, count2);
} finally {
reactiveCouchbaseTemplate.removeById().inCollection(collectionName)
.all(Arrays.stream(iatas).map((iata) -> "airports::" + iata).collect(Collectors.toSet())).collectList()
.block();
}
}
/**
* find . -name 'Exec*OperationSupport.java'|awk -F/ '{print $NF}'|sort| awk -F. '{print "* ", NR, ")",$1, ""}'<br>
* 1) ExecutableExistsByIdOperationSupport <br>
* 2) ExecutableFindByAnalyticsOperationSupport <br>
* 3) ExecutableFindByIdOperationSupport <br>
* 4) ExecutableFindByQueryOperationSupport <br>
* 5) ExecutableFindFromReplicasByIdOperationSupport <br>
* 6) ExecutableInsertByIdOperationSupport <br>
* 7) ExecutableRemoveByIdOperationSupport <br>
* 8) ExecutableRemoveByQueryOperationSupport <br>
* 9) ExecutableReplaceByIdOperationSupport <br>
* 10)ExecutableUpsertByIdOperationSupport <br>
*/
@Test
public void existsById() { // 1
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
ExistsOptions existsOptions = ExistsOptions.existsOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("low7")).block();
try {
Boolean exists = template.existsById().inScope(scopeName).inCollection(collectionName).withOptions(existsOptions)
.one(saved.getId()).block();
assertTrue(exists, "Airport should exist: " + saved.getId());
} finally {
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
}
}
@Test
@Disabled // needs analytics data set
public void findByAnalytics() { // 2
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("low8")).block();
try {
List<Airport> found = template.findByAnalytics(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).all().collectList().block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
}
}
@Test
public void findById() { // 3
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("low9")).block();
try {
Airport found = template.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(saved.getId()).block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
}
}
@Test
public void findByQuery() { // 4
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("lowa")).block();
try {
List<Airport> found = template.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inScope(scopeName).inCollection(collectionName).withOptions(options).all().collectList().block();
assertEquals(saved.getId(), found.get(0).getId());
} finally {
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
}
}
@Test
public void findFromReplicasById() { // 5
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("lowb")).block();
try {
Airport found = template.findFromReplicasById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).any(saved.getId()).block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
}
}
@Test
public void insertById() { // 6
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(vie.withIcao("lowc").withId(UUID.randomUUID().toString())).block();
try {
Airport found = template.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(getOptions).one(saved.getId()).block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
}
}
@Test
public void removeById() { // 7
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("lowd")).block();
RemoveResult removeResult = template.removeById().inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(saved.getId()).block();
assertEquals(saved.getId(), removeResult.getId());
}
@Test
public void removeByQuery() { // 8
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName).one(vie.withIcao("lowe")).block();
List<RemoveResult> removeResults = template.removeByQuery(Airport.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(scopeName).inCollection(collectionName)
.withOptions(options).matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all().collectList()
.block();
assertEquals(saved.getId(), removeResults.get(0).getId());
}
@Test
public void replaceById() { // 9
InsertOptions insertOptions = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(insertOptions).one(vie.withIcao("lowe")).block();
Airport replaced = template.replaceById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(vie.withIcao("newIcao")).block();
try {
Airport found = template.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(getOptions).one(saved.getId()).block();
assertEquals(replaced, found);
} finally {
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
}
}
@Test
public void upsertById() { // 10
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.upsertById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(options).one(vie.withIcao("lowf")).block();
try {
Airport found = template.findById(Airport.class).inScope(scopeName).inCollection(collectionName)
.withOptions(getOptions).one(saved.getId()).block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(scopeName).inCollection(collectionName).one(saved.getId()).block();
}
}
@Test
public void existsByIdOther() { // 1
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
ExistsOptions existsOptions = ExistsOptions.existsOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowg"))
.block();
try {
Boolean exists = template.existsById().inScope(otherScope).inCollection(otherCollection)
.withOptions(existsOptions).one(saved.getId()).block();
assertTrue(exists, "Airport should exist: " + saved.getId());
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
@Disabled // needs analytics data set
public void findByAnalyticsOther() { // 2
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowh"))
.block();
try {
List<Airport> found = template.findByAnalytics(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).all().collectList().block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
public void findByIdOther() { // 3
GetOptions options = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowi"))
.block();
try {
Airport found = template.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(saved.getId()).block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
public void findByQueryOther() { // 4
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowj"))
.block();
try {
List<Airport> found = template.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inScope(otherScope).inCollection(otherCollection).withOptions(options).all().collectList().block();
assertEquals(saved.getId(), found.get(0).getId());
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
public void findFromReplicasByIdOther() { // 5
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowk"))
.block();
try {
Airport found = template.findFromReplicasById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).any(saved.getId()).block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
public void insertByIdOther() { // 6
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(vie.withIcao("lowl").withId(UUID.randomUUID().toString())).block();
try {
Airport found = template.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(getOptions).one(saved.getId()).block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
public void removeByIdOther() { // 7
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lowm"))
.block();
RemoveResult removeResult = template.removeById().inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(saved.getId()).block();
assertEquals(saved.getId(), removeResult.getId());
}
@Test
public void removeByQueryOther() { // 8
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie.withIcao("lown"))
.block();
List<RemoveResult> removeResults = template.removeByQuery(Airport.class)
.withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all().collectList()
.block();
assertEquals(saved.getId(), removeResults.get(0).getId());
}
@Test
public void replaceByIdOther() { // 9
InsertOptions insertOptions = InsertOptions.insertOptions().timeout(Duration.ofSeconds(10));
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(insertOptions).one(vie.withIcao("lown")).block();
Airport replaced = template.replaceById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(vie.withIcao("newIcao")).block();
try {
Airport found = template.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(getOptions).one(saved.getId()).block();
assertEquals(replaced, found);
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
public void upsertByIdOther() { // 10
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofSeconds(10));
GetOptions getOptions = GetOptions.getOptions().timeout(Duration.ofSeconds(10));
Airport saved = template.upsertById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).one(vie.withIcao("lowo")).block();
try {
Airport found = template.findById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(getOptions).one(saved.getId()).block();
assertEquals(saved, found);
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
public void existsByIdOptions() { // 1 - Options
ExistsOptions options = ExistsOptions.existsOptions().timeout(Duration.ofNanos(10));
assertThrows(UnambiguousTimeoutException.class, () -> template.existsById().inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.getId()).block());
}
@Test
@Disabled // needs analytics data set
public void findByAnalyticsOptions() { // 2
AnalyticsOptions options = AnalyticsOptions.analyticsOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> template.findByAnalytics(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).all().collectList().block());
}
@Test
public void findByIdOptions() { // 3
GetOptions options = GetOptions.getOptions().timeout(Duration.ofNanos(10));
assertThrows(UnambiguousTimeoutException.class, () -> template.findById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.getId()).block());
}
@Test
public void findByQueryOptions() { // 4
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class,
() -> template.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).all().collectList().block());
}
@Test
public void findFromReplicasByIdOptions() { // 5
GetAnyReplicaOptions options = GetAnyReplicaOptions.getAnyReplicaOptions().timeout(Duration.ofNanos(1000));
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie)
.block();
try {
Airport found = template.findFromReplicasById(Airport.class).inScope(otherScope).inCollection(otherCollection)
.withOptions(options).any(saved.getId()).block();
assertNull(found, "should not have found document in short timeout");
} finally {
template.removeById().inScope(otherScope).inCollection(otherCollection).one(saved.getId()).block();
}
}
@Test
public void insertByIdOptions() { // 6
InsertOptions options = InsertOptions.insertOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> template.insertById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.withId(UUID.randomUUID().toString())).block());
}
@Test
public void removeByIdOptions() { // 7 - options
Airport saved = template.insertById(Airport.class).inScope(otherScope).inCollection(otherCollection).one(vie)
.block();
RemoveOptions options = RemoveOptions.removeOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> template.removeById().inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.getId()).block());
}
@Test
public void removeByQueryOptions() { // 8 - options
QueryOptions options = QueryOptions.queryOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class,
() -> template.removeByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inScope(otherScope).inCollection(otherCollection).withOptions(options)
.matching(Query.query(QueryCriteria.where("iata").is(vie.getIata()))).all().collectList().block());
}
@Test
public void replaceByIdOptions() { // 9 - options
ReplaceOptions options = ReplaceOptions.replaceOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> template.replaceById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie.withIcao("newIcao")).block());
}
@Test
public void upsertByIdOptions() { // 10 - options
UpsertOptions options = UpsertOptions.upsertOptions().timeout(Duration.ofNanos(10));
assertThrows(AmbiguousTimeoutException.class, () -> template.upsertById(Airport.class).inScope(otherScope)
.inCollection(otherCollection).withOptions(options).one(vie).block());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@ import com.couchbase.client.java.json.JacksonTransformers;
*/
@Configuration
@EnableCouchbaseRepositories
@EnableCouchbaseAuditing(auditorAwareRef="auditorAwareRef", dateTimeProviderRef="dateTimeProviderRef") // this activates auditing
@EnableCouchbaseAuditing(auditorAwareRef = "auditorAwareRef", dateTimeProviderRef = "dateTimeProviderRef")
public class Config extends AbstractCouchbaseConfiguration {
String bucketname = "travel-sample";
String username = "Administrator";
@@ -205,4 +205,15 @@ public class Config extends AbstractCouchbaseConfiguration {
return "t"; // this will override '_class', is passed in to new CustomMappingCouchbaseConverter
}
static String scopeName = null;
@Override
protected String getScopeName() {
return scopeName;
}
public static void setScopeName(String scopeName) {
Config.scopeName = scopeName;
}
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright 2012-2020 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.domain;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
/**
* Configuration that uses a scope. This is a separate class as it is difficult to debug if you forget to unset the
* scopeName and the config is used for non-collection operations.
*
* @Author Michael Reiche
*/
@Configuration
@EnableCouchbaseRepositories
public class ConfigScoped extends Config {
static String scopeName = null;
@Override
protected String getScopeName() {
return scopeName;
}
public static void setScopeName(String scopeName) {
ConfigScoped.scopeName = scopeName;
}
}

View File

@@ -1,7 +1,21 @@
/*
* Copyright 2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.domain;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
@@ -9,13 +23,16 @@ import org.springframework.data.mapping.context.MappingContext;
public class CustomMappingCouchbaseConverter extends MappingCouchbaseConverter {
/**
* this constructer creates a TypeBasedCouchbaseTypeMapper with the specified typeKey
* while MappingCouchbaseConverter uses a DefaultCouchbaseTypeMapper
* typeMapper = new DefaultCouchbaseTypeMapper(typeKey != null ? typeKey : TYPEKEY_DEFAULT);
* this constructer creates a TypeBasedCouchbaseTypeMapper with the specified typeKey while MappingCouchbaseConverter
* uses a DefaultCouchbaseTypeMapper typeMapper = new DefaultCouchbaseTypeMapper(typeKey != null ? typeKey :
* TYPEKEY_DEFAULT);
*
* @param mappingContext
* @param typeKey - the typeKey to be used (normally "_class")
*/
public CustomMappingCouchbaseConverter(final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext, final String typeKey) {
public CustomMappingCouchbaseConverter(
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
final String typeKey) {
super(mappingContext, typeKey);
this.typeMapper = new TypeBasedCouchbaseTypeMapper(typeKey);
}

View File

@@ -0,0 +1,225 @@
package org.springframework.data.couchbase.domain;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryProfile;
import com.couchbase.client.java.query.QueryResult;
import com.couchbase.client.java.query.QueryScanConsistency;
import org.junit.jupiter.api.AfterAll;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.couchbase.config.BeanNames;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.RemoveResult;
import org.springframework.data.couchbase.util.Capabilities;
import org.springframework.data.couchbase.util.ClusterType;
import org.springframework.data.couchbase.util.IgnoreWhen;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.publisher.ParallelFlux;
import reactor.core.scheduler.Schedulers;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.repository.config.EnableReactiveCouchbaseRepositories;
import org.springframework.data.couchbase.util.JavaIntegrationTests;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.couchbase.client.java.Collection;
import com.couchbase.client.java.ReactiveCollection;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.kv.GetResult;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringJUnitConfig(FluxTest.Config.class)
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
public class FluxTest extends JavaIntegrationTests {
@BeforeAll
public static void beforeEverything() {
/**
* The couchbaseTemplate inherited from JavaIntegrationTests uses org.springframework.data.couchbase.domain.Config
* It has typeName = 't' (instead of _class). Don't use it.
*/
ApplicationContext ac = new AnnotationConfigApplicationContext(FluxTest.Config.class);
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(BeanNames.COUCHBASE_TEMPLATE);
reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(BeanNames.REACTIVE_COUCHBASE_TEMPLATE);
collection = couchbaseTemplate.getCouchbaseClientFactory().getBucket().defaultCollection();
rCollection = couchbaseTemplate.getCouchbaseClientFactory().getBucket().reactive().defaultCollection();
for (String k : keyList) {
couchbaseTemplate.getCouchbaseClientFactory().getBucket().defaultCollection().upsert(k,
JsonObject.create().put("x", k));
}
}
@AfterAll
public static void afterEverthing() {
couchbaseTemplate.removeByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
couchbaseTemplate.findByQuery(Airport.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).all();
}
@BeforeEach
@Override
public void beforeEach() {
super.beforeEach();
}
static List<String> keyList = Arrays.asList("a", "b", "c", "d", "e");
static Collection collection;
static ReactiveCollection rCollection;
@Autowired ReactiveAirportRepository airportRepository; // intellij flags "Could not Autowire", but it runs ok.
AtomicInteger rCat = new AtomicInteger(0);
AtomicInteger rFlat = new AtomicInteger(0);
@Test
public void concatMapCB() throws Exception {
System.out.println("Start concatMapCB");
System.out.println("\n******** Using concatMap() *********");
ParallelFlux<GetResult> concat = Flux.fromIterable(keyList).parallel(2).runOn(Schedulers.parallel())
.concatMap(item -> cbGet(item)
/* rCollection.get(item) */.doOnSubscribe((x) -> System.out.println(" +" + rCat.incrementAndGet()))
.doOnTerminate(() -> System.out.println(" -" + rCat.decrementAndGet())));
System.out.println(concat.sequential().collectList().block());
}
@Test
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
public void cbse() {
LinkedList<LinkedList<Airport>> listOfLists = new LinkedList<>();
Airport a = new Airport(UUID.randomUUID().toString(), "iata", "lowp");
String last = null;
for (int i = 0; i < 5; i++) {
LinkedList<Airport> list = new LinkedList<>();
for (int j = 0; j < 10; j++) {
list.add(a.withId(UUID.randomUUID().toString()));
last = a.getId();
}
listOfLists.add(list);
}
Flux<Object> af = Flux.fromIterable(listOfLists).concatMap(catalogToStore -> Flux.fromIterable(catalogToStore)
.parallel(4).runOn(Schedulers.parallel()).concatMap((entity) -> airportRepository.save(entity)));
List<Object> saved = af.collectList().block();
System.out.println("results.size() : " + saved.size());
String statement = "select * from `" + /*config().bucketname()*/ "_default" + "` where META().id >= '" + last + "'";
System.out.println("statement: " + statement);
try {
QueryResult qr = couchbaseTemplate.getCouchbaseClientFactory().getScope().query(statement,
QueryOptions.queryOptions().profile(QueryProfile.PHASES));
List<RemoveResult> rr = couchbaseTemplate.removeByQuery(Airport.class)
.withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS)).all();
System.out.println(qr.metaData().profile().get());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
List<Airport> airports = airportRepository.findAll().collectList().block();
assertEquals(0, airports.size(), "should have been all deleted");
}
@Test
public void flatMapCB() throws Exception {
System.out.println("Start flatMapCB");
ParallelFlux<GetResult> concat = Flux.fromIterable(keyList).parallel(2).runOn(Schedulers.parallel())
.flatMap(item -> cbGet(item) /* rCollection.get(item) */
.doOnSubscribe((x) -> System.out.println(" +" + rCat.incrementAndGet()))
.doOnTerminate(() -> System.out.println(" -" + rCat.decrementAndGet())));
System.out.println(concat.sequential().collectList().block());
}
@Test
public void flatMapSyncCB() throws Exception {
System.out.println("Start flatMapSyncCB");
System.out.println("\n******** Using flatSyncMap() *********");
ParallelFlux<GetResult> concat = Flux.fromIterable(keyList).parallel(2).runOn(Schedulers.parallel())
.flatMap(item -> Flux.just(cbGetSync(item) /* collection.get(item) */));
System.out.println(concat.sequential().collectList().block());
;
}
@Test
public void flatMapVsConcatMapCB2() throws Exception {
System.out.println("Start flatMapCB2");
System.out.println("\n******** Using flatMap() *********");
ParallelFlux<GetResult> flat = Flux.fromIterable(keyList).parallel(1).runOn(Schedulers.parallel())
.flatMap(item -> rCollection.get(item).doOnSubscribe((x) -> System.out.println(" +" + rCat.incrementAndGet()))
.doOnTerminate(() -> System.out.println(" -" + rCat.getAndDecrement())));
System.out.println(flat.sequential().collectList().block());
System.out.println("Start concatMapCB");
System.out.println("\n******** Using concatMap() *********");
ParallelFlux<GetResult> concat = Flux.fromIterable(keyList).parallel(2).runOn(Schedulers.parallel())
.concatMap(item -> cbGet(item).doOnSubscribe((x) -> System.out.println(" +" + rCat.incrementAndGet()))
.doOnTerminate(() -> System.out.println(" -" + rCat.getAndDecrement())));
System.out.println(concat.sequential().collectList().block());
;
}
static Random r = new Random();
static void sleep(long sleepMs) {
try {
int random = Math.abs(r.nextInt() % 1000);
Thread.sleep(sleepMs * random);
} catch (InterruptedException e) {}
}
AtomicInteger cbCount = new AtomicInteger();
Mono<GetResult> cbGet(String id) {
// System.out.println(" =" + id);
return rCollection.get(id);
}
GetResult cbGetSync(String id) {
// System.out.println(id + " +" + rCat.incrementAndGet());
GetResult result = collection.get(id);
// System.out.println(id + " -" + rCat.getAndDecrement());
return result;
}
static String tab(int len) {
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++)
sb.append(" ");
return sb.toString();
}
@Configuration
@EnableReactiveCouchbaseRepositories("org.springframework.data.couchbase")
static class Config extends AbstractCouchbaseConfiguration {
@Override
public String getConnectionString() {
return connectionString();
}
@Override
public String getUserName() {
return config().adminUsername();
}
@Override
public String getPassword() {
return config().adminPassword();
}
@Override
public String getBucketName() {
return bucketName();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package org.springframework.data.couchbase.domain;
import java.util.Optional;
import java.util.UUID;
import com.couchbase.client.core.deps.com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
@@ -53,6 +52,7 @@ public class Person extends AbstractEntity {
this();
setFirstname(firstname);
setLastname(lastname);
setMiddlename("Nick");
}
public Person(int id, String firstname, String lastname) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,15 +15,16 @@
*/
package org.springframework.data.couchbase.domain;
import com.couchbase.client.java.query.QueryScanConsistency;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.couchbase.repository.ScanConsistency;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* @author Michael Reiche
@@ -107,6 +108,9 @@ public interface PersonRepository extends CrudRepository<Person, String> {
void deleteAll();
@ScanConsistency(query=QueryScanConsistency.REQUEST_PLUS)
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
List<Person> findByAddressStreet(String street);
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
List<Person> findByMiddlename(String nickName);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 the original author or authors.
* Copyright 2017-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.data.couchbase.domain;
import org.springframework.data.couchbase.core.RemoveResult;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -30,6 +31,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
import org.springframework.stereotype.Repository;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
@@ -55,6 +57,15 @@ public interface ReactiveAirportRepository extends ReactiveSortingRepository<Air
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Flux<Airport> findAllByIata(String iata);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}")
Flux<Airport> findAllPoliciesByApplicableTypes(String state, JsonArray applicableTypes);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} and icao != $1 ORDER BY effectiveDateTime DESC LIMIT 1")
Mono<Airport> findPolicySnapshotByPolicyIdAndEffectiveDateTime(String policyId, long effectiveDateTime);
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} ORDER BY effectiveDateTime DESC")
Flux<Airport> findPolicySnapshotAll();
@Query("#{#n1ql.selectEntity} where iata = $1")
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Flux<Airport> getAllByIata(String iata);

View File

@@ -109,7 +109,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
void shouldSaveAndFindAll() {
Airport vie = null;
try {
vie = new Airport("airports::vie", "vie", "loww");
vie = new Airport("airports::vie", "vie", "low4");
airportRepository.save(vie);
List<Airport> all = new ArrayList<>();
airportRepository.findAll().forEach(all::add);
@@ -133,6 +133,22 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
personRepository.save(person);
List<Person> persons = personRepository.findByAddressStreet("Maple");
assertEquals(1, persons.size());
List<Person> persons2 = personRepository.findByMiddlename("Nick");
assertEquals(1, persons2.size());
} finally {
personRepository.deleteById(person.getId().toString());
}
}
@Test
void annotatedFieldFind() {
Person person = null;
try {
person = new Person(1, "first", "last");
person.setMiddlename("Nick"); // middlename is stored as nickname
personRepository.save(person);
List<Person> persons2 = personRepository.findByMiddlename("Nick");
assertEquals(1, persons2.size());
} finally {
personRepository.deleteById(person.getId().toString());
}
@@ -144,7 +160,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
Airport vie = null;
Airport xxx = null;
try {
vie = new Airport("airports::vie", "vie", "loww");
vie = new Airport("airports::vie", "vie", "low5");
airportRepository.save(vie);
xxx = new Airport("airports::xxx", "xxx", "xxxx");
airportRepository.save(xxx);
@@ -164,7 +180,7 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
void findBySimpleProperty() {
Airport vie = null;
try {
vie = new Airport("airports::vie", "vie", "loww");
vie = new Airport("airports::vie", "vie", "low6");
vie = airportRepository.save(vie);
List<Airport> airports = airportRepository.findAllByIata("vie");
assertEquals(1, airports.size());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2019 the original author or authors.
* Copyright 2017-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,11 +22,15 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.time.Instant;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -72,7 +76,7 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
Airport vie = null;
Airport jfk = null;
try {
vie = new Airport("airports::vie", "vie", "loww");
vie = new Airport("airports::vie", "vie", "low1");
airportRepository.save(vie).block();
jfk = new Airport("airports::jfk", "JFK", "xxxx");
airportRepository.save(jfk).block();
@@ -92,7 +96,7 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
void findBySimpleProperty() {
Airport vie = null;
try {
vie = new Airport("airports::vie", "vie", "loww");
vie = new Airport("airports::vie", "vie", "low2");
airportRepository.save(vie).block();
List<Airport> airports1 = airportRepository.findAllByIata("vie").collectList().block();
assertEquals(1, airports1.size());
@@ -121,6 +125,37 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
userRepository.delete(user).block();
}
@Test
void limitTest() {
Airport vie = new Airport("airports::vie", "vie", "low3");
Airport saved1 = airportRepository.save(vie).block();
Airport saved2 = airportRepository.save(vie.withId(UUID.randomUUID().toString())).block();
try {
airportRepository.findAll().collectList().block(); // findAll has QueryScanConsistency;
Mono<Airport> airport = airportRepository.findPolicySnapshotByPolicyIdAndEffectiveDateTime("any", 0);
System.out.println("------------------------------");
System.out.println(airport.block());
System.out.println("------------------------------");
Flux<Airport> airports = airportRepository.findPolicySnapshotAll();
System.out.println(airports.collectList().block());
System.out.println("------------------------------");
Mono<Airport> ap = getPolicyByIdAndEffectiveDateTime("x", Instant.now());
System.out.println(ap.block());
} finally {
airportRepository.delete(saved1).block();
airportRepository.delete(saved2).block();
}
}
public Mono<Airport> getPolicyByIdAndEffectiveDateTime(String policyId, Instant effectiveDateTime) {
return airportRepository
.findPolicySnapshotByPolicyIdAndEffectiveDateTime(policyId, effectiveDateTime.toEpochMilli())
// .map(Airport::getEntity)
.doOnError(
error -> System.out.println("MSG='Exception happened while retrieving Policy by Id and effectiveDateTime', "
+ "policyId={}, effectiveDateTime={}"));
}
@Test
void count() {
Set<String> iatas = new HashSet();

View File

@@ -32,6 +32,8 @@ import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.domain.Person;
import org.springframework.data.couchbase.domain.PersonRepository;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserRepository;
import org.springframework.data.mapping.context.MappingContext;
@@ -75,6 +77,19 @@ class N1qlQueryCreatorTests {
assertEquals(query.export(), " WHERE " + where(i("firstname")).is("Oliver").export());
}
@Test
void createsQueryFieldAnnotationCorrectly() throws Exception {
String input = "findByMiddlename";
PartTree tree = new PartTree(input, Person.class);
Method method = PersonRepository.class.getMethod(input, String.class);
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), null, converter,
bucketName);
Query query = creator.createQuery();
assertEquals(query.export(), " WHERE " + where(i("nickname")).is("Oliver").export());
}
@Test
void queryParametersArray() throws Exception {
String input = "findByFirstnameIn";
@@ -89,9 +104,9 @@ class N1qlQueryCreatorTests {
// Query expected = (new Query()).addCriteria(where("firstname").in("Oliver", "Charles"));
assertEquals(expected.export(new int[1]), query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(actualOptions);
expected.buildQueryOptions(null, null).build().injectParams(actualOptions);
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
}
@@ -111,9 +126,9 @@ class N1qlQueryCreatorTests {
Query expected = (new Query()).addCriteria(where(i("firstname")).in("Oliver", "Charles"));
assertEquals(expected.export(new int[1]), query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(actualOptions);
expected.buildQueryOptions(null, null).build().injectParams(actualOptions);
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
}
@@ -133,9 +148,9 @@ class N1qlQueryCreatorTests {
assertEquals(expected.export(new int[1]), query.export(new int[1]));
JsonObject expectedOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(expectedOptions);
expected.buildQueryOptions(null, null).build().injectParams(expectedOptions);
JsonObject actualOptions = JsonObject.create();
expected.buildQueryOptions(null).build().injectParams(actualOptions);
expected.buildQueryOptions(null, null).build().injectParams(actualOptions);
assertEquals(expectedOptions.removeKey("client_context_id"), actualOptions.removeKey("client_context_id"));
}

View File

@@ -30,7 +30,7 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.ExecutableFindByQuery;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
@@ -101,7 +101,7 @@ class StringN1qlQueryCreatorTests extends ClusterAwareIntegrationTests {
try {
Thread.sleep(3000);
} catch (Exception e) {}
ExecutableFindByQueryOperation.ExecutableFindByQuery q = (ExecutableFindByQueryOperation.ExecutableFindByQuery) couchbaseTemplate
ExecutableFindByQuery q = (ExecutableFindByQuery) couchbaseTemplate
.findByQuery(Airline.class).matching(query);
Optional<Airline> al = q.one();

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.couchbase.util;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Optional;
@@ -113,31 +114,57 @@ public abstract class ClusterAwareIntegrationTests {
public void afterEach() {}
/**
* This should probably be the first call in the @BeforeAll method of a test class.
* This will call super.beforeAll() when called as callSuperBeforeAll(new Object() {}); this trickery is necessary
* because super.beforeAll() cannot be used because it is a static method. it is possible and likely that the
* beforeAll() method of should still be called even when a test class defines its own beforeAll() method which would
* hide the beforeAll() of the super class.
* This trickery is not necessary for before/AfterEach, as those are not static methods
* This should probably be the first call in the @BeforeAll method of a test class. This will call super @BeforeAll
* methods when called as callSuperBeforeAll(new Object() {}); this trickery is necessary because super.beforeAll()
* cannot be used because it is a static method. it is possible and likely that the beforeAll() method of should still
* be called even when a test class defines its own beforeAll() method which would hide the beforeAll() of the super
* class. This trickery is not necessary for before/AfterEach, as those are not static methods
*
* @Author Michael Reiche
*
* @param createdHere - an object from a class defined in the calling class
*/
public static void callSuperBeforeAll(Object createdHere) {
callSuper(createdHere, "beforeAll");
callSuper(createdHere, BeforeAll.class);
}
// see comments for callSuperBeforeAll()
public static void callSuperAfterAll(Object createdHere) {
callSuper(createdHere, "afterAll");
callSuper(createdHere, AfterAll.class);
}
private static void callSuper(Object createdHere, String methodName) {
private static void callSuper(Object createdHere, Class annotationClass) {
try {
Method method = createdHere.getClass().getEnclosingClass().getSuperclass().getMethod(methodName);
method.invoke(null);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Class<?> encClass = createdHere.getClass().getEnclosingClass();
Class<?> theClass = encClass;
Annotation annotation = null;
Method invokedSuper = null;
if (annotationClass != BeforeAll.class && annotationClass != AfterAll.class) {
throw new RuntimeException("can only call super for BeforeAll and AfterAll " + annotationClass);
}
// look recursively for @BeforeAll or @AfterAll methods
// when one is found and executed, do not continue the recursive search
// as it is expected that the @BeforeAll or @AfterAll methods call
// any super methods explicitly - perhaps using callSuperBeforeAll() or callSuperAfterAll()
// Note that if the @BeforeAll and @AfterAll methods have different names, they will be
// called twice - once by this callSuper() mechanism and once by junit as the method will not be hidden
while ((theClass = theClass.getSuperclass()) != null) {
Method[] methods = theClass.getMethods();
for (Method m : methods) {
annotation = m.getAnnotation(annotationClass);
if (annotation != null) {
if (annotation != null) {
m.invoke(null);
invokedSuper = m;
}
}
}
if (invokedSuper != null) { // called method is responsible for calling any super methods
return;
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}

View File

@@ -19,6 +19,8 @@ import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMP
import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -34,7 +36,8 @@ import com.couchbase.client.java.Cluster;
import com.couchbase.client.java.ClusterOptions;
import com.couchbase.client.java.env.ClusterEnvironment;
import com.couchbase.client.java.manager.collection.CollectionManager;
import org.springframework.data.couchbase.domain.ConfigScoped;
import com.couchbase.client.java.manager.collection.CollectionSpec;
import com.couchbase.client.java.manager.collection.ScopeSpec;
/**
* Provides Collection support for integration tests
@@ -43,8 +46,10 @@ import org.springframework.data.couchbase.domain.ConfigScoped;
*/
public class CollectionAwareIntegrationTests extends JavaIntegrationTests {
public static String scopeName = "scope_" + randomString();
public static String collectionName = "collection_" + randomString();
public static String scopeName = "my_scope";// + randomString();
public static String otherScope = "other_scope";
public static String collectionName = "my_collection";// + randomString();
public static String otherCollection = "other_collection";// + randomString();
@BeforeAll
public static void beforeAll() {
@@ -57,20 +62,27 @@ public class CollectionAwareIntegrationTests extends JavaIntegrationTests {
waitForService(bucket, ServiceType.QUERY);
waitForQueryIndexerToHaveBucket(cluster, config().bucketname());
CollectionManager collectionManager = bucket.collections();
if (scopeName != null || collectionName != null) {
setupScopeCollection(cluster, scopeName, collectionName, collectionManager);
setupScopeCollection(cluster, scopeName, collectionName, collectionManager);
if (otherScope != null || otherCollection != null) {
// afterAll should be undoing the creation of scope etc
setupScopeCollection(cluster, otherScope, otherCollection, collectionManager);
}
ConfigScoped.setScopeName(scopeName);
ApplicationContext ac = new AnnotationConfigApplicationContext(ConfigScoped.class);
Config.setScopeName(scopeName);
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// the Config class has been modified, these need to be loaded again
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE);
}
@AfterAll
public static void afterAll(){
System.out.println("CollectionAwareIntegrationTests.afterAll()");
ConfigScoped.setScopeName(null);
callSuperBeforeAll(new Object() {});
public static void afterAll() {
Config.setScopeName(null);
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// the Config class has been modified, these need to be loaded again
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
reactiveCouchbaseTemplate = (ReactiveCouchbaseTemplate) ac.getBean(REACTIVE_COUCHBASE_TEMPLATE);
callSuperAfterAll(new Object() {});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020 the original author or authors
* Copyright 2020-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,15 +39,16 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import com.couchbase.client.core.io.CollectionIdentifier;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.couchbase.CouchbaseClientFactory;
import org.springframework.data.couchbase.SimpleCouchbaseClientFactory;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.domain.Config;
import com.couchbase.client.core.diagnostics.PingResult;
import com.couchbase.client.core.diagnostics.PingState;
@@ -80,7 +81,6 @@ import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryResult;
import com.couchbase.client.java.search.SearchQuery;
import com.couchbase.client.java.search.result.SearchResult;
import org.springframework.data.couchbase.domain.Config;
/**
* Extends the {@link ClusterAwareIntegrationTests} with java-client specific code.
@@ -91,8 +91,9 @@ import org.springframework.data.couchbase.domain.Config;
@Timeout(value = 10, unit = TimeUnit.MINUTES) // Safety timer so tests can't block CI executors
public class JavaIntegrationTests extends ClusterAwareIntegrationTests {
@Autowired static public CouchbaseTemplate couchbaseTemplate;
@Autowired static public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
// Autowired annotation is not supported on static fields
static public CouchbaseTemplate couchbaseTemplate;
static public ReactiveCouchbaseTemplate reactiveCouchbaseTemplate;
@BeforeAll
public static void beforeAll() {
@@ -141,15 +142,28 @@ public class JavaIntegrationTests extends ClusterAwareIntegrationTests {
ScopeSpec scopeSpec = ScopeSpec.create(scopeName);
CollectionSpec collSpec = CollectionSpec.create(collectionName, scopeName);
if (!scopeName.equals("_default")) {
collectionManager.createScope(scopeName);
if (!scopeName.equals(CollectionIdentifier.DEFAULT_SCOPE)) {
try {
collectionManager.createScope(scopeName);
waitUntilCondition(() -> scopeExists(collectionManager, scopeName));
ScopeSpec found = collectionManager.getScope(scopeName);
assertEquals(scopeSpec, found);
} catch (CouchbaseException e) {
if (!e.toString().contains("already exists")) {
e.printStackTrace();
throw e;
}
}
}
waitUntilCondition(() -> scopeExists(collectionManager, scopeName));
ScopeSpec found = collectionManager.getScope(scopeName);
assertEquals(scopeSpec, found);
collectionManager.createCollection(collSpec);
try {
collectionManager.createCollection(collSpec);
} catch (CouchbaseException e) {
if (!e.toString().contains("already exists")) {
e.printStackTrace();
throw e;
}
}
waitUntilCondition(() -> collectionExists(collectionManager, collSpec));
waitUntilCondition(
() -> collectionReady(cluster.bucket(config().bucketname()).scope(scopeName).collection(collectionName)));
@@ -258,6 +272,7 @@ public class JavaIntegrationTests extends ClusterAwareIntegrationTests {
String collectionName) {
CreatePrimaryQueryIndexOptions options = CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions();
options.timeout(Duration.ofSeconds(300));
options.ignoreIfExists(true);
final CreatePrimaryQueryIndexOptions.Built builtOpts = options.build();
final String indexName = builtOpts.indexName().orElse(null);
@@ -335,7 +350,6 @@ public class JavaIntegrationTests extends ClusterAwareIntegrationTests {
break;
} catch (CouchbaseException | IllegalStateException ex) {
// this is a pretty dirty hack to avoid a race where we don't know if the index is ready yet
System.out.println("createFtsCollectionIndex: " + i + " " + ex);
if (i < (maxTries - 1) && (ex.getMessage().contains("no planPIndexes for indexName")
|| ex.getMessage().contains("pindex_consistency mismatched partition")
|| ex.getMessage().contains("pindex not available"))) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors
* Copyright 2012-2021 the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
*/
package org.springframework.data.couchbase.util;
import static java.nio.charset.StandardCharsets.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import okhttp3.Credentials;
import okhttp3.FormBody;
@@ -43,6 +43,7 @@ public class UnmanagedTestCluster extends TestCluster {
private final String adminPassword;
private final int numReplicas;
private volatile String bucketname;
private long startTime = System.currentTimeMillis();
UnmanagedTestCluster(final Properties properties) {
seedHost = properties.getProperty("cluster.unmanaged.seed").split(":")[0];
@@ -69,8 +70,9 @@ public class UnmanagedTestCluster extends TestCluster {
.build())
.execute();
if (postResponse.code() != 202) {
throw new Exception("Could not create bucket: " + postResponse + ", Reason: " + postResponse.body().string());
String reason = postResponse.body().string();
if (postResponse.code() != 202 && !(reason.contains("Bucket with given name already exists"))) {
throw new Exception("Could not create bucket: " + postResponse + ", Reason: " + reason);
}
Response getResponse = httpClient
@@ -140,10 +142,13 @@ public class UnmanagedTestCluster extends TestCluster {
@Override
public void close() {
try {
httpClient
.newCall(new Request.Builder().header("Authorization", Credentials.basic(adminUsername, adminPassword))
.url("http://" + seedHost + ":" + seedPort + "/pools/default/buckets/" + bucketname).delete().build())
.execute();
if (!bucketname.equals("my_bucket")) {
httpClient
.newCall(new Request.Builder().header("Authorization", Credentials.basic(adminUsername, adminPassword))
.url("http://" + seedHost + ":" + seedPort + "/pools/default/buckets/" + bucketname).delete().build())
.execute();
}
System.out.println("elapsed: " + (System.currentTimeMillis() - startTime));
} catch (Exception ex) {
throw new RuntimeException(ex);
}