Fix 1441 in 4_4_x - scope and collection not considered in repo delete.

This is a more localized change than the one in main.

Closes #1464.
This commit is contained in:
Michael Reiche
2022-06-16 15:20:40 -07:00
parent 72886bb256
commit 797a454108
14 changed files with 304 additions and 113 deletions

View File

@@ -19,6 +19,7 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.ExecutableFindByQuery;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.TerminatingFindByQuery;
import org.springframework.data.couchbase.core.ExecutableRemoveByQueryOperation.ExecutableRemoveByQuery;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.repository.query.CouchbaseQueryExecution.DeleteExecution;
import org.springframework.data.couchbase.repository.query.CouchbaseQueryExecution.PagedExecution;
@@ -43,6 +44,7 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
implements RepositoryQuery {
private final ExecutableFindByQuery<?> findOperationWithProjection;
private final ExecutableRemoveByQuery<?> removeOp;
/**
* Creates a new {@link AbstractCouchbaseQuery} from the given {@link ReactiveCouchbaseQueryMethod} and
@@ -65,6 +67,8 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
ExecutableFindByQuery<?> findOp = operations.findByQuery(type);
findOp = (ExecutableFindByQuery<?>) (findOp.inScope(method.getScope()).inCollection(method.getCollection()));
this.findOperationWithProjection = findOp;
this.removeOp = (ExecutableRemoveByQuery<?>) (operations.removeByQuery(type).inScope(method.getScope())
.inCollection(method.getCollection()));
}
/**
@@ -114,7 +118,7 @@ public abstract class AbstractCouchbaseQuery extends AbstractCouchbaseQueryBase<
private CouchbaseQueryExecution getExecutionToWrap(ParameterAccessor accessor, ExecutableFindByQuery<?> operation) {
if (isDeleteQuery()) {
return new DeleteExecution(getOperations(), getQueryMethod());
return new DeleteExecution(removeOp);
} else if (isTailable(getQueryMethod())) {
return (q, t, r, c) -> operation.as(r).matching(q.with(accessor.getPageable())).all(); // s/b tail() instead of
// all()

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020-2021 the original author or authors.
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import org.springframework.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations;
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperation;
import org.springframework.data.couchbase.core.ReactiveFindByQueryOperation.ReactiveFindByQuery;
import org.springframework.data.couchbase.core.ReactiveRemoveByQueryOperation.ReactiveRemoveByQuery;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.repository.query.ReactiveCouchbaseQueryExecution.DeleteExecution;
import org.springframework.data.couchbase.repository.query.ReactiveCouchbaseQueryExecution.ResultProcessingExecution;
@@ -41,7 +42,8 @@ import org.springframework.util.Assert;
public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQueryBase<ReactiveCouchbaseOperations>
implements RepositoryQuery {
private final ReactiveFindByQuery<?> findOperationWithProjection;
private final ReactiveFindByQuery<?> findOp;
private final ReactiveRemoveByQuery<?> removeOp;
/**
* Creates a new {@link AbstractReactiveCouchbaseQuery} from the given {@link ReactiveCouchbaseQueryMethod} and
@@ -62,9 +64,10 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu
EntityMetadata<?> metadata = method.getEntityInformation();
Class<?> type = metadata.getJavaType();
ReactiveFindByQuery<?> findOp = operations.findByQuery(type);
findOp = (ReactiveFindByQuery<?>) (findOp.inScope(method.getScope()).inCollection(method.getCollection()));
this.findOperationWithProjection = findOp;
this.findOp = (ReactiveFindByQuery<?>) (operations.findByQuery(type).inScope(method.getScope())
.inCollection(method.getCollection()));
this.removeOp = (ReactiveRemoveByQuery<?>) (operations.removeByQuery(type).inScope(method.getScope())
.inCollection(method.getCollection()));
}
/**
@@ -83,10 +86,8 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu
// query = applyAnnotatedCollationIfPresent(query, accessor); // not yet implemented
query = applyQueryMetaAttributesIfPresent(query, typeToRead);
ReactiveFindByQuery<?> find = findOperationWithProjection;
ReactiveCouchbaseQueryExecution execution = getExecution(accessor,
new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), find);
new ResultProcessingConverter<>(processor, getOperations(), getInstantiators()), findOp);
return execution.execute(query, processor.getReturnedType().getDomainType(), typeToRead, null);
}
@@ -113,7 +114,7 @@ public abstract class AbstractReactiveCouchbaseQuery extends AbstractCouchbaseQu
ReactiveFindByQuery<?> operation) {
if (isDeleteQuery()) {
return new DeleteExecution(getOperations(), getQueryMethod());
return new DeleteExecution(removeOp);
} else if (isTailable(getQueryMethod())) {
return (q, t, r, c) -> operation.as(r).matching(q.with(accessor.getPageable())).all(); // s/b tail() instead of
// all()

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020 the original author or authors.
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,15 +18,14 @@ package org.springframework.data.couchbase.repository.query;
import java.util.List;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.ExecutableFindByQuery;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation.TerminatingFindByQuery;
import org.springframework.data.couchbase.core.ExecutableRemoveByQueryOperation.ExecutableRemoveByQuery;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.util.Assert;
/**
@@ -47,12 +46,10 @@ interface CouchbaseQueryExecution {
final class DeleteExecution implements CouchbaseQueryExecution {
private final CouchbaseOperations operations;
private final QueryMethod method;
private final ExecutableRemoveByQuery<?> removeOperation;
public DeleteExecution(CouchbaseOperations operations, QueryMethod method) {
this.operations = operations;
this.method = method;
public DeleteExecution(ExecutableRemoveByQuery<?> removeOperation) {
this.removeOperation = removeOperation;
}
/*
@@ -61,7 +58,7 @@ interface CouchbaseQueryExecution {
*/
@Override
public Object execute(Query query, Class<?> type, Class<?> returnType, String collection) {
return operations.removeByQuery(type).matching(query).all();
return removeOperation.matching(query).all();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2020 the original author or authors.
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
package org.springframework.data.couchbase.repository.query;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations;
import org.springframework.data.couchbase.core.ReactiveRemoveByQueryOperation;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.util.Assert;
@@ -39,12 +39,10 @@ interface ReactiveCouchbaseQueryExecution {
final class DeleteExecution implements ReactiveCouchbaseQueryExecution {
private final ReactiveCouchbaseOperations operations;
private final CouchbaseQueryMethod method;
private final ReactiveRemoveByQueryOperation.ReactiveRemoveByQuery removeOp;
public DeleteExecution(ReactiveCouchbaseOperations operations, CouchbaseQueryMethod method) {
this.operations = operations;
this.method = method;
public DeleteExecution(ReactiveRemoveByQueryOperation.ReactiveRemoveByQuery<?> removeOp) {
this.removeOp = removeOp;
}
/*
@@ -53,7 +51,7 @@ interface ReactiveCouchbaseQueryExecution {
*/
@Override
public Object execute(Query query, Class<?> type, Class<?> returnType, String collection) {
return operations.removeByQuery(type)/*.inCollection(collection)*/.matching(query).all();
return removeOp.matching(query).all();
}
}

View File

@@ -104,6 +104,11 @@ public interface AirportRepository extends CouchbaseRepository<Airport, String>,
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
List<RemoveResult> deleteByIata(String iata);
@Query("#{#n1ql.delete} WHERE #{#n1ql.filter} and iata = $1 #{#n1ql.returning}")
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
@Collection("bogus_collection")
List<RemoveResult> deleteByIataAnnotated(String iata);
@Query("SELECT __cas, * from #{#n1ql.bucket} where iata = $1")
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
List<Airport> getAllByIataNoID(String iata);

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.domain;
import org.springframework.data.couchbase.repository.Collection;
/**
* AirportRepository with collection annotation
*
* @author Michael Reiche
*/
@Collection("my_collection2")
public interface AirportRepositoryAnnotated extends AirportRepository {}

View File

@@ -105,7 +105,7 @@ public class FluxTest extends JavaIntegrationTests {
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.
@Autowired ReactiveAirportRepository reactiveAirportRepository; // intellij flags "Could not Autowire", runs ok
AtomicInteger rCat = new AtomicInteger(0);
AtomicInteger rFlat = new AtomicInteger(0);
@@ -136,7 +136,7 @@ public class FluxTest extends JavaIntegrationTests {
listOfLists.add(list);
}
Flux<Object> af = Flux.fromIterable(listOfLists).concatMap(catalogToStore -> Flux.fromIterable(catalogToStore)
.parallel(4).runOn(Schedulers.parallel()).concatMap((entity) -> airportRepository.save(entity)));
.parallel(4).runOn(Schedulers.parallel()).concatMap((entity) -> reactiveAirportRepository.save(entity)));
List<Object> saved = af.collectList().block();
System.out.println("results.size() : " + saved.size());
@@ -152,7 +152,7 @@ public class FluxTest extends JavaIntegrationTests {
e.printStackTrace();
throw e;
}
List<Airport> airports = airportRepository.findAll().collectList().block();
List<Airport> airports = reactiveAirportRepository.findAll().collectList().block();
assertEquals(0, airports.size(), "should have been all deleted");
}
@@ -164,11 +164,11 @@ public class FluxTest extends JavaIntegrationTests {
for (int i = 0; i < 5; i++) {
list.add(a.withId(UUID.randomUUID().toString()));
}
Flux<Object> af = Flux.fromIterable(list).concatMap((entity) -> airportRepository.save(entity));
Flux<Object> af = Flux.fromIterable(list).concatMap((entity) -> reactiveAirportRepository.save(entity));
List<Object> saved = af.collectList().block();
System.out.println("results.size() : " + saved.size());
Flux<Pair<String, Mono<Airport>>> pairFlux = Flux.fromIterable(list)
.map((airport) -> Pair.of(airport.getId(), airportRepository.findById(airport.getId())));
.map((airport) -> Pair.of(airport.getId(), reactiveAirportRepository.findById(airport.getId())));
List<Pair<String, Mono<Airport>>> airportPairs = pairFlux.collectList().block();
for (Pair<String, Mono<Airport>> airportPair : airportPairs) {
System.out.println("id: " + airportPair.getFirst() + " airport: " + airportPair.getSecond().block());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,8 @@ import reactor.core.publisher.Mono;
import java.util.ArrayList;
import org.springframework.data.couchbase.core.RemoveResult;
import org.springframework.data.couchbase.repository.Collection;
import org.springframework.data.couchbase.repository.DynamicProxyable;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.couchbase.repository.ReactiveCouchbaseRepository;
@@ -99,6 +101,15 @@ public interface ReactiveAirportRepository
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Mono<Airport> findByIata(String iata);
@Query("#{#n1ql.delete} WHERE #{#n1ql.filter} and iata = $1 #{#n1ql.returning}")
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
Flux<RemoveResult> deleteByIata(String iata);
@Query("#{#n1ql.delete} WHERE #{#n1ql.filter} and iata = $1 #{#n1ql.returning}")
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
@Collection("bogus_collection")
Flux<RemoveResult> deleteByIataAnnotated(String iata);
// This is not efficient. See findAllByIataLike for efficient reactive paging
default public Mono<Page<Airport>> findAllAirportsPaged(Pageable pageable) {
return count().flatMap(airportCount -> {

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.domain;
import org.springframework.data.couchbase.repository.Collection;
/**
* AirportRepository with collection annotation
*
* @author Michael Reiche
*/
@Collection("my_collection2")
public interface ReactiveAirportRepositoryAnnotated extends ReactiveAirportRepository{}

View File

@@ -60,7 +60,7 @@ public class ReactiveCouchbaseRepositoryKeyValueIntegrationTests extends Cluster
@Autowired ReactiveUserRepository userRepository;
@Autowired ReactiveAirportRepository airportRepository;
@Autowired ReactiveAirportRepository reactiveAirportRepository;
@Autowired ReactiveAirlineRepository airlineRepository;
@@ -108,13 +108,13 @@ public class ReactiveCouchbaseRepositoryKeyValueIntegrationTests extends Cluster
Airport vie = null;
try {
vie = new Airport("airports::vie", "vie", "low2");
Airport saved = airportRepository.save(vie).block();
Airport airport1 = airportRepository.findById(saved.getId()).block();
Airport saved = reactiveAirportRepository.save(vie).block();
Airport airport1 = reactiveAirportRepository.findById(saved.getId()).block();
assertEquals(airport1, saved);
assertEquals(saved.getCreatedBy(), ReactiveNaiveAuditorAware.AUDITOR); // ReactiveNaiveAuditorAware will provide
// this
} finally {
airportRepository.delete(vie).block();
reactiveAirportRepository.delete(vie).block();
}
}

View File

@@ -72,7 +72,8 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
@Autowired CouchbaseClientFactory clientFactory;
@Autowired ReactiveAirportRepository airportRepository; // intellij flags "Could not Autowire", but it runs ok.
@Autowired ReactiveAirportRepository reactiveAirportRepository; // intellij flags "Could not Autowire", but it runs
// ok.
@Autowired ReactiveUserRepository userRepository; // intellij flags "Could not Autowire", but it runs ok.
@Test
@@ -81,18 +82,18 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
Airport jfk = null;
try {
vie = new Airport("airports::vie", "vie", "low1");
airportRepository.save(vie).block();
reactiveAirportRepository.save(vie).block();
jfk = new Airport("airports::jfk", "JFK", "xxxx");
airportRepository.save(jfk).block();
reactiveAirportRepository.save(jfk).block();
List<Airport> all = airportRepository.findAll().toStream().collect(Collectors.toList());
List<Airport> all = reactiveAirportRepository.findAll().toStream().collect(Collectors.toList());
assertFalse(all.isEmpty());
assertTrue(all.stream().anyMatch(a -> a.getId().equals("airports::vie")));
assertTrue(all.stream().anyMatch(a -> a.getId().equals("airports::jfk")));
} finally {
airportRepository.delete(vie).block();
airportRepository.delete(jfk).block();
reactiveAirportRepository.delete(vie).block();
reactiveAirportRepository.delete(jfk).block();
}
}
@@ -101,20 +102,20 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
Airport vie = null;
try {
vie = new Airport("airports::vie", "vie", "low2");
airportRepository.save(vie).block();
List<Airport> airports1 = airportRepository.findAllByIata("vie").collectList().block();
reactiveAirportRepository.save(vie).block();
List<Airport> airports1 = reactiveAirportRepository.findAllByIata("vie").collectList().block();
assertEquals(1, airports1.size());
List<Airport> airports2 = airportRepository.findAllByIata("vie").collectList().block();
List<Airport> airports2 = reactiveAirportRepository.findAllByIata("vie").collectList().block();
assertEquals(1, airports2.size());
vie = airportRepository.save(vie).block();
List<Airport> airports = airportRepository.findAllByIata("vie").collectList().block();
vie = reactiveAirportRepository.save(vie).block();
List<Airport> airports = reactiveAirportRepository.findAllByIata("vie").collectList().block();
assertEquals(1, airports.size());
Airport airport1 = airportRepository.findById(airports.get(0).getId()).block();
Airport airport1 = reactiveAirportRepository.findById(airports.get(0).getId()).block();
assertEquals(airport1.getIata(), vie.getIata());
Airport airport2 = airportRepository.findByIata(airports.get(0).getIata()).block();
Airport airport2 = reactiveAirportRepository.findByIata(airports.get(0).getIata()).block();
assertEquals(airport1.getId(), vie.getId());
} finally {
airportRepository.delete(vie).block();
reactiveAirportRepository.delete(vie).block();
}
}
@@ -133,27 +134,27 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
@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();
Airport saved1 = reactiveAirportRepository.save(vie).block();
Airport saved2 = reactiveAirportRepository.save(vie.withId(UUID.randomUUID().toString())).block();
try {
airportRepository.findAll().collectList().block(); // findAll has QueryScanConsistency;
Mono<Airport> airport = airportRepository.findPolicySnapshotByPolicyIdAndEffectiveDateTime("any", 0);
reactiveAirportRepository.findAll().collectList().block(); // findAll has QueryScanConsistency;
Mono<Airport> airport = reactiveAirportRepository.findPolicySnapshotByPolicyIdAndEffectiveDateTime("any", 0);
System.out.println("------------------------------");
System.out.println(airport.block());
System.out.println("------------------------------");
Flux<Airport> airports = airportRepository.findPolicySnapshotAll();
Flux<Airport> airports = reactiveAirportRepository.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();
reactiveAirportRepository.delete(saved1).block();
reactiveAirportRepository.delete(saved2).block();
}
}
public Mono<Airport> getPolicyByIdAndEffectiveDateTime(String policyId, Instant effectiveDateTime) {
return airportRepository
return reactiveAirportRepository
.findPolicySnapshotByPolicyIdAndEffectiveDateTime(policyId, effectiveDateTime.toEpochMilli())
// .map(Airport::getEntity)
.doOnError(
@@ -177,36 +178,36 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
Callable<Boolean>[] suppliers = new Callable[iatas.size()];
for (String iata : iatas) {
Airport airport = new Airport("airports::" + iata, iata, iata.toLowerCase() /* lcao */);
airportRepository.save(airport).block();
reactiveAirportRepository.save(airport).block();
}
int page = 0;
airportRepository.findAllByIataLike("S%", PageRequest.of(page++, 2)).as(StepVerifier::create) //
reactiveAirportRepository.findAllByIataLike("S%", PageRequest.of(page++, 2)).as(StepVerifier::create) //
.expectNextMatches(a -> {
return iatas.contains(a.getIata());
}).expectNextMatches(a -> iatas.contains(a.getIata())).verifyComplete();
airportRepository.findAllByIataLike("S%", PageRequest.of(page++, 2)).as(StepVerifier::create) //
reactiveAirportRepository.findAllByIataLike("S%", PageRequest.of(page++, 2)).as(StepVerifier::create) //
.expectNextMatches(a -> iatas.contains(a.getIata())).verifyComplete();
Long airportCount = airportRepository.count().block();
Long airportCount = reactiveAirportRepository.count().block();
assertEquals(iatas.size(), airportCount);
airportCount = airportRepository.countByIataIn("JFK", "IAD", "SFO").block();
airportCount = reactiveAirportRepository.countByIataIn("JFK", "IAD", "SFO").block();
assertEquals(3, airportCount);
airportCount = airportRepository.countByIcaoAndIataIn("jfk", "JFK", "IAD", "SFO", "XXX").block();
airportCount = reactiveAirportRepository.countByIcaoAndIataIn("jfk", "JFK", "IAD", "SFO", "XXX").block();
assertEquals(1, airportCount);
airportCount = airportRepository.countByIataIn("XXX").block();
airportCount = reactiveAirportRepository.countByIataIn("XXX").block();
assertEquals(0, airportCount);
} finally {
for (String iata : iatas) {
Airport airport = new Airport("airports::" + iata, iata, iata.toLowerCase() /* lcao */);
try {
airportRepository.delete(airport).block();
reactiveAirportRepository.delete(airport).block();
} catch (DataRetrievalFailureException drfe) {
System.out.println("Failed to delete: " + airport);
}
@@ -224,17 +225,17 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
try {
// This failed once against Capella - not sure why.
airportRepository.saveAll(asList(vienna, frankfurt, losAngeles)).blockLast();
reactiveAirportRepository.saveAll(asList(vienna, frankfurt, losAngeles)).blockLast();
airportRepository.deleteAllById(asList(vienna.getId(), losAngeles.getId())).as(StepVerifier::create)
reactiveAirportRepository.deleteAllById(asList(vienna.getId(), losAngeles.getId())).as(StepVerifier::create)
.verifyComplete();
airportRepository.findAll().as(StepVerifier::create).expectNext(frankfurt).verifyComplete();
reactiveAirportRepository.findAll().as(StepVerifier::create).expectNext(frankfurt).verifyComplete();
} finally {
List<Airport> airports = airportRepository.findAll().collectList().block(); // .as(StepVerifier::create).expectNext(frankfurt).verifyComplete();
List<Airport> airports = reactiveAirportRepository.findAll().collectList().block(); // .as(StepVerifier::create).expectNext(frankfurt).verifyComplete();
System.out.println(airports);
airportRepository.deleteAll().block();
reactiveAirportRepository.deleteAll().block();
}
}
@@ -246,14 +247,14 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
Airport losAngeles = new Airport("airports::lax", "lax", "KLAX");
try {
airportRepository.saveAll(asList(vienna, frankfurt, losAngeles)).blockLast();
reactiveAirportRepository.saveAll(asList(vienna, frankfurt, losAngeles)).blockLast();
airportRepository.deleteAll().as(StepVerifier::create).verifyComplete();
reactiveAirportRepository.deleteAll().as(StepVerifier::create).verifyComplete();
airportRepository.findAll().as(StepVerifier::create).verifyComplete();
reactiveAirportRepository.findAll().as(StepVerifier::create).verifyComplete();
} finally {
airportRepository.deleteAll().block();
reactiveAirportRepository.deleteAll().block();
}
}
@@ -263,13 +264,13 @@ public class ReactiveCouchbaseRepositoryQueryIntegrationTests extends JavaIntegr
Airport vienna = new Airport("airports::vie", "vie", "LOWW");
try {
Airport ap = airportRepository.save(vienna).block();
Airport ap = reactiveAirportRepository.save(vienna).block();
assertEquals(vienna.getId(), ap.getId(), "should have saved what was provided");
airportRepository.delete(vienna).as(StepVerifier::create).verifyComplete();
reactiveAirportRepository.delete(vienna).as(StepVerifier::create).verifyComplete();
airportRepository.findAll().as(StepVerifier::create).verifyComplete();
reactiveAirportRepository.findAll().as(StepVerifier::create).verifyComplete();
} finally {
airportRepository.deleteAll().block();
reactiveAirportRepository.deleteAll().block();
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.couchbase.repository.query;
import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_SCOPE;
import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -29,13 +30,12 @@ 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.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.data.couchbase.domain.Address;
import org.springframework.data.couchbase.domain.AddressAnnotated;
import org.springframework.data.couchbase.domain.Airport;
import org.springframework.data.couchbase.domain.AirportRepository;
import org.springframework.data.couchbase.domain.AirportRepositoryAnnotated;
import org.springframework.data.couchbase.domain.Config;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserCol;
@@ -48,13 +48,14 @@ 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 org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.couchbase.client.core.error.IndexFailureException;
import com.couchbase.client.core.io.CollectionIdentifier;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
@SpringJUnitConfig(Config.class)
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
public class CouchbaseRepositoryQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests {
@@ -62,6 +63,7 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
@Autowired UserColRepository userColRepository; // initialized in beforeEach()
@Autowired UserSubmissionAnnotatedRepository userSubmissionAnnotatedRepository; // initialized in beforeEach()
@Autowired UserSubmissionUnannotatedRepository userSubmissionUnannotatedRepository; // initialized in beforeEach()
@Autowired AirportRepositoryAnnotated airportRepositoryAnnotated;
@BeforeAll
public static void beforeAll() {
@@ -86,14 +88,9 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
// then do processing for this class
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
couchbaseTemplate.removeByQuery(UserCol.class).inScope(otherScope).inCollection(otherCollection).all();
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// seems that @Autowired is not adequate, so ...
airportRepository = (AirportRepository) ac.getBean("airportRepository");
userColRepository = (UserColRepository) ac.getBean("userColRepository");
userSubmissionAnnotatedRepository = (UserSubmissionAnnotatedRepository) ac
.getBean("userSubmissionAnnotatedRepository");
userSubmissionUnannotatedRepository = (UserSubmissionUnannotatedRepository) ac
.getBean("userSubmissionUnannotatedRepository");
couchbaseTemplate.removeByQuery(Airport.class).inCollection(collectionName).all();
couchbaseTemplate.removeByQuery(Airport.class).inCollection(collectionName2).all();
couchbaseTemplate.findByQuery(Airport.class).withConsistency(REQUEST_PLUS).inCollection(collectionName).all();
}
@AfterEach
@@ -149,8 +146,7 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
// valid scope, collection in options
Airport airport2 = ar.withCollection(collectionName)
.withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS))
.iata(vie.getIata());
.withOptions(QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS)).iata(vie.getIata());
assertEquals(saved, airport2);
// given bad collectionName in fluent
@@ -159,8 +155,7 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
// given bad scopeName in fluent
assertThrows(IndexFailureException.class, () -> ar.withScope("bogusScope").iata(vie.getIata()));
Airport airport6 = ar.withOptions(QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS))
.iata(vie.getIata());
Airport airport6 = ar.withOptions(QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS)).iata(vie.getIata());
assertEquals(saved, airport6);
} catch (Exception e) {
@@ -180,8 +175,8 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
try {
Airport saved = ar.save(vie);
Airport airport3 = ar.withOptions(
QueryOptions.queryOptions().scanConsistency(QueryScanConsistency.REQUEST_PLUS).parameters(positionalParams))
Airport airport3 = ar
.withOptions(QueryOptions.queryOptions().scanConsistency(REQUEST_PLUS).parameters(positionalParams))
.iata(vie.getIata());
assertEquals(saved, airport3);
@@ -278,8 +273,7 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
address1 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address1);
address2 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address2);
address3 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address3);
couchbaseTemplate.findByQuery(AddressAnnotated.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inScope(scopeName).all();
couchbaseTemplate.findByQuery(AddressAnnotated.class).withConsistency(REQUEST_PLUS).inScope(scopeName).all();
// scope for AddressesAnnotated in N1qlJoin comes from userSubmissionAnnotatedRepository.
List<UserSubmissionAnnotated> users = userSubmissionAnnotatedRepository.findByUsername(user.getUsername());
@@ -333,8 +327,7 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
address1 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address1);
address2 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address2);
address3 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address3);
couchbaseTemplate.findByQuery(AddressAnnotated.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
.inScope(scopeName).all();
couchbaseTemplate.findByQuery(AddressAnnotated.class).withConsistency(REQUEST_PLUS).inScope(scopeName).all();
// scope for AddressesAnnotated in N1qlJoin comes from userSubmissionAnnotatedRepository.
List<UserSubmissionUnannotated> users = userSubmissionUnannotatedRepository.findByUsername(user.getUsername());
@@ -359,4 +352,57 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
}
}
@Test
void stringDeleteCollectionTest() {
Airport airport = new Airport(loc(), "vie", "abc");
Airport otherAirport = new Airport(loc(), "xxx", "xyz");
try {
airport = airportRepository.withScope(scopeName).withCollection(collectionName).save(airport);
otherAirport = airportRepository.withScope(scopeName).withCollection(collectionName).save(otherAirport);
assertEquals(1,
airportRepository.withScope(scopeName).withCollection(collectionName).deleteByIata(airport.getIata()).size());
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
airportRepository.withScope(scopeName).withCollection(collectionName).deleteById(otherAirport.getId());
}
}
@Test
void stringDeleteWithRepositoryAnnotationTest() {
Airport airport = new Airport(loc(), "vie", "abc");
Airport otherAirport = new Airport(loc(), "xxx", "xyz");
try {
airport = airportRepositoryAnnotated.withScope(scopeName).save(airport);
otherAirport = airportRepositoryAnnotated.withScope(scopeName).save(otherAirport);
// don't specify a collection - should get collection from AirportRepositoryAnnotated
assertEquals(1, airportRepositoryAnnotated.withScope(scopeName).deleteByIata(airport.getIata()).size());
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
// this will fail if the above didn't use collectionName2
airportRepository.withScope(scopeName).withCollection(collectionName2).deleteById(otherAirport.getId());
}
}
@Test
void stringDeleteWithMethodAnnotationTest() {
Airport airport = new Airport(loc(), "vie", "abc");
Airport otherAirport = new Airport(loc(), "xxx", "xyz");
try {
Airport airportSaved = airportRepositoryAnnotated.withScope(scopeName).save(airport);
Airport otherAirportSaved = airportRepositoryAnnotated.withScope(scopeName).save(otherAirport);
// don't specify a collection - should get collection from deleteByIataAnnotated method
assertThrows(IndexFailureException.class, () -> assertEquals(1,
airportRepositoryAnnotated.withScope(scopeName).deleteByIataAnnotated(airport.getIata()).size()));
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
// this will fail if the above didn't use collectionName2
airportRepository.withScope(scopeName).withCollection(collectionName2).deleteById(otherAirport.getId());
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017-2021 the original author or authors.
* Copyright 2017-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
*/
package org.springframework.data.couchbase.repository.query;
import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -26,12 +27,11 @@ 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.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.dao.DataRetrievalFailureException;
import org.springframework.data.couchbase.domain.Airport;
import org.springframework.data.couchbase.domain.Config;
import org.springframework.data.couchbase.domain.ReactiveAirportRepository;
import org.springframework.data.couchbase.domain.ReactiveAirportRepositoryAnnotated;
import org.springframework.data.couchbase.domain.ReactiveUserColRepository;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserCol;
@@ -39,6 +39,7 @@ 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 org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import com.couchbase.client.core.error.IndexFailureException;
import com.couchbase.client.core.io.CollectionIdentifier;
@@ -46,11 +47,18 @@ import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.query.QueryOptions;
import com.couchbase.client.java.query.QueryScanConsistency;
/**
* Reactive Repository Query tests with Collections
*
* @author Michael Reiche
*/
@SpringJUnitConfig(Config.class)
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
public class ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests {
@Autowired ReactiveAirportRepository airportRepository;
@Autowired ReactiveAirportRepository reactiveAirportRepository;
@Autowired ReactiveUserColRepository userColRepository;
@Autowired ReactiveAirportRepositoryAnnotated reactiveAirportRepositoryAnnotated;
@BeforeAll
public static void beforeAll() {
@@ -75,11 +83,9 @@ public class ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests extends
// then do processing for this class
couchbaseTemplate.removeByQuery(User.class).inCollection(collectionName).all();
couchbaseTemplate.removeByQuery(UserCol.class).inScope(otherScope).inCollection(otherCollection).all();
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
// seems that @Autowired is not adequate, so ...
airportRepository = (ReactiveAirportRepository) ac.getBean("reactiveAirportRepository");
userColRepository = (ReactiveUserColRepository) ac.getBean("reactiveUserColRepository");
couchbaseTemplate.removeByQuery(Airport.class).inCollection(collectionName).all();
couchbaseTemplate.removeByQuery(Airport.class).inCollection(collectionName2).all();
couchbaseTemplate.findByQuery(Airport.class).withConsistency(REQUEST_PLUS).inCollection(collectionName).all();
}
@AfterEach
@@ -94,7 +100,7 @@ public class ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests extends
@Test
public void myTest() {
ReactiveAirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName);
ReactiveAirportRepository ar = reactiveAirportRepository.withScope(scopeName).withCollection(collectionName);
Airport vie = new Airport("airports::vie", "vie", "loww");
try {
Airport saved = ar.save(vie).block();
@@ -119,7 +125,7 @@ public class ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests extends
Airport vie = new Airport("airports::vie", "vie", "loww");
// create proxy with scope, collection
ReactiveAirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName);
ReactiveAirportRepository ar = reactiveAirportRepository.withScope(scopeName).withCollection(collectionName);
try {
Airport saved = ar.save(vie).block();
@@ -150,7 +156,7 @@ public class ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests extends
void findBySimplePropertyWithOptions() {
Airport vie = new Airport("airports::vie", "vie", "loww");
ReactiveAirportRepository ar = airportRepository.withScope(scopeName).withCollection(collectionName);
ReactiveAirportRepository ar = reactiveAirportRepository.withScope(scopeName).withCollection(collectionName);
JsonArray positionalParams = JsonArray.create().add("\"this parameter will be overridden\"");
try {
Airport saved = ar.save(vie).block();
@@ -212,4 +218,61 @@ public class ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests extends
} catch (DataRetrievalFailureException drfe) {}
}
}
@Test
void stringDeleteCollectionTest() {
Airport airport = new Airport(loc(), "vie", "abc");
Airport otherAirport = new Airport(loc(), "xxx", "xyz");
try {
airport = reactiveAirportRepository.withScope(scopeName).withCollection(collectionName).save(airport).block();
otherAirport = reactiveAirportRepository.withScope(scopeName).withCollection(collectionName).save(otherAirport)
.block();
assertEquals(1, reactiveAirportRepository.withScope(scopeName).withCollection(collectionName)
.deleteByIata(airport.getIata()).collectList().block().size());
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
reactiveAirportRepository.withScope(scopeName).withCollection(collectionName).deleteById(otherAirport.getId());
}
}
@Test
void stringDeleteWithRepositoryAnnotationTest() {
Airport airport = new Airport(loc(), "vie", "abc");
Airport otherAirport = new Airport(loc(), "xxx", "xyz");
try {
airport = reactiveAirportRepositoryAnnotated.withScope(scopeName).save(airport).block();
otherAirport = reactiveAirportRepositoryAnnotated.withScope(scopeName).save(otherAirport).block();
// don't specify a collection - should get collection from AirportRepositoryAnnotated
assertEquals(1, reactiveAirportRepositoryAnnotated.withScope(scopeName).deleteByIata(airport.getIata())
.collectList().block().size());
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
// this will fail if the above didn't use collectionName2
reactiveAirportRepository.withScope(scopeName).withCollection(collectionName2).deleteById(otherAirport.getId());
}
}
@Test
void stringDeleteWithMethodAnnotationTest() {
Airport airport = new Airport(loc(), "vie", "abc");
Airport otherAirport = new Airport(loc(), "xxx", "xyz");
try {
Airport airportSaved = reactiveAirportRepositoryAnnotated.withScope(scopeName).save(airport).block();
Airport otherAirportSaved = reactiveAirportRepositoryAnnotated.withScope(scopeName).save(otherAirport).block();
// don't specify a collection - should get collection from deleteByIataAnnotated method
assertThrows(IndexFailureException.class, () -> assertEquals(1, reactiveAirportRepositoryAnnotated
.withScope(scopeName).deleteByIataAnnotated(airport.getIata()).collectList().block().size()));
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
// this will fail if the above didn't use collectionName2
reactiveAirportRepository.withScope(scopeName).withCollection(collectionName2).deleteById(otherAirport.getId());
}
}
}

View File

@@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import org.junit.jupiter.api.AfterAll;
@@ -49,6 +50,8 @@ import com.couchbase.client.java.manager.query.CreateQueryIndexOptions;
/**
* Parent class which drives all dynamic integration tests based on the configured cluster setup.
*
* @author Michael Reiche
*
* @since 2.0.0
*/
@ExtendWith(ClusterInvocationProvider.class)
@@ -215,4 +218,14 @@ public abstract class ClusterAwareIntegrationTests {
}
}
/**
* @return unique identifier for line - to use as key for documents to identify where they were created
*/
public static String loc() {
String uuid = UUID.randomUUID().toString();
String uid = uuid.substring(uuid.length() - 4);
StackTraceElement ste = Thread.currentThread().getStackTrace()[2];
return ste.getClassName() + ":" + ste.getMethodName() + ":" + ste.getLineNumber() + ":" + uid;
}
}