Polishing.

Switch to Flux.fromIterable(…) from StreamUtils in deleteAll(Iterable). Use switch expressions, refine toList/toCollection arrangement. Guard tests against absent ReplicaSet.

See #4838
Original pull request: #4843
This commit is contained in:
Mark Paluch
2024-11-28 15:56:22 +01:00
parent 721799dfdc
commit 5b86b2fa88
2 changed files with 48 additions and 28 deletions

View File

@@ -28,9 +28,9 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import org.reactivestreams.Publisher;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.data.domain.Example;
@@ -47,7 +47,6 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -264,8 +263,15 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
Assert.notNull(ids, "The given Iterable of Id's must not be null");
return deleteAllById(ids, getReadPreference());
}
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private Mono<Void> deleteAllById(Iterable<? extends ID> ids, Optional<ReadPreference> readPreference) {
Query query = getIdQuery(ids);
getReadPreference().ifPresent(query::withReadPreference);
readPreference.ifPresent(query::withReadPreference);
return mongoOperations.remove(query, entityInformation.getJavaType(), entityInformation.getCollectionName()).then();
}
@@ -274,10 +280,9 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
Assert.notNull(entities, "The given Iterable of entities must not be null");
Collection<? extends ID> ids = StreamUtils.createStreamFromIterator(entities.iterator())
.map(entityInformation::getId).collect(Collectors.toList());
return deleteAllById(ids);
Optional<ReadPreference> readPreference = getReadPreference();
return Flux.fromIterable(entities).map(entityInformation::getRequiredId).collectList()
.flatMap(ids -> deleteAllById(ids, readPreference));
}
@Override
@@ -464,10 +469,10 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
/**
* Transform the elements emitted by this Flux into Publishers, then flatten these inner publishers into a single
* Flux. The operation does not allow interleave between performing the map operation for the first and second source
* element guaranteeing the mapping operation completed before subscribing to its following inners, that will then be
* subscribed to eagerly emitting elements in order of their source.
*
* Flux. The operation does not allow interleaving between performing the map operation for the first and second
* source element guaranteeing the mapping operation completed before subscribing to its following inners, that will
* then be subscribed to eagerly emitting elements in order of their source.
*
* <pre class="code">
* Flux.just(first-element).flatMap(...)
* .concatWith(Flux.fromIterable(remaining-elements).flatMapSequential(...))
@@ -481,25 +486,23 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
static <T> Flux<T> concatMapSequentially(List<T> source,
Function<? super T, ? extends Publisher<? extends T>> mapper) {
if (source.isEmpty()) {
return Flux.empty();
}
if (source.size() == 1) {
return Flux.just(source.iterator().next()).flatMap(mapper);
}
if (source.size() == 2) {
return Flux.fromIterable(source).concatMap(mapper);
}
return switch (source.size()) {
case 0 -> Flux.empty();
case 1 -> Flux.just(source.get(0)).flatMap(mapper);
case 2 -> Flux.fromIterable(source).concatMap(mapper);
default -> {
Flux<T> first = Flux.just(source.get(0)).flatMap(mapper);
Flux<T> theRest = Flux.fromIterable(source.subList(1, source.size())).flatMapSequential(mapper);
return first.concatWith(theRest);
Flux<T> first = Flux.just(source.get(0)).flatMap(mapper);
Flux<T> theRest = Flux.fromIterable(source.subList(1, source.size())).flatMapSequential(mapper);
yield first.concatWith(theRest);
}
};
}
static <T> Flux<T> concatMapSequentially(Publisher<T> publisher,
Function<? super T, ? extends Publisher<? extends T>> mapper) {
return Flux.from(publisher).switchOnFirst(((signal, source) -> {
return Flux.from(publisher).switchOnFirst((signal, source) -> {
if (!signal.hasValue()) {
return source.concatMap(mapper);
@@ -507,16 +510,30 @@ public class SimpleReactiveMongoRepository<T, ID extends Serializable> implement
Mono<T> firstCall = Mono.from(mapper.apply(signal.get()));
return firstCall.concatWith(source.skip(1).flatMapSequential(mapper));
}));
});
}
private static <E> List<E> toList(Iterable<E> source) {
return source instanceof List<E> list ? list : new ArrayList<>(toCollection(source));
Collection<E> collection = toCollection(source);
if (collection instanceof List<E> list) {
return list;
}
return new ArrayList<>(collection);
}
private static <E> Collection<E> toCollection(Iterable<E> source) {
return source instanceof Collection<E> collection ? collection
: StreamUtils.createStreamFromIterator(source.iterator()).collect(Collectors.toList());
if (source instanceof Collection<E> collection) {
return collection;
}
List<E> list = new ArrayList<>();
source.forEach(list::add);
return list;
}
/**

View File

@@ -46,6 +46,7 @@ import org.springframework.data.mongodb.ReactiveMongoTransactionManager;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory;
import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository;
import org.springframework.data.mongodb.test.util.EnableIfReplicaSetAvailable;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.lang.Nullable;
@@ -339,6 +340,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@RepeatedTest(10) // GH-4838
@EnableIfReplicaSetAvailable
void transactionalSaveAllForStuffThatIsConsideredAnUpdateOfExistingData() {
ReactiveMongoTransactionManager txmgr = new ReactiveMongoTransactionManager(template.getMongoDatabaseFactory());
@@ -349,6 +351,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
}
@RepeatedTest(10) // GH-4838
@EnableIfReplicaSetAvailable
void transactionalSaveAllWithPublisherForStuffThatIsConsideredAnUpdateOfExistingData() {
ReactiveMongoTransactionManager txmgr = new ReactiveMongoTransactionManager(template.getMongoDatabaseFactory());