Support ReadConcern & ReadPreference via the Query and Aggregation API.
Add support for setting the ReadConcern and ReadPreference via the Query and Aggregation API. Closes: #4277, #4286 Original Pull Request: #4288
This commit is contained in:
committed by
Christoph Strobl
parent
368c644922
commit
c5c6fc107c
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.mongodb.core;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.client.MongoCollection;
|
||||
|
||||
/**
|
||||
* Interface for functional preparation of a {@link MongoCollection}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface CollectionPreparer<T> {
|
||||
|
||||
/**
|
||||
* Returns a preparer that always returns its input collection.
|
||||
*
|
||||
* @return a preparer that always returns its input collection.
|
||||
*/
|
||||
static <T> CollectionPreparer<T> identity() {
|
||||
return it -> it;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the {@code collection}.
|
||||
*
|
||||
* @param collection the collection to prepare.
|
||||
* @return the prepared collection.
|
||||
*/
|
||||
T prepare(T collection);
|
||||
|
||||
/**
|
||||
* Returns a composed {@code CollectionPreparer} that first applies this preparer to the collection, and then applies
|
||||
* the {@code after} preparer to the result. If evaluation of either function throws an exception, it is relayed to
|
||||
* the caller of the composed function.
|
||||
*
|
||||
* @param after the collection preparer to apply after this function is applied.
|
||||
* @return a composed {@code CollectionPreparer} that first applies this preparer and then applies the {@code after}
|
||||
* preparer.
|
||||
*/
|
||||
default CollectionPreparer<T> andThen(CollectionPreparer<T> after) {
|
||||
Assert.notNull(after, "After CollectionPreparer must not be null");
|
||||
return c -> after.prepare(prepare(c));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.mongodb.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
import com.mongodb.ReadConcern;
|
||||
import com.mongodb.ReadPreference;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
|
||||
/**
|
||||
* Support class for delegate implementations to apply {@link ReadConcern} and {@link ReadPreference} settings upon
|
||||
* {@link CollectionPreparer preparing a collection}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.1
|
||||
*/
|
||||
class CollectionPreparerSupport implements ReadConcernAware, ReadPreferenceAware {
|
||||
|
||||
private final List<Object> sources;
|
||||
|
||||
private CollectionPreparerSupport(List<Object> sources) {
|
||||
this.sources = sources;
|
||||
}
|
||||
|
||||
<T> T doPrepare(T collection, Function<T, ReadConcern> concernAccessor, BiFunction<T, ReadConcern, T> concernFunction,
|
||||
Function<T, ReadPreference> preferenceAccessor, BiFunction<T, ReadPreference, T> preferenceFunction) {
|
||||
|
||||
T collectionToUse = collection;
|
||||
|
||||
for (Object source : sources) {
|
||||
if (source instanceof ReadConcernAware rca && rca.hasReadConcern()) {
|
||||
|
||||
ReadConcern concern = rca.getReadConcern();
|
||||
if (concernAccessor.apply(collectionToUse) != concern) {
|
||||
collectionToUse = concernFunction.apply(collectionToUse, concern);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (Object source : sources) {
|
||||
if (source instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
|
||||
|
||||
ReadPreference preference = rpa.getReadPreference();
|
||||
if (preferenceAccessor.apply(collectionToUse) != preference) {
|
||||
collectionToUse = preferenceFunction.apply(collectionToUse, preference);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return collectionToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReadConcern() {
|
||||
|
||||
for (Object aware : sources) {
|
||||
if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadConcern getReadConcern() {
|
||||
|
||||
for (Object aware : sources) {
|
||||
if (aware instanceof ReadConcernAware rca && rca.hasReadConcern()) {
|
||||
return rca.getReadConcern();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReadPreference() {
|
||||
|
||||
for (Object aware : sources) {
|
||||
if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadPreference getReadPreference() {
|
||||
|
||||
for (Object aware : sources) {
|
||||
if (aware instanceof ReadPreferenceAware rpa && rpa.hasReadPreference()) {
|
||||
return rpa.getReadPreference();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static class CollectionPreparerDelegate extends CollectionPreparerSupport
|
||||
implements CollectionPreparer<MongoCollection<Document>> {
|
||||
|
||||
private CollectionPreparerDelegate(List<Object> sources) {
|
||||
super(sources);
|
||||
}
|
||||
|
||||
public static CollectionPreparerDelegate of(ReadPreferenceAware... awares) {
|
||||
return of((Object[]) awares);
|
||||
}
|
||||
|
||||
public static CollectionPreparerDelegate of(Object... mixedAwares) {
|
||||
|
||||
if (mixedAwares.length == 1 && mixedAwares[0] instanceof CollectionPreparerDelegate) {
|
||||
return (CollectionPreparerDelegate) mixedAwares[0];
|
||||
}
|
||||
|
||||
return new CollectionPreparerDelegate(Arrays.asList(mixedAwares));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MongoCollection<Document> prepare(MongoCollection<Document> collection) {
|
||||
return doPrepare(collection, MongoCollection::getReadConcern, MongoCollection::withReadConcern,
|
||||
MongoCollection::getReadPreference, MongoCollection::withReadPreference);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ReactiveCollectionPreparerDelegate extends CollectionPreparerSupport
|
||||
implements CollectionPreparer<com.mongodb.reactivestreams.client.MongoCollection<Document>> {
|
||||
|
||||
private ReactiveCollectionPreparerDelegate(List<Object> sources) {
|
||||
super(sources);
|
||||
}
|
||||
|
||||
public static ReactiveCollectionPreparerDelegate of(ReadPreferenceAware... awares) {
|
||||
return of((Object[]) awares);
|
||||
}
|
||||
|
||||
public static ReactiveCollectionPreparerDelegate of(Object... mixedAwares) {
|
||||
|
||||
if (mixedAwares.length == 1 && mixedAwares[0] instanceof CollectionPreparerDelegate) {
|
||||
return (ReactiveCollectionPreparerDelegate) mixedAwares[0];
|
||||
}
|
||||
|
||||
return new ReactiveCollectionPreparerDelegate(Arrays.asList(mixedAwares));
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.mongodb.reactivestreams.client.MongoCollection<Document> prepare(
|
||||
com.mongodb.reactivestreams.client.MongoCollection<Document> collection) {
|
||||
return doPrepare(collection, //
|
||||
com.mongodb.reactivestreams.client.MongoCollection::getReadConcern,
|
||||
com.mongodb.reactivestreams.client.MongoCollection::withReadConcern,
|
||||
com.mongodb.reactivestreams.client.MongoCollection::getReadPreference,
|
||||
com.mongodb.reactivestreams.client.MongoCollection::withReadPreference);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.bson.Document;
|
||||
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
@@ -168,7 +167,8 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation {
|
||||
Document queryObject = query.getQueryObject();
|
||||
Document fieldsObject = query.getFieldsObject();
|
||||
|
||||
return template.doFind(getCollectionName(), queryObject, fieldsObject, domainType, returnType,
|
||||
return template.doFind(template.createDelegate(query), getCollectionName(), queryObject, fieldsObject, domainType,
|
||||
returnType,
|
||||
getCursorPreparer(query, preparer));
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.springframework.data.mongodb.MongoDatabaseFactory;
|
||||
import org.springframework.data.mongodb.MongoDatabaseUtils;
|
||||
import org.springframework.data.mongodb.SessionSynchronization;
|
||||
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
|
||||
import org.springframework.data.mongodb.core.CollectionPreparerSupport.CollectionPreparerDelegate;
|
||||
import org.springframework.data.mongodb.core.DefaultBulkOperations.BulkOperationContext;
|
||||
import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
|
||||
import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
|
||||
@@ -66,6 +67,7 @@ import org.springframework.data.mongodb.core.QueryOperations.UpdateContext;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOptions.Builder;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationPipeline;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
|
||||
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
@@ -83,7 +85,6 @@ import org.springframework.data.mongodb.core.mapreduce.MapReduceResults;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Meta;
|
||||
import org.springframework.data.mongodb.core.query.Meta.CursorOption;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.UpdateDefinition;
|
||||
@@ -141,7 +142,8 @@ import com.mongodb.client.result.UpdateResult;
|
||||
* @author Bartłomiej Mazur
|
||||
* @author Michael Krog
|
||||
*/
|
||||
public class MongoTemplate implements MongoOperations, ApplicationContextAware, IndexOperationsProvider {
|
||||
public class MongoTemplate
|
||||
implements MongoOperations, ApplicationContextAware, IndexOperationsProvider, ReadPreferenceAware {
|
||||
|
||||
private static final Log LOGGER = LogFactory.getLog(MongoTemplate.class);
|
||||
private static final WriteResultChecking DEFAULT_WRITE_RESULT_CHECKING = WriteResultChecking.NONE;
|
||||
@@ -293,6 +295,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
this.readPreference = readPreference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReadPreference() {
|
||||
return this.readPreference != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadPreference getReadPreference() {
|
||||
return this.readPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure whether lifecycle events such as {@link AfterLoadEvent}, {@link BeforeSaveEvent}, etc. should be
|
||||
* published or whether emission should be suppressed. Enabled by default.
|
||||
@@ -363,10 +375,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
if (enabled) {
|
||||
|
||||
this.countExecution = (collectionName, filter, options) -> {
|
||||
this.countExecution = (collectionPreparer, collectionName, filter, options) -> {
|
||||
|
||||
if (!estimationFilter.test(filter, options)) {
|
||||
return doExactCount(collectionName, filter, options);
|
||||
return doExactCount(collectionPreparer, collectionName, filter, options);
|
||||
}
|
||||
|
||||
EstimatedDocumentCountOptions estimatedDocumentCountOptions = new EstimatedDocumentCountOptions();
|
||||
@@ -374,7 +386,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
estimatedDocumentCountOptions.maxTime(options.getMaxTime(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
return doEstimatedCount(collectionName, estimatedDocumentCountOptions);
|
||||
return doEstimatedCount(collectionPreparer, collectionName, estimatedDocumentCountOptions);
|
||||
};
|
||||
} else {
|
||||
this.countExecution = this::doExactCount;
|
||||
@@ -443,8 +455,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Document mappedQuery = queryContext.getMappedQuery(persistentEntity);
|
||||
Document mappedFields = queryContext.getMappedFields(persistentEntity, projection);
|
||||
|
||||
CollectionPreparerDelegate readPreference = createDelegate(query);
|
||||
FindIterable<Document> cursor = new QueryCursorPreparer(query, entityType).initiateFind(collection,
|
||||
col -> col.find(mappedQuery, Document.class).projection(mappedFields));
|
||||
col -> readPreference.prepare(col).find(mappedQuery, Document.class).projection(mappedFields));
|
||||
|
||||
return new CloseableIterableCursorAdapter<>(cursor, exceptionTranslator,
|
||||
new ProjectingReadCallback<>(mongoConverter, projection, collectionName)).stream();
|
||||
@@ -517,7 +530,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
serializeToJsonSafely(queryObject), sortObject, fieldsObject, collectionName));
|
||||
}
|
||||
|
||||
this.executeQueryInternal(new FindCallback(queryObject, fieldsObject, null),
|
||||
this.executeQueryInternal(new FindCallback(createDelegate(query), queryObject, fieldsObject, null),
|
||||
preparer != null ? preparer : CursorPreparer.NO_OP_PREPARER, documentCallbackHandler, collectionName);
|
||||
}
|
||||
|
||||
@@ -765,7 +778,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
if (ObjectUtils.isEmpty(query.getSortObject())) {
|
||||
|
||||
return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
return doFindOne(createDelegate(query), collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
new QueryCursorPreparer(query, entityClass), entityClass);
|
||||
} else {
|
||||
query.limit(1);
|
||||
@@ -797,7 +810,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Document mappedQuery = queryContext.getMappedQuery(entityClass, this::getPersistentEntity);
|
||||
|
||||
return execute(collectionName,
|
||||
new ExistsCallback(mappedQuery, queryContext.getCollation(entityClass).orElse(null)));
|
||||
new ExistsCallback(createDelegate(query), mappedQuery, queryContext.getCollation(entityClass).orElse(null)));
|
||||
}
|
||||
|
||||
// Find methods that take a Query to express the query and that return a List of objects.
|
||||
@@ -814,7 +827,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Assert.notNull(collectionName, "CollectionName must not be null");
|
||||
Assert.notNull(entityClass, "EntityClass must not be null");
|
||||
|
||||
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||
return doFind(createDelegate(query), collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||
new QueryCursorPreparer(query, entityClass));
|
||||
}
|
||||
|
||||
@@ -834,7 +847,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
String idKey = operations.getIdPropertyName(entityClass);
|
||||
|
||||
return doFindOne(collectionName, new Document(idKey, id), new Document(), entityClass);
|
||||
return doFindOne(CollectionPreparer.identity(), collectionName, new Document(idKey, id), new Document(),
|
||||
entityClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -867,10 +881,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
serializeToJsonSafely(mappedQuery), field, collectionName));
|
||||
}
|
||||
|
||||
QueryCursorPreparer preparer = new QueryCursorPreparer(query, entityClass);
|
||||
if (preparer.hasReadPreference()) {
|
||||
collection = collection.withReadPreference(preparer.getReadPreference());
|
||||
}
|
||||
collection = createDelegate(query).prepare(collection);
|
||||
|
||||
DistinctIterable<T> iterable = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType);
|
||||
distinctQueryContext.applyCollation(entityClass, iterable::collation);
|
||||
@@ -920,8 +931,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
String collection = StringUtils.hasText(collectionName) ? collectionName : getCollectionName(domainType);
|
||||
String distanceField = operations.nearQueryDistanceFieldName(domainType);
|
||||
|
||||
Builder optionsBuilder = AggregationOptions.builder().collation(near.getCollation());
|
||||
Query query = near.getQuery();
|
||||
|
||||
if (query != null && query.hasReadPreference()) {
|
||||
optionsBuilder.readPreference(query.getReadPreference());
|
||||
}
|
||||
|
||||
Aggregation $geoNear = TypedAggregation.newAggregation(domainType, Aggregation.geoNear(near, distanceField))
|
||||
.withOptions(AggregationOptions.builder().collation(near.getCollation()).build());
|
||||
.withOptions(optionsBuilder.build());
|
||||
|
||||
AggregationResults<Document> results = aggregate($geoNear, collection, Document.class);
|
||||
EntityProjection<T, ?> projection = operations.introspectProjection(returnType, domainType);
|
||||
@@ -986,7 +1004,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
operations.forType(entityClass).getCollation(query).ifPresent(optionsToUse::collation);
|
||||
}
|
||||
|
||||
return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
return doFindAndModify(createDelegate(query), collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
|
||||
}
|
||||
|
||||
@@ -1008,6 +1026,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
QueryContext queryContext = queryOperations.createQueryContext(query);
|
||||
|
||||
EntityProjection<T, S> projection = operations.introspectProjection(resultType, entityType);
|
||||
CollectionPreparerDelegate collectionPreparer = createDelegate(query);
|
||||
Document mappedQuery = queryContext.getMappedQuery(entity);
|
||||
Document mappedFields = queryContext.getMappedFields(entity, projection);
|
||||
Document mappedSort = queryContext.getMappedSort(entity);
|
||||
@@ -1018,7 +1037,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
maybeEmitEvent(new BeforeSaveEvent<>(replacement, mappedReplacement, collectionName));
|
||||
maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
|
||||
|
||||
T saved = doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort,
|
||||
T saved = doFindAndReplace(collectionPreparer, collectionName, mappedQuery, mappedFields, mappedSort,
|
||||
queryContext.getCollation(entityType).orElse(null), entityType, mappedReplacement, options, projection);
|
||||
|
||||
if (saved != null) {
|
||||
@@ -1046,7 +1065,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Assert.notNull(entityClass, "EntityClass must not be null");
|
||||
Assert.notNull(collectionName, "CollectionName must not be null");
|
||||
|
||||
return doFindAndRemove(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
return doFindAndRemove(createDelegate(query), collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
getMappedSortObject(query, entityClass), operations.forType(entityClass).getCollation(query).orElse(null),
|
||||
entityClass);
|
||||
}
|
||||
@@ -1078,17 +1097,19 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
CountOptions options = countContext.getCountOptions(entityClass);
|
||||
Document mappedQuery = countContext.getMappedQuery(entityClass, mappingContext::getPersistentEntity);
|
||||
|
||||
return doCount(collectionName, mappedQuery, options);
|
||||
CollectionPreparerDelegate readPreference = createDelegate(query);
|
||||
return doCount(readPreference, collectionName, mappedQuery, options);
|
||||
}
|
||||
|
||||
protected long doCount(String collectionName, Document filter, CountOptions options) {
|
||||
protected long doCount(CollectionPreparer collectionPreparer, String collectionName, Document filter,
|
||||
CountOptions options) {
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER
|
||||
.debug(String.format("Executing count: %s in collection: %s", serializeToJsonSafely(filter), collectionName));
|
||||
}
|
||||
|
||||
return countExecution.countDocuments(collectionName, filter, options);
|
||||
return countExecution.countDocuments(collectionPreparer, collectionName, filter, options);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1097,11 +1118,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
@Override
|
||||
public long estimatedCount(String collectionName) {
|
||||
return doEstimatedCount(collectionName, new EstimatedDocumentCountOptions());
|
||||
return doEstimatedCount(CollectionPreparerDelegate.of(this), collectionName, new EstimatedDocumentCountOptions());
|
||||
}
|
||||
|
||||
protected long doEstimatedCount(String collectionName, EstimatedDocumentCountOptions options) {
|
||||
return execute(collectionName, collection -> collection.estimatedDocumentCount(options));
|
||||
protected long doEstimatedCount(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
||||
String collectionName,
|
||||
EstimatedDocumentCountOptions options) {
|
||||
return execute(collectionName,
|
||||
collection -> collectionPreparer.prepare(collection).estimatedDocumentCount(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1112,12 +1136,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
CountOptions options = countContext.getCountOptions(entityClass);
|
||||
Document mappedQuery = countContext.getMappedQuery(entityClass, mappingContext::getPersistentEntity);
|
||||
|
||||
return doExactCount(collectionName, mappedQuery, options);
|
||||
return doExactCount(createDelegate(query), collectionName, mappedQuery, options);
|
||||
}
|
||||
|
||||
protected long doExactCount(String collectionName, Document filter, CountOptions options) {
|
||||
return execute(collectionName,
|
||||
collection -> collection.countDocuments(CountQuery.of(filter).toQueryDocument(), options));
|
||||
protected long doExactCount(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName,
|
||||
Document filter, CountOptions options) {
|
||||
return execute(collectionName, collection -> collectionPreparer.prepare(collection)
|
||||
.countDocuments(CountQuery.of(filter).toQueryDocument(), options));
|
||||
}
|
||||
|
||||
protected boolean countCanBeEstimated(Document filter, CountOptions options) {
|
||||
@@ -1177,8 +1202,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
protected MongoCollection<Document> prepareCollection(MongoCollection<Document> collection) {
|
||||
|
||||
if (this.readPreference != null) {
|
||||
collection = collection.withReadPreference(readPreference);
|
||||
if (this.readPreference != null && this.readPreference != collection.getReadPreference()) {
|
||||
return collection.withReadPreference(readPreference);
|
||||
}
|
||||
|
||||
return collection;
|
||||
@@ -1754,7 +1779,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
@Override
|
||||
public <T> List<T> findAll(Class<T> entityClass, String collectionName) {
|
||||
return executeFindMultiInternal(
|
||||
new FindCallback(new Document(), new Document(),
|
||||
new FindCallback(CollectionPreparer.identity(), new Document(), new Document(),
|
||||
operations.forType(entityClass).getCollation().map(Collation::toMongoCollation).orElse(null)),
|
||||
CursorPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
|
||||
collectionName);
|
||||
@@ -1812,7 +1837,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
String mapFunc = replaceWithResourceIfNecessary(mapFunction);
|
||||
String reduceFunc = replaceWithResourceIfNecessary(reduceFunction);
|
||||
MongoCollection<Document> inputCollection = getAndPrepareCollection(doGetDatabase(), inputCollectionName);
|
||||
CollectionPreparerDelegate readPreference = createDelegate(query);
|
||||
MongoCollection<Document> inputCollection = readPreference
|
||||
.prepare(getAndPrepareCollection(doGetDatabase(), inputCollectionName));
|
||||
|
||||
// MapReduceOp
|
||||
MapReduceIterable<Document> mapReduce = inputCollection.mapReduce(mapFunc, reduceFunc, Document.class);
|
||||
@@ -1977,6 +2004,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
if (!CollectionUtils.isEmpty(result)) {
|
||||
|
||||
Query byIdInQuery = operations.getByIdInQuery(result);
|
||||
if (query.hasReadPreference()) {
|
||||
byIdInQuery.withReadPreference(query.getReadPreference());
|
||||
}
|
||||
|
||||
remove(byIdInQuery, entityClass, collectionName);
|
||||
}
|
||||
@@ -2032,7 +2062,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
return execute(collectionName, collection -> {
|
||||
|
||||
List<Document> rawResult = new ArrayList<>();
|
||||
|
||||
CollectionPreparerDelegate delegate = CollectionPreparerDelegate.of(options);
|
||||
Class<?> domainType = aggregation instanceof TypedAggregation ? ((TypedAggregation<?>) aggregation).getInputType()
|
||||
: null;
|
||||
|
||||
@@ -2040,7 +2070,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
() -> operations.forType(domainType) //
|
||||
.getCollation());
|
||||
|
||||
AggregateIterable<Document> aggregateIterable = collection.aggregate(pipeline, Document.class) //
|
||||
AggregateIterable<Document> aggregateIterable = delegate.prepare(collection).aggregate(pipeline, Document.class) //
|
||||
.collation(collation.map(Collation::toMongoCollation).orElse(null)) //
|
||||
.allowDiskUse(options.isAllowDiskUse());
|
||||
|
||||
@@ -2103,7 +2133,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
return execute(collectionName, (CollectionCallback<Stream<O>>) collection -> {
|
||||
|
||||
AggregateIterable<Document> cursor = collection.aggregate(pipeline, Document.class) //
|
||||
CollectionPreparerDelegate delegate = CollectionPreparerDelegate.of(options);
|
||||
|
||||
AggregateIterable<Document> cursor = delegate.prepare(collection).aggregate(pipeline, Document.class) //
|
||||
.allowDiskUse(options.isAllowDiskUse());
|
||||
|
||||
if (options.getCursorBatchSize() != null) {
|
||||
@@ -2350,8 +2382,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* @return the converted object or {@literal null} if none exists.
|
||||
*/
|
||||
@Nullable
|
||||
protected <T> T doFindOne(String collectionName, Document query, Document fields, Class<T> entityClass) {
|
||||
return doFindOne(collectionName, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass);
|
||||
protected <T> T doFindOne(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
||||
Document fields, Class<T> entityClass) {
|
||||
return doFindOne(collectionPreparer, collectionName, query, fields, CursorPreparer.NO_OP_PREPARER, entityClass);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2368,8 +2401,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
protected <T> T doFindOne(String collectionName, Document query, Document fields, CursorPreparer preparer,
|
||||
Class<T> entityClass) {
|
||||
protected <T> T doFindOne(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
||||
Document fields, CursorPreparer preparer, Class<T> entityClass) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
@@ -2382,7 +2415,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
|
||||
}
|
||||
|
||||
return executeFindOneInternal(new FindOneCallback(mappedQuery, mappedFields, preparer),
|
||||
return executeFindOneInternal(new FindOneCallback(collectionPreparer, mappedQuery, mappedFields, preparer),
|
||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||
}
|
||||
|
||||
@@ -2396,8 +2429,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @return the List of converted objects.
|
||||
*/
|
||||
protected <T> List<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass) {
|
||||
return doFind(collectionName, query, fields, entityClass, null,
|
||||
protected <T> List<T> doFind(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
||||
Document fields, Class<T> entityClass) {
|
||||
return doFind(collectionPreparer, collectionName, query, fields, entityClass, null,
|
||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName));
|
||||
}
|
||||
|
||||
@@ -2414,14 +2448,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* (apply limits, skips and so on).
|
||||
* @return the {@link List} of converted objects.
|
||||
*/
|
||||
protected <T> List<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass,
|
||||
CursorPreparer preparer) {
|
||||
return doFind(collectionName, query, fields, entityClass, preparer,
|
||||
protected <T> List<T> doFind(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
||||
Document fields, Class<T> entityClass, CursorPreparer preparer) {
|
||||
return doFind(collectionPreparer, collectionName, query, fields, entityClass, preparer,
|
||||
new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName));
|
||||
}
|
||||
|
||||
protected <S, T> List<T> doFind(String collectionName, Document query, Document fields, Class<S> entityClass,
|
||||
@Nullable CursorPreparer preparer, DocumentCallback<T> objectCallback) {
|
||||
protected <S, T> List<T> doFind(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
||||
Document fields, Class<S> entityClass, @Nullable CursorPreparer preparer, DocumentCallback<T> objectCallback) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
@@ -2434,7 +2468,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName));
|
||||
}
|
||||
|
||||
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields, null),
|
||||
return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields, null),
|
||||
preparer != null ? preparer : CursorPreparer.NO_OP_PREPARER, objectCallback, collectionName);
|
||||
}
|
||||
|
||||
@@ -2444,8 +2478,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
<S, T> List<T> doFind(String collectionName, Document query, Document fields, Class<S> sourceClass,
|
||||
Class<T> targetClass, CursorPreparer preparer) {
|
||||
<S, T> List<T> doFind(CollectionPreparer collectionPreparer, String collectionName, Document query, Document fields,
|
||||
Class<S> sourceClass, Class<T> targetClass, CursorPreparer preparer) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(sourceClass);
|
||||
EntityProjection<T, S> projection = operations.introspectProjection(targetClass, sourceClass);
|
||||
@@ -2459,7 +2493,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
serializeToJsonSafely(mappedQuery), mappedFields, sourceClass, collectionName));
|
||||
}
|
||||
|
||||
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields, null), preparer,
|
||||
return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields, null), preparer,
|
||||
new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
|
||||
}
|
||||
|
||||
@@ -2533,8 +2567,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* @return the List of converted objects.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
protected <T> T doFindAndRemove(String collectionName, Document query, Document fields, Document sort,
|
||||
@Nullable Collation collation, Class<T> entityClass) {
|
||||
protected <T> T doFindAndRemove(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
||||
Document fields, Document sort, @Nullable Collation collation, Class<T> entityClass) {
|
||||
|
||||
EntityReader<? super T, Bson> readerToUse = this.mongoConverter;
|
||||
|
||||
@@ -2545,14 +2579,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
return executeFindOneInternal(
|
||||
new FindAndRemoveCallback(queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
||||
return executeFindOneInternal(new FindAndRemoveCallback(collectionPreparer,
|
||||
queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
||||
new ReadDocumentCallback<>(readerToUse, entityClass, collectionName), collectionName);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
protected <T> T doFindAndModify(String collectionName, Document query, Document fields, Document sort,
|
||||
Class<T> entityClass, UpdateDefinition update, @Nullable FindAndModifyOptions options) {
|
||||
protected <T> T doFindAndModify(CollectionPreparer collectionPreparer, String collectionName, Document query,
|
||||
Document fields, Document sort, Class<T> entityClass, UpdateDefinition update,
|
||||
@Nullable FindAndModifyOptions options) {
|
||||
|
||||
EntityReader<? super T, Bson> readerToUse = this.mongoConverter;
|
||||
|
||||
@@ -2577,7 +2612,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
}
|
||||
|
||||
return executeFindOneInternal(
|
||||
new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate,
|
||||
new FindAndModifyCallback(collectionPreparer, mappedQuery, fields, sort, mappedUpdate,
|
||||
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
|
||||
new ReadDocumentCallback<>(readerToUse, entityClass, collectionName), collectionName);
|
||||
}
|
||||
@@ -2598,14 +2633,18 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
||||
*/
|
||||
@Nullable
|
||||
protected <T> T doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
|
||||
Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, Class<?> entityType,
|
||||
Document replacement, FindAndReplaceOptions options, Class<T> resultType) {
|
||||
protected <T> T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, Document mappedQuery,
|
||||
Document mappedFields, Document mappedSort, @Nullable com.mongodb.client.model.Collation collation,
|
||||
Class<?> entityType, Document replacement, FindAndReplaceOptions options, Class<T> resultType) {
|
||||
|
||||
EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType);
|
||||
|
||||
return doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort, collation, entityType, replacement,
|
||||
options, projection);
|
||||
return doFindAndReplace(collectionPreparer, collectionName, mappedQuery, mappedFields, mappedSort, collation,
|
||||
entityType, replacement, options, projection);
|
||||
}
|
||||
|
||||
CollectionPreparerDelegate createDelegate(Query query) {
|
||||
return CollectionPreparerDelegate.of(query);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2625,9 +2664,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
* @since 3.4
|
||||
*/
|
||||
@Nullable
|
||||
private <T> T doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
|
||||
Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, Class<?> entityType,
|
||||
Document replacement, FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
|
||||
private <T> T doFindAndReplace(CollectionPreparer collectionPreparer, String collectionName, Document mappedQuery,
|
||||
Document mappedFields, Document mappedSort, @Nullable com.mongodb.client.model.Collation collation,
|
||||
Class<?> entityType, Document replacement, FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER
|
||||
@@ -2638,9 +2677,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
serializeToJsonSafely(mappedSort), entityType, serializeToJsonSafely(replacement), collectionName));
|
||||
}
|
||||
|
||||
return executeFindOneInternal(
|
||||
new FindAndReplaceCallback(mappedQuery, mappedFields, mappedSort, replacement, collation, options),
|
||||
new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
|
||||
return executeFindOneInternal(new FindAndReplaceCallback(collectionPreparer, mappedQuery, mappedFields, mappedSort,
|
||||
replacement, collation, options), new ProjectingReadCallback<>(mongoConverter, projection, collectionName),
|
||||
collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2810,12 +2849,15 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
private static class FindOneCallback implements CollectionCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Optional<Document> fields;
|
||||
private final CursorPreparer cursorPreparer;
|
||||
|
||||
FindOneCallback(Document query, Document fields, CursorPreparer preparer) {
|
||||
FindOneCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields,
|
||||
CursorPreparer preparer) {
|
||||
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = Optional.of(fields).filter(it -> !ObjectUtils.isEmpty(fields));
|
||||
this.cursorPreparer = preparer;
|
||||
@@ -2824,7 +2866,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
@Override
|
||||
public Document doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
||||
|
||||
FindIterable<Document> iterable = cursorPreparer.initiateFind(collection, col -> col.find(query, Document.class));
|
||||
FindIterable<Document> iterable = cursorPreparer.initiateFind(collection,
|
||||
col -> collectionPreparer.prepare(col).find(query, Document.class));
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
|
||||
@@ -2851,15 +2894,19 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
private static class FindCallback implements CollectionCallback<FindIterable<Document>> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Document fields;
|
||||
private final @Nullable com.mongodb.client.model.Collation collation;
|
||||
|
||||
public FindCallback(Document query, Document fields, @Nullable com.mongodb.client.model.Collation collation) {
|
||||
public FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
||||
Document fields,
|
||||
@Nullable com.mongodb.client.model.Collation collation) {
|
||||
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(fields, "Fields must not be null");
|
||||
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
this.collation = collation;
|
||||
@@ -2869,7 +2916,8 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
public FindIterable<Document> doInCollection(MongoCollection<Document> collection)
|
||||
throws MongoException, DataAccessException {
|
||||
|
||||
FindIterable<Document> findIterable = collection.find(query, Document.class).projection(fields);
|
||||
FindIterable<Document> findIterable = collectionPreparer.prepare(collection).find(query, Document.class)
|
||||
.projection(fields);
|
||||
|
||||
if (collation != null) {
|
||||
findIterable = findIterable.collation(collation);
|
||||
@@ -2887,11 +2935,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
private class ExistsCallback implements CollectionCallback<Boolean> {
|
||||
|
||||
private final CollectionPreparer collectionPreparer;
|
||||
private final Document mappedQuery;
|
||||
private final com.mongodb.client.model.Collation collation;
|
||||
|
||||
ExistsCallback(Document mappedQuery, com.mongodb.client.model.Collation collation) {
|
||||
ExistsCallback(CollectionPreparer collectionPreparer, Document mappedQuery,
|
||||
com.mongodb.client.model.Collation collation) {
|
||||
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.mappedQuery = mappedQuery;
|
||||
this.collation = collation;
|
||||
}
|
||||
@@ -2899,7 +2950,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
@Override
|
||||
public Boolean doInCollection(MongoCollection<Document> collection) throws MongoException, DataAccessException {
|
||||
|
||||
return doCount(collection.getNamespace().getCollectionName(), mappedQuery,
|
||||
return doCount(collectionPreparer, collection.getNamespace().getCollectionName(), mappedQuery,
|
||||
new CountOptions().limit(1).collation(collation)) > 0;
|
||||
}
|
||||
}
|
||||
@@ -2912,12 +2963,16 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
private static class FindAndRemoveCallback implements CollectionCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Document fields;
|
||||
private final Document sort;
|
||||
private final Optional<Collation> collation;
|
||||
|
||||
FindAndRemoveCallback(Document query, Document fields, Document sort, @Nullable Collation collation) {
|
||||
FindAndRemoveCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
||||
Document fields, Document sort,
|
||||
@Nullable Collation collation) {
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
@@ -2931,12 +2986,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
FindOneAndDeleteOptions opts = new FindOneAndDeleteOptions().sort(sort).projection(fields);
|
||||
collation.map(Collation::toMongoCollation).ifPresent(opts::collation);
|
||||
|
||||
return collection.findOneAndDelete(query, opts);
|
||||
return collectionPreparer.prepare(collection).findOneAndDelete(query, opts);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FindAndModifyCallback implements CollectionCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Document fields;
|
||||
private final Document sort;
|
||||
@@ -2944,9 +3000,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
private final List<Document> arrayFilters;
|
||||
private final FindAndModifyOptions options;
|
||||
|
||||
FindAndModifyCallback(Document query, Document fields, Document sort, Object update, List<Document> arrayFilters,
|
||||
FindAndModifyOptions options) {
|
||||
FindAndModifyCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
||||
Document fields, Document sort,
|
||||
Object update, List<Document> arrayFilters, FindAndModifyOptions options) {
|
||||
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
this.sort = sort;
|
||||
@@ -2975,9 +3033,9 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
}
|
||||
|
||||
if (update instanceof Document) {
|
||||
return collection.findOneAndUpdate(query, (Document) update, opts);
|
||||
return collectionPreparer.prepare(collection).findOneAndUpdate(query, (Document) update, opts);
|
||||
} else if (update instanceof List) {
|
||||
return collection.findOneAndUpdate(query, (List<Document>) update, opts);
|
||||
return collectionPreparer.prepare(collection).findOneAndUpdate(query, (List<Document>) update, opts);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Using %s is not supported in findOneAndUpdate", update));
|
||||
@@ -2993,6 +3051,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
*/
|
||||
private static class FindAndReplaceCallback implements CollectionCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Document fields;
|
||||
private final Document sort;
|
||||
@@ -3000,9 +3059,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
private final @Nullable com.mongodb.client.model.Collation collation;
|
||||
private final FindAndReplaceOptions options;
|
||||
|
||||
FindAndReplaceCallback(Document query, Document fields, Document sort, Document update,
|
||||
@Nullable com.mongodb.client.model.Collation collation, FindAndReplaceOptions options) {
|
||||
|
||||
FindAndReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
||||
Document fields, Document sort,
|
||||
Document update, @Nullable com.mongodb.client.model.Collation collation, FindAndReplaceOptions options) {
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
this.sort = sort;
|
||||
@@ -3027,7 +3087,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
opts.returnDocument(ReturnDocument.AFTER);
|
||||
}
|
||||
|
||||
return collection.findOneAndReplace(query, update, opts);
|
||||
return collectionPreparer.prepare(collection).findOneAndReplace(query, update, opts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3209,11 +3269,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
return cursorToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadPreference getReadPreference() {
|
||||
return query.getMeta().getFlags().contains(CursorOption.SECONDARY_READS) ? ReadPreference.primaryPreferred()
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3399,6 +3454,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
|
||||
@FunctionalInterface
|
||||
interface CountExecution {
|
||||
long countDocuments(String collection, Document filter, CountOptions options);
|
||||
long countDocuments(CollectionPreparer collectionPreparer, String collection, Document filter,
|
||||
CountOptions options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import reactor.core.publisher.Mono;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.data.mongodb.core.CollectionPreparerSupport.ReactiveCollectionPreparerDelegate;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.SerializationUtils;
|
||||
@@ -67,8 +68,8 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation {
|
||||
private final String collection;
|
||||
private final Query query;
|
||||
|
||||
ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType,
|
||||
String collection, Query query) {
|
||||
ReactiveFindSupport(ReactiveMongoTemplate template, Class<?> domainType, Class<T> returnType, String collection,
|
||||
Query query) {
|
||||
|
||||
this.template = template;
|
||||
this.domainType = domainType;
|
||||
@@ -169,8 +170,8 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation {
|
||||
Document queryObject = query.getQueryObject();
|
||||
Document fieldsObject = query.getFieldsObject();
|
||||
|
||||
return template.doFind(getCollectionName(), queryObject, fieldsObject, domainType, returnType,
|
||||
preparer != null ? preparer : getCursorPreparer(query));
|
||||
return template.doFind(ReactiveCollectionPreparerDelegate.of(query), getCollectionName(), queryObject,
|
||||
fieldsObject, domainType, returnType, preparer != null ? preparer : getCursorPreparer(query));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -70,6 +70,7 @@ import org.springframework.data.mongodb.MongoDatabaseFactory;
|
||||
import org.springframework.data.mongodb.ReactiveMongoDatabaseFactory;
|
||||
import org.springframework.data.mongodb.ReactiveMongoDatabaseUtils;
|
||||
import org.springframework.data.mongodb.SessionSynchronization;
|
||||
import org.springframework.data.mongodb.core.CollectionPreparerSupport.ReactiveCollectionPreparerDelegate;
|
||||
import org.springframework.data.mongodb.core.EntityOperations.AdaptibleEntity;
|
||||
import org.springframework.data.mongodb.core.QueryOperations.AggregationDefinition;
|
||||
import org.springframework.data.mongodb.core.QueryOperations.CountContext;
|
||||
@@ -105,7 +106,6 @@ import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.data.mongodb.core.query.Meta;
|
||||
import org.springframework.data.mongodb.core.query.Meta.CursorOption;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.UpdateDefinition;
|
||||
@@ -756,8 +756,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
public <T> Mono<T> findOne(Query query, Class<T> entityClass, String collectionName) {
|
||||
|
||||
if (ObjectUtils.isEmpty(query.getSortObject())) {
|
||||
return doFindOne(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||
new QueryFindPublisherPreparer(query, entityClass));
|
||||
return doFindOne(ReactiveCollectionPreparerDelegate.of(query), collectionName, query.getQueryObject(),
|
||||
query.getFieldsObject(), entityClass, new QueryFindPublisherPreparer(query, entityClass));
|
||||
}
|
||||
|
||||
query.limit(1);
|
||||
@@ -783,10 +783,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
|
||||
return createFlux(collectionName, collection -> {
|
||||
|
||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
||||
QueryContext queryContext = queryOperations.createQueryContext(query);
|
||||
Document filter = queryContext.getMappedQuery(entityClass, this::getPersistentEntity);
|
||||
|
||||
FindPublisher<Document> findPublisher = collection.find(filter, Document.class)
|
||||
FindPublisher<Document> findPublisher = collectionPreparer.prepare(collection).find(filter, Document.class)
|
||||
.projection(new Document("_id", 1));
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
@@ -811,8 +812,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
return findAll(entityClass, collectionName);
|
||||
}
|
||||
|
||||
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||
new QueryFindPublisherPreparer(query, entityClass));
|
||||
return doFind(ReactiveCollectionPreparerDelegate.of(query), collectionName, query.getQueryObject(),
|
||||
query.getFieldsObject(), entityClass, new QueryFindPublisherPreparer(query, entityClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -825,7 +826,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
|
||||
String idKey = operations.getIdPropertyName(entityClass);
|
||||
|
||||
return doFindOne(collectionName, new Document(idKey, id), null, entityClass, (Collation) null);
|
||||
return doFindOne(CollectionPreparer.identity(), collectionName, new Document(idKey, id), null, entityClass,
|
||||
(Collation) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -850,6 +852,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
Document mappedQuery = distinctQueryContext.getMappedQuery(entity);
|
||||
String mappedFieldName = distinctQueryContext.getMappedFieldName(entity);
|
||||
Class<T> mongoDriverCompatibleType = distinctQueryContext.getDriverCompatibleClass(resultClass);
|
||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
||||
|
||||
Flux<?> result = execute(collectionName, collection -> {
|
||||
|
||||
@@ -859,11 +862,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
}
|
||||
|
||||
FindPublisherPreparer preparer = new QueryFindPublisherPreparer(query, entityClass);
|
||||
if (preparer.hasReadPreference()) {
|
||||
collection = collection.withReadPreference(preparer.getReadPreference());
|
||||
}
|
||||
|
||||
DistinctPublisher<T> publisher = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType);
|
||||
DistinctPublisher<T> publisher = collectionPreparer.prepare(collection).distinct(mappedFieldName, mappedQuery,
|
||||
mongoDriverCompatibleType);
|
||||
distinctQueryContext.applyCollation(entityClass, publisher::collation);
|
||||
return publisher;
|
||||
});
|
||||
@@ -929,7 +930,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
boolean isOutOrMerge, AggregationOptions options, ReadDocumentCallback<O> readCallback,
|
||||
@Nullable Class<?> inputType) {
|
||||
|
||||
AggregatePublisher<Document> cursor = collection.aggregate(pipeline, Document.class)
|
||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(options);
|
||||
AggregatePublisher<Document> cursor = collectionPreparer.prepare(collection).aggregate(pipeline, Document.class)
|
||||
.allowDiskUse(options.isAllowDiskUse());
|
||||
|
||||
if (options.getCursorBatchSize() != null) {
|
||||
@@ -1028,8 +1030,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
operations.forType(entityClass).getCollation(query).ifPresent(optionsToUse::collation);
|
||||
}
|
||||
|
||||
return doFindAndModify(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
|
||||
return doFindAndModify(ReactiveCollectionPreparerDelegate.of(query), collectionName, query.getQueryObject(),
|
||||
query.getFieldsObject(), getMappedSortObject(query, entityClass), entityClass, update, optionsToUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1053,6 +1055,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
Document mappedQuery = queryContext.getMappedQuery(entity);
|
||||
Document mappedFields = queryContext.getMappedFields(entity, projection);
|
||||
Document mappedSort = queryContext.getMappedSort(entity);
|
||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
||||
|
||||
return Mono.defer(() -> {
|
||||
|
||||
@@ -1070,8 +1073,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
mapped.getCollection()));
|
||||
}).flatMap(it -> {
|
||||
|
||||
Mono<T> afterFindAndReplace = doFindAndReplace(it.getCollection(), mappedQuery, mappedFields, mappedSort,
|
||||
queryContext.getCollation(entityType).orElse(null), entityType, it.getTarget(), options, projection);
|
||||
Mono<T> afterFindAndReplace = doFindAndReplace(collectionPreparer, it.getCollection(), mappedQuery,
|
||||
mappedFields, mappedSort, queryContext.getCollation(entityType).orElse(null), entityType, it.getTarget(),
|
||||
options, projection);
|
||||
return afterFindAndReplace.flatMap(saved -> {
|
||||
maybeEmitEvent(new AfterSaveEvent<>(saved, it.getTarget(), it.getCollection()));
|
||||
return maybeCallAfterSave(saved, it.getTarget(), it.getCollection());
|
||||
@@ -1089,9 +1093,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
public <T> Mono<T> findAndRemove(Query query, Class<T> entityClass, String collectionName) {
|
||||
|
||||
operations.forType(entityClass).getCollation(query);
|
||||
return doFindAndRemove(collectionName, query.getQueryObject(), query.getFieldsObject(),
|
||||
getMappedSortObject(query, entityClass), operations.forType(entityClass).getCollation(query).orElse(null),
|
||||
entityClass);
|
||||
return doFindAndRemove(ReactiveCollectionPreparerDelegate.of(query), collectionName, query.getQueryObject(),
|
||||
query.getFieldsObject(), getMappedSortObject(query, entityClass),
|
||||
operations.forType(entityClass).getCollation(query).orElse(null), entityClass);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1799,12 +1803,14 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
MongoAction mongoAction = new MongoAction(writeConcern, MongoActionOperation.REMOVE, collectionName, entityClass,
|
||||
null, removeQuery);
|
||||
WriteConcern writeConcernToUse = prepareWriteConcern(mongoAction);
|
||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
||||
|
||||
return execute(collectionName, collection -> {
|
||||
|
||||
maybeEmitEvent(new BeforeDeleteEvent<>(removeQuery, entityClass, collectionName));
|
||||
|
||||
MongoCollection<Document> collectionToUse = prepareCollection(collection, writeConcernToUse);
|
||||
MongoCollection<Document> collectionToUse = collectionPreparer
|
||||
.prepare(prepareCollection(collection, writeConcernToUse));
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(String.format("Remove using query: %s in collection: %s.", serializeToJsonSafely(removeQuery),
|
||||
@@ -1839,8 +1845,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
|
||||
@Override
|
||||
public <T> Flux<T> findAll(Class<T> entityClass, String collectionName) {
|
||||
return executeFindMultiInternal(new FindCallback(null), FindPublisherPreparer.NO_OP_PREPARER,
|
||||
new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName), collectionName);
|
||||
return executeFindMultiInternal(new FindCallback(CollectionPreparer.identity(), null),
|
||||
FindPublisherPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
|
||||
collectionName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1867,17 +1874,19 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
@Override
|
||||
public <T> Flux<T> tail(@Nullable Query query, Class<T> entityClass, String collectionName) {
|
||||
|
||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(query);
|
||||
if (query == null) {
|
||||
|
||||
LOGGER.debug(String.format("Tail for class: %s in collection: %s", entityClass, collectionName));
|
||||
|
||||
return executeFindMultiInternal(
|
||||
collection -> new FindCallback(null).doInCollection(collection).cursorType(CursorType.TailableAwait),
|
||||
collection -> new FindCallback(collectionPreparer, null).doInCollection(collection)
|
||||
.cursorType(CursorType.TailableAwait),
|
||||
FindPublisherPreparer.NO_OP_PREPARER, new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName),
|
||||
collectionName);
|
||||
}
|
||||
|
||||
return doFind(collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||
return doFind(collectionPreparer, collectionName, query.getQueryObject(), query.getFieldsObject(), entityClass,
|
||||
new TailingQueryFindPublisherPreparer(query, entityClass));
|
||||
}
|
||||
|
||||
@@ -1961,12 +1970,14 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
|
||||
assertLocalFunctionNames(mapFunction, reduceFunction);
|
||||
|
||||
ReactiveCollectionPreparerDelegate collectionPreparer = ReactiveCollectionPreparerDelegate.of(filterQuery);
|
||||
return createFlux(inputCollectionName, collection -> {
|
||||
|
||||
Document mappedQuery = queryMapper.getMappedObject(filterQuery.getQueryObject(),
|
||||
mappingContext.getPersistentEntity(domainType));
|
||||
|
||||
MapReducePublisher<Document> publisher = collection.mapReduce(mapFunction, reduceFunction, Document.class);
|
||||
MapReducePublisher<Document> publisher = collectionPreparer.prepare(collection).mapReduce(mapFunction,
|
||||
reduceFunction, Document.class);
|
||||
|
||||
publisher.filter(mappedQuery);
|
||||
|
||||
@@ -2139,10 +2150,11 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* @param collation can be {@literal null}.
|
||||
* @return the {@link List} of converted objects.
|
||||
*/
|
||||
protected <T> Mono<T> doFindOne(String collectionName, Document query, @Nullable Document fields,
|
||||
Class<T> entityClass, @Nullable Collation collation) {
|
||||
protected <T> Mono<T> doFindOne(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
||||
String collectionName, Document query, @Nullable Document fields, Class<T> entityClass,
|
||||
@Nullable Collation collation) {
|
||||
|
||||
return doFindOne(collectionName, query, fields, entityClass,
|
||||
return doFindOne(collectionPreparer, collectionName, query, fields, entityClass,
|
||||
findPublisher -> collation != null ? findPublisher.collation(collation.toMongoCollation()) : findPublisher);
|
||||
}
|
||||
|
||||
@@ -2158,8 +2170,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* @return the {@link List} of converted objects.
|
||||
* @since 2.2
|
||||
*/
|
||||
protected <T> Mono<T> doFindOne(String collectionName, Document query, @Nullable Document fields,
|
||||
Class<T> entityClass, FindPublisherPreparer preparer) {
|
||||
protected <T> Mono<T> doFindOne(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
||||
String collectionName, Document query, @Nullable Document fields, Class<T> entityClass,
|
||||
FindPublisherPreparer preparer) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
@@ -2173,7 +2186,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
serializeToJsonSafely(query), mappedFields, entityClass, collectionName));
|
||||
}
|
||||
|
||||
return executeFindOneInternal(new FindOneCallback(mappedQuery, mappedFields, preparer),
|
||||
return executeFindOneInternal(new FindOneCallback(collectionPreparer, mappedQuery, mappedFields, preparer),
|
||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||
}
|
||||
|
||||
@@ -2187,8 +2200,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @return the List of converted objects.
|
||||
*/
|
||||
protected <T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass) {
|
||||
return doFind(collectionName, query, fields, entityClass, null,
|
||||
protected <T> Flux<T> doFind(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName,
|
||||
Document query, Document fields, Class<T> entityClass) {
|
||||
return doFind(collectionPreparer, collectionName, query, fields, entityClass, null,
|
||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName));
|
||||
}
|
||||
|
||||
@@ -2205,13 +2219,14 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* the result set, (apply limits, skips and so on).
|
||||
* @return the {@link List} of converted objects.
|
||||
*/
|
||||
protected <T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<T> entityClass,
|
||||
FindPublisherPreparer preparer) {
|
||||
return doFind(collectionName, query, fields, entityClass, preparer,
|
||||
protected <T> Flux<T> doFind(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName,
|
||||
Document query, Document fields, Class<T> entityClass, FindPublisherPreparer preparer) {
|
||||
return doFind(collectionPreparer, collectionName, query, fields, entityClass, preparer,
|
||||
new ReadDocumentCallback<>(mongoConverter, entityClass, collectionName));
|
||||
}
|
||||
|
||||
protected <S, T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<S> entityClass,
|
||||
protected <S, T> Flux<T> doFind(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
||||
String collectionName, Document query, Document fields, Class<S> entityClass,
|
||||
@Nullable FindPublisherPreparer preparer, DocumentCallback<T> objectCallback) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
@@ -2225,8 +2240,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
serializeToJsonSafely(mappedQuery), mappedFields, entityClass, collectionName));
|
||||
}
|
||||
|
||||
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields), preparer, objectCallback,
|
||||
collectionName);
|
||||
return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields), preparer,
|
||||
objectCallback, collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2235,8 +2250,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
<S, T> Flux<T> doFind(String collectionName, Document query, Document fields, Class<S> sourceClass,
|
||||
Class<T> targetClass, FindPublisherPreparer preparer) {
|
||||
<S, T> Flux<T> doFind(CollectionPreparer<MongoCollection<Document>> collectionPreparer, String collectionName,
|
||||
Document query, Document fields, Class<S> sourceClass, Class<T> targetClass, FindPublisherPreparer preparer) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(sourceClass);
|
||||
EntityProjection<T, S> projection = operations.introspectProjection(targetClass, sourceClass);
|
||||
@@ -2250,7 +2265,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
serializeToJsonSafely(mappedQuery), mappedFields, sourceClass, collectionName));
|
||||
}
|
||||
|
||||
return executeFindMultiInternal(new FindCallback(mappedQuery, mappedFields), preparer,
|
||||
return executeFindMultiInternal(new FindCallback(collectionPreparer, mappedQuery, mappedFields), preparer,
|
||||
new ProjectingReadCallback<>(mongoConverter, projection, collectionName), collectionName);
|
||||
}
|
||||
|
||||
@@ -2274,8 +2289,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* @param entityClass the parameterized type of the returned list.
|
||||
* @return the List of converted objects.
|
||||
*/
|
||||
protected <T> Mono<T> doFindAndRemove(String collectionName, Document query, Document fields, Document sort,
|
||||
@Nullable Collation collation, Class<T> entityClass) {
|
||||
protected <T> Mono<T> doFindAndRemove(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
||||
String collectionName, Document query, Document fields, Document sort, @Nullable Collation collation,
|
||||
Class<T> entityClass) {
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(String.format("findAndRemove using query: %s fields: %s sort: %s for class: %s in collection: %s",
|
||||
@@ -2284,13 +2300,14 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
|
||||
return executeFindOneInternal(
|
||||
new FindAndRemoveCallback(queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
||||
return executeFindOneInternal(new FindAndRemoveCallback(collectionPreparer,
|
||||
queryMapper.getMappedObject(query, entity), fields, sort, collation),
|
||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||
}
|
||||
|
||||
protected <T> Mono<T> doFindAndModify(String collectionName, Document query, Document fields, Document sort,
|
||||
Class<T> entityClass, UpdateDefinition update, FindAndModifyOptions options) {
|
||||
protected <T> Mono<T> doFindAndModify(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
||||
String collectionName, Document query, Document fields, Document sort, Class<T> entityClass,
|
||||
UpdateDefinition update, FindAndModifyOptions options) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityClass);
|
||||
UpdateContext updateContext = queryOperations.updateSingleContext(update, query, false);
|
||||
@@ -2310,7 +2327,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
}
|
||||
|
||||
return executeFindOneInternal(
|
||||
new FindAndModifyCallback(mappedQuery, fields, sort, mappedUpdate,
|
||||
new FindAndModifyCallback(collectionPreparer, mappedQuery, fields, sort, mappedUpdate,
|
||||
update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList()), options),
|
||||
new ReadDocumentCallback<>(this.mongoConverter, entityClass, collectionName), collectionName);
|
||||
});
|
||||
@@ -2332,14 +2349,15 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
||||
* @since 2.1
|
||||
*/
|
||||
protected <T> Mono<T> doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
|
||||
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
||||
protected <T> Mono<T> doFindAndReplace(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
||||
String collectionName, Document mappedQuery, Document mappedFields, Document mappedSort,
|
||||
com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
||||
FindAndReplaceOptions options, Class<T> resultType) {
|
||||
|
||||
EntityProjection<T, ?> projection = operations.introspectProjection(resultType, entityType);
|
||||
|
||||
return doFindAndReplace(collectionName, mappedQuery, mappedFields, mappedSort, collation, entityType, replacement,
|
||||
options, projection);
|
||||
return doFindAndReplace(collectionPreparer, collectionName, mappedQuery, mappedFields, mappedSort, collation,
|
||||
entityType, replacement, options, projection);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2358,8 +2376,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* {@literal false} and {@link FindAndReplaceOptions#isUpsert() upsert} is {@literal false}.
|
||||
* @since 3.4
|
||||
*/
|
||||
private <T> Mono<T> doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
|
||||
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
||||
private <T> Mono<T> doFindAndReplace(CollectionPreparer<MongoCollection<Document>> collectionPreparer,
|
||||
String collectionName, Document mappedQuery, Document mappedFields, Document mappedSort,
|
||||
com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
|
||||
FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
|
||||
|
||||
return Mono.defer(() -> {
|
||||
@@ -2372,8 +2391,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
serializeToJsonSafely(replacement), collectionName));
|
||||
}
|
||||
|
||||
return executeFindOneInternal(
|
||||
new FindAndReplaceCallback(mappedQuery, mappedFields, mappedSort, replacement, collation, options),
|
||||
return executeFindOneInternal(new FindAndReplaceCallback(collectionPreparer, mappedQuery, mappedFields,
|
||||
mappedSort, replacement, collation, options),
|
||||
new ProjectingReadCallback<>(this.mongoConverter, projection, collectionName), collectionName);
|
||||
|
||||
});
|
||||
@@ -2451,7 +2470,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
* @param collection
|
||||
*/
|
||||
protected MongoCollection<Document> prepareCollection(MongoCollection<Document> collection) {
|
||||
return this.readPreference != null ? collection.withReadPreference(readPreference) : collection;
|
||||
|
||||
if (this.readPreference != null && this.readPreference != collection.getReadPreference()) {
|
||||
return collection.withReadPreference(readPreference);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2621,11 +2645,14 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
*/
|
||||
private static class FindOneCallback implements ReactiveCollectionCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Optional<Document> fields;
|
||||
private final FindPublisherPreparer preparer;
|
||||
|
||||
FindOneCallback(Document query, @Nullable Document fields, FindPublisherPreparer preparer) {
|
||||
FindOneCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
||||
@Nullable Document fields, FindPublisherPreparer preparer) {
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = Optional.ofNullable(fields);
|
||||
this.preparer = preparer;
|
||||
@@ -2642,7 +2669,8 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
serializeToJsonSafely(fields.orElseGet(Document::new)), collection.getNamespace().getFullName()));
|
||||
}
|
||||
|
||||
FindPublisher<Document> publisher = preparer.initiateFind(collection, col -> col.find(query, Document.class));
|
||||
FindPublisher<Document> publisher = preparer.initiateFind(collectionPreparer.prepare(collection),
|
||||
col -> col.find(query, Document.class));
|
||||
|
||||
if (fields.isPresent()) {
|
||||
publisher = publisher.projection(fields.get());
|
||||
@@ -2660,15 +2688,17 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
*/
|
||||
private static class FindCallback implements ReactiveCollectionQueryCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
|
||||
private final @Nullable Document query;
|
||||
private final @Nullable Document fields;
|
||||
|
||||
FindCallback(@Nullable Document query) {
|
||||
this(query, null);
|
||||
FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, @Nullable Document query) {
|
||||
this(collectionPreparer, query, null);
|
||||
}
|
||||
|
||||
FindCallback(Document query, Document fields) {
|
||||
|
||||
FindCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document fields) {
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
}
|
||||
@@ -2676,11 +2706,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
@Override
|
||||
public FindPublisher<Document> doInCollection(MongoCollection<Document> collection) {
|
||||
|
||||
MongoCollection<Document> collectionToUse = collectionPreparer.prepare(collection);
|
||||
FindPublisher<Document> findPublisher;
|
||||
if (ObjectUtils.isEmpty(query)) {
|
||||
findPublisher = collection.find(Document.class);
|
||||
findPublisher = collectionToUse.find(Document.class);
|
||||
} else {
|
||||
findPublisher = collection.find(query, Document.class);
|
||||
findPublisher = collectionToUse.find(query, Document.class);
|
||||
}
|
||||
|
||||
if (ObjectUtils.isEmpty(fields)) {
|
||||
@@ -2699,13 +2730,15 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
*/
|
||||
private static class FindAndRemoveCallback implements ReactiveCollectionCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Document fields;
|
||||
private final Document sort;
|
||||
private final Optional<Collation> collation;
|
||||
|
||||
FindAndRemoveCallback(Document query, Document fields, Document sort, @Nullable Collation collation) {
|
||||
|
||||
FindAndRemoveCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
||||
Document fields, Document sort, @Nullable Collation collation) {
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
this.sort = sort;
|
||||
@@ -2719,7 +2752,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
FindOneAndDeleteOptions findOneAndDeleteOptions = convertToFindOneAndDeleteOptions(fields, sort);
|
||||
collation.map(Collation::toMongoCollation).ifPresent(findOneAndDeleteOptions::collation);
|
||||
|
||||
return collection.findOneAndDelete(query, findOneAndDeleteOptions);
|
||||
return collectionPreparer.prepare(collection).findOneAndDelete(query, findOneAndDeleteOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2728,6 +2761,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
*/
|
||||
private static class FindAndModifyCallback implements ReactiveCollectionCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Document fields;
|
||||
private final Document sort;
|
||||
@@ -2735,9 +2769,10 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
private final List<Document> arrayFilters;
|
||||
private final FindAndModifyOptions options;
|
||||
|
||||
FindAndModifyCallback(Document query, Document fields, Document sort, Object update, List<Document> arrayFilters,
|
||||
FindAndModifyOptions options) {
|
||||
FindAndModifyCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
||||
Document fields, Document sort, Object update, List<Document> arrayFilters, FindAndModifyOptions options) {
|
||||
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
this.sort = sort;
|
||||
@@ -2750,21 +2785,22 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
public Publisher<Document> doInCollection(MongoCollection<Document> collection)
|
||||
throws MongoException, DataAccessException {
|
||||
|
||||
MongoCollection<Document> collectionToUse = collectionPreparer.prepare(collection);
|
||||
if (options.isRemove()) {
|
||||
FindOneAndDeleteOptions findOneAndDeleteOptions = convertToFindOneAndDeleteOptions(fields, sort);
|
||||
|
||||
findOneAndDeleteOptions = options.getCollation().map(Collation::toMongoCollation)
|
||||
.map(findOneAndDeleteOptions::collation).orElse(findOneAndDeleteOptions);
|
||||
|
||||
return collection.findOneAndDelete(query, findOneAndDeleteOptions);
|
||||
return collectionToUse.findOneAndDelete(query, findOneAndDeleteOptions);
|
||||
}
|
||||
|
||||
FindOneAndUpdateOptions findOneAndUpdateOptions = convertToFindOneAndUpdateOptions(options, fields, sort,
|
||||
arrayFilters);
|
||||
if (update instanceof Document) {
|
||||
return collection.findOneAndUpdate(query, (Document) update, findOneAndUpdateOptions);
|
||||
return collectionToUse.findOneAndUpdate(query, (Document) update, findOneAndUpdateOptions);
|
||||
} else if (update instanceof List) {
|
||||
return collection.findOneAndUpdate(query, (List<Document>) update, findOneAndUpdateOptions);
|
||||
return collectionToUse.findOneAndUpdate(query, (List<Document>) update, findOneAndUpdateOptions);
|
||||
}
|
||||
|
||||
return Flux
|
||||
@@ -2803,6 +2839,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
*/
|
||||
private static class FindAndReplaceCallback implements ReactiveCollectionCallback<Document> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Document fields;
|
||||
private final Document sort;
|
||||
@@ -2810,9 +2847,10 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
private final @Nullable com.mongodb.client.model.Collation collation;
|
||||
private final FindAndReplaceOptions options;
|
||||
|
||||
FindAndReplaceCallback(Document query, Document fields, Document sort, Document update,
|
||||
com.mongodb.client.model.Collation collation, FindAndReplaceOptions options) {
|
||||
|
||||
FindAndReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query,
|
||||
Document fields, Document sort, Document update, com.mongodb.client.model.Collation collation,
|
||||
FindAndReplaceOptions options) {
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.fields = fields;
|
||||
this.sort = sort;
|
||||
@@ -2826,7 +2864,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
throws MongoException, DataAccessException {
|
||||
|
||||
FindOneAndReplaceOptions findOneAndReplaceOptions = convertToFindOneAndReplaceOptions(options, fields, sort);
|
||||
return collection.findOneAndReplace(query, update, findOneAndReplaceOptions);
|
||||
return collectionPreparer.prepare(collection).findOneAndReplace(query, update, findOneAndReplaceOptions);
|
||||
}
|
||||
|
||||
private FindOneAndReplaceOptions convertToFindOneAndReplaceOptions(FindAndReplaceOptions options, Document fields,
|
||||
@@ -3092,11 +3130,6 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
return findPublisherToUse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadPreference getReadPreference() {
|
||||
return query.getMeta().getFlags().contains(CursorOption.SECONDARY_READS) ? ReadPreference.primaryPreferred()
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
class TailingQueryFindPublisherPreparer extends QueryFindPublisherPreparer {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.mongodb.core;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import com.mongodb.ReadConcern;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by any object that wishes to expose the {@link ReadConcern}.
|
||||
* <p>
|
||||
* Typically implemented by cursor or query preparer objects.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface ReadConcernAware {
|
||||
|
||||
/**
|
||||
* @return {@literal true} if a {@link ReadConcern} is set.
|
||||
*/
|
||||
default boolean hasReadConcern() {
|
||||
return getReadConcern() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link ReadConcern} to apply or {@literal null} if none set.
|
||||
*/
|
||||
@Nullable
|
||||
ReadConcern getReadConcern();
|
||||
}
|
||||
@@ -19,11 +19,16 @@ import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mongodb.core.ReadConcernAware;
|
||||
import org.springframework.data.mongodb.core.ReadPreferenceAware;
|
||||
import org.springframework.data.mongodb.core.query.Collation;
|
||||
import org.springframework.data.mongodb.util.BsonUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.ReadConcern;
|
||||
import com.mongodb.ReadPreference;
|
||||
|
||||
/**
|
||||
* Holds a set of configurable aggregation options that can be used within an aggregation pipeline. A list of support
|
||||
* aggregation options can be found in the MongoDB reference documentation
|
||||
@@ -39,7 +44,7 @@ import org.springframework.util.Assert;
|
||||
* @see TypedAggregation#withOptions(AggregationOptions)
|
||||
* @since 1.6
|
||||
*/
|
||||
public class AggregationOptions {
|
||||
public class AggregationOptions implements ReadConcernAware, ReadPreferenceAware {
|
||||
|
||||
private static final String BATCH_SIZE = "batchSize";
|
||||
private static final String CURSOR = "cursor";
|
||||
@@ -56,6 +61,10 @@ public class AggregationOptions {
|
||||
private final Optional<Collation> collation;
|
||||
private final Optional<String> comment;
|
||||
private final Optional<Object> hint;
|
||||
|
||||
private Optional<ReadConcern> readConcern;
|
||||
|
||||
private Optional<ReadPreference> readPreference;
|
||||
private Duration maxTime = Duration.ZERO;
|
||||
private ResultOptions resultOptions = ResultOptions.READ;
|
||||
private DomainTypeMapping domainTypeMapping = DomainTypeMapping.RELAXED;
|
||||
@@ -123,6 +132,8 @@ public class AggregationOptions {
|
||||
this.collation = Optional.ofNullable(collation);
|
||||
this.comment = Optional.ofNullable(comment);
|
||||
this.hint = Optional.ofNullable(hint);
|
||||
this.readConcern = Optional.empty();
|
||||
this.readPreference = Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,6 +279,26 @@ public class AggregationOptions {
|
||||
return hint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReadConcern() {
|
||||
return readConcern.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadConcern getReadConcern() {
|
||||
return readConcern.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReadPreference() {
|
||||
return readPreference.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadPreference getReadPreference() {
|
||||
return readPreference.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the time limit for processing. {@link Duration#ZERO} is used for the default unbounded behavior.
|
||||
* @since 3.0
|
||||
@@ -385,6 +416,8 @@ public class AggregationOptions {
|
||||
private @Nullable Collation collation;
|
||||
private @Nullable String comment;
|
||||
private @Nullable Object hint;
|
||||
private @Nullable ReadConcern readConcern;
|
||||
private @Nullable ReadPreference readPreference;
|
||||
private @Nullable Duration maxTime;
|
||||
private @Nullable ResultOptions resultOptions;
|
||||
private @Nullable DomainTypeMapping domainTypeMapping;
|
||||
@@ -490,6 +523,32 @@ public class AggregationOptions {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a {@link ReadConcern} to apply to the aggregation.
|
||||
*
|
||||
* @param readConcern can be {@literal null}.
|
||||
* @return this.
|
||||
* @since 4.1
|
||||
*/
|
||||
public Builder readConcern(@Nullable ReadConcern readConcern) {
|
||||
|
||||
this.readConcern = readConcern;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a {@link ReadPreference} to apply to the aggregation.
|
||||
*
|
||||
* @param readPreference can be {@literal null}.
|
||||
* @return this.
|
||||
* @since 4.1
|
||||
*/
|
||||
public Builder readPreference(@Nullable ReadPreference readPreference) {
|
||||
|
||||
this.readPreference = readPreference;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time limit for processing.
|
||||
*
|
||||
@@ -573,6 +632,12 @@ public class AggregationOptions {
|
||||
if (domainTypeMapping != null) {
|
||||
options.domainTypeMapping = domainTypeMapping;
|
||||
}
|
||||
if (readConcern != null) {
|
||||
options.readConcern = Optional.of(readConcern);
|
||||
}
|
||||
if (readPreference != null) {
|
||||
options.readPreference = Optional.of(readPreference);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -536,6 +536,11 @@ public final class NearQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Query getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of elements to skip.
|
||||
*/
|
||||
|
||||
@@ -34,10 +34,16 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
|
||||
import org.springframework.data.mongodb.core.ReadConcernAware;
|
||||
import org.springframework.data.mongodb.core.ReadPreferenceAware;
|
||||
import org.springframework.data.mongodb.core.query.Meta.CursorOption;
|
||||
import org.springframework.data.mongodb.util.BsonUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.ReadConcern;
|
||||
import com.mongodb.ReadPreference;
|
||||
|
||||
/**
|
||||
* MongoDB Query object representing criteria, projection, sorting and query hints.
|
||||
*
|
||||
@@ -48,7 +54,7 @@ import org.springframework.util.Assert;
|
||||
* @author Mark Paluch
|
||||
* @author Anton Barkan
|
||||
*/
|
||||
public class Query {
|
||||
public class Query implements ReadConcernAware, ReadPreferenceAware {
|
||||
|
||||
private static final String RESTRICTED_TYPES_KEY = "_$RESTRICTED_TYPES";
|
||||
|
||||
@@ -58,6 +64,9 @@ public class Query {
|
||||
private Sort sort = Sort.unsorted();
|
||||
private long skip;
|
||||
private int limit;
|
||||
private @Nullable ReadConcern readConcern;
|
||||
private @Nullable ReadPreference readPreference;
|
||||
|
||||
private @Nullable String hint;
|
||||
|
||||
private Meta meta = new Meta();
|
||||
@@ -160,6 +169,59 @@ public class Query {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the query to use the given {@link ReadConcern} when being executed.
|
||||
*
|
||||
* @param readConcern must not be {@literal null}.
|
||||
* @return this.
|
||||
* @since 3.1
|
||||
*/
|
||||
public Query withReadConcern(ReadConcern readConcern) {
|
||||
|
||||
Assert.notNull(readConcern, "ReadConcern must not be null");
|
||||
this.readConcern = readConcern;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the query to use the given {@link ReadPreference} when being executed.
|
||||
*
|
||||
* @param readPreference must not be {@literal null}.
|
||||
* @return this.
|
||||
* @since 4.1
|
||||
*/
|
||||
public Query withReadPreference(ReadPreference readPreference) {
|
||||
|
||||
Assert.notNull(readPreference, "ReadPreference must not be null");
|
||||
this.readPreference = readPreference;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReadConcern() {
|
||||
return this.readConcern != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadConcern getReadConcern() {
|
||||
return this.readConcern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReadPreference() {
|
||||
return this.readPreference != null || getMeta().getFlags().contains(CursorOption.SECONDARY_READS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadPreference getReadPreference() {
|
||||
|
||||
if (readPreference == null) {
|
||||
return getMeta().getFlags().contains(CursorOption.SECONDARY_READS) ? ReadPreference.primaryPreferred() : null;
|
||||
}
|
||||
|
||||
return this.readPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the query to use the given {@link Document hint} when being executed.
|
||||
*
|
||||
|
||||
@@ -64,6 +64,7 @@ class QueryUtils {
|
||||
combinedSort.putAll((Document) invocation.proceed());
|
||||
return combinedSort;
|
||||
});
|
||||
factory.setInterfaces(new Class[0]);
|
||||
|
||||
return (Query) factory.getProxy(query.getClass().getClassLoader());
|
||||
}
|
||||
@@ -113,7 +114,7 @@ class QueryUtils {
|
||||
if(parameters.isEmpty()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int i = 0;
|
||||
for(Class<?> parameterType : parameters) {
|
||||
if(ClassUtils.isAssignable(type, parameterType)) {
|
||||
|
||||
@@ -108,6 +108,7 @@ import org.springframework.util.CollectionUtils;
|
||||
import com.mongodb.MongoClientSettings;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.MongoNamespace;
|
||||
import com.mongodb.ReadConcern;
|
||||
import com.mongodb.ReadPreference;
|
||||
import com.mongodb.ServerAddress;
|
||||
import com.mongodb.ServerCursor;
|
||||
@@ -120,16 +121,7 @@ import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoCursor;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.CountOptions;
|
||||
import com.mongodb.client.model.CreateCollectionOptions;
|
||||
import com.mongodb.client.model.DeleteOptions;
|
||||
import com.mongodb.client.model.FindOneAndDeleteOptions;
|
||||
import com.mongodb.client.model.FindOneAndReplaceOptions;
|
||||
import com.mongodb.client.model.FindOneAndUpdateOptions;
|
||||
import com.mongodb.client.model.MapReduceAction;
|
||||
import com.mongodb.client.model.ReplaceOptions;
|
||||
import com.mongodb.client.model.TimeSeriesGranularity;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import com.mongodb.client.model.*;
|
||||
import com.mongodb.client.result.DeleteResult;
|
||||
import com.mongodb.client.result.UpdateResult;
|
||||
|
||||
@@ -182,6 +174,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
when(collection.estimatedDocumentCount(any())).thenReturn(1L);
|
||||
when(collection.getNamespace()).thenReturn(new MongoNamespace("db.mock-collection"));
|
||||
when(collection.aggregate(any(List.class), any())).thenReturn(aggregateIterable);
|
||||
when(collection.withReadConcern(any())).thenReturn(collection);
|
||||
when(collection.withReadPreference(any())).thenReturn(collection);
|
||||
when(collection.replaceOne(any(), any(), any(ReplaceOptions.class))).thenReturn(updateResult);
|
||||
when(collection.withWriteConcern(any())).thenReturn(collectionWithWriteConcern);
|
||||
@@ -478,6 +471,34 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
verify(collection, never()).withReadPreference(any());
|
||||
}
|
||||
|
||||
@Test // GH-4277
|
||||
void aggregateShouldHonorOptionsReadConcernWhenSet() {
|
||||
|
||||
AggregationOptions options = AggregationOptions.builder().readConcern(ReadConcern.SNAPSHOT).build();
|
||||
template.aggregate(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1", Wrapper.class);
|
||||
|
||||
verify(collection).withReadConcern(ReadConcern.SNAPSHOT);
|
||||
}
|
||||
|
||||
@Test // GH-4277
|
||||
void aggregateShouldHonorOptionsReadPreferenceWhenSet() {
|
||||
|
||||
AggregationOptions options = AggregationOptions.builder().readPreference(ReadPreference.secondary()).build();
|
||||
template.aggregate(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1", Wrapper.class);
|
||||
|
||||
verify(collection).withReadPreference(ReadPreference.secondary());
|
||||
}
|
||||
|
||||
@Test // GH-4277
|
||||
void aggregateStreamShouldHonorOptionsReadPreferenceWhenSet() {
|
||||
|
||||
AggregationOptions options = AggregationOptions.builder().readPreference(ReadPreference.secondary()).build();
|
||||
template.aggregateStream(newAggregation(Aggregation.unwind("foo")).withOptions(options), "collection-1",
|
||||
Wrapper.class);
|
||||
|
||||
verify(collection).withReadPreference(ReadPreference.secondary());
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2153
|
||||
void aggregateShouldHonorOptionsComment() {
|
||||
|
||||
@@ -558,6 +579,19 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
verify(collection).withReadPreference(eq(ReadPreference.secondary()));
|
||||
}
|
||||
|
||||
@Test // GH-4277
|
||||
void geoNearShouldHonorReadPreferenceFromQuery() {
|
||||
|
||||
NearQuery query = NearQuery.near(new Point(1, 1));
|
||||
|
||||
Query inner = new Query();
|
||||
inner.withReadPreference(ReadPreference.secondary());
|
||||
query.query(inner);
|
||||
template.geoNear(query, Wrapper.class);
|
||||
|
||||
verify(collection).withReadPreference(eq(ReadPreference.secondary()));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1166, DATAMONGO-2264
|
||||
void geoNearShouldIgnoreReadPreferenceWhenNotSet() {
|
||||
|
||||
@@ -802,6 +836,24 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
verify(findIterable).batchSize(1234);
|
||||
}
|
||||
|
||||
@Test // GH-4277
|
||||
void findShouldUseReadConcernWhenPresent() {
|
||||
|
||||
template.find(new BasicQuery("{'foo' : 'bar'}").withReadConcern(ReadConcern.SNAPSHOT),
|
||||
AutogenerateableId.class);
|
||||
|
||||
verify(collection).withReadConcern(ReadConcern.SNAPSHOT);
|
||||
}
|
||||
|
||||
@Test // GH-4277
|
||||
void findShouldUseReadPreferenceWhenPresent() {
|
||||
|
||||
template.find(new BasicQuery("{'foo' : 'bar'}").withReadPreference(ReadPreference.secondary()),
|
||||
AutogenerateableId.class);
|
||||
|
||||
verify(collection).withReadPreference(ReadPreference.secondary());
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1518
|
||||
void executeQueryShouldUseCollationWhenPresent() {
|
||||
|
||||
@@ -1048,7 +1100,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-1733
|
||||
void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonProjection.class,
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonProjection.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(new Document("firstname", 1)));
|
||||
@@ -1057,7 +1110,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-1733
|
||||
void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, PersonProjection.class,
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document("bar", 1), Person.class,
|
||||
PersonProjection.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(new Document("bar", 1)));
|
||||
@@ -1066,7 +1120,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-1733
|
||||
void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonSpELProjection.class,
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonSpELProjection.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||
@@ -1075,7 +1130,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-1733, DATAMONGO-2041
|
||||
void appliesFieldsToDtoProjection() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, Jedi.class,
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
Jedi.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(new Document("firstname", 1)));
|
||||
@@ -1084,7 +1140,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-1733
|
||||
void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, Jedi.class,
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document("bar", 1), Person.class,
|
||||
Jedi.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(new Document("bar", 1)));
|
||||
@@ -1093,7 +1150,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-1733
|
||||
void doesNotApplyFieldsWhenTargetIsNotAProjection() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, Person.class,
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
Person.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||
@@ -1102,7 +1160,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-1733
|
||||
void doesNotApplyFieldsWhenTargetExtendsDomainType() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonExtended.class,
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonExtended.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||
|
||||
@@ -23,8 +23,6 @@ import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.data.mongodb.core.MongoTemplateUnitTests.Wrapper;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
@@ -56,7 +54,6 @@ import org.mockito.junit.jupiter.MockitoSettings;
|
||||
import org.mockito.quality.Strictness;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.reactivestreams.Subscriber;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
@@ -100,6 +97,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.mongodb.MongoClientSettings;
|
||||
import com.mongodb.ReadConcern;
|
||||
import com.mongodb.ReadPreference;
|
||||
import com.mongodb.client.model.CountOptions;
|
||||
import com.mongodb.client.model.CreateCollectionOptions;
|
||||
@@ -168,6 +166,7 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
when(db.runCommand(any(), any(Class.class))).thenReturn(runCommandPublisher);
|
||||
when(db.createCollection(any(), any(CreateCollectionOptions.class))).thenReturn(runCommandPublisher);
|
||||
when(collection.withReadPreference(any())).thenReturn(collection);
|
||||
when(collection.withReadConcern(any())).thenReturn(collection);
|
||||
when(collection.find(any(Class.class))).thenReturn(findPublisher);
|
||||
when(collection.find(any(Document.class), any(Class.class))).thenReturn(findPublisher);
|
||||
when(collection.aggregate(anyList())).thenReturn(aggregatePublisher);
|
||||
@@ -388,8 +387,8 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
@Test // DATAMONGO-1719
|
||||
void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonProjection.class,
|
||||
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonProjection.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
|
||||
verify(findPublisher).projection(eq(new Document("firstname", 1)));
|
||||
}
|
||||
@@ -397,8 +396,8 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
@Test // DATAMONGO-1719
|
||||
void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, PersonProjection.class,
|
||||
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document("bar", 1), Person.class,
|
||||
PersonProjection.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
|
||||
verify(findPublisher).projection(eq(new Document("bar", 1)));
|
||||
}
|
||||
@@ -406,8 +405,8 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
@Test // DATAMONGO-1719
|
||||
void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonSpELProjection.class,
|
||||
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonSpELProjection.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
|
||||
verify(findPublisher, never()).projection(any());
|
||||
}
|
||||
@@ -415,8 +414,8 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
@Test // DATAMONGO-1719, DATAMONGO-2041
|
||||
void appliesFieldsToDtoProjection() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, Jedi.class,
|
||||
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
Jedi.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
|
||||
verify(findPublisher).projection(eq(new Document("firstname", 1)));
|
||||
}
|
||||
@@ -424,8 +423,8 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
@Test // DATAMONGO-1719
|
||||
void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document("bar", 1), Person.class, Jedi.class,
|
||||
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document("bar", 1), Person.class,
|
||||
Jedi.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
|
||||
verify(findPublisher).projection(eq(new Document("bar", 1)));
|
||||
}
|
||||
@@ -433,8 +432,8 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
@Test // DATAMONGO-1719
|
||||
void doesNotApplyFieldsWhenTargetIsNotAProjection() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, Person.class,
|
||||
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
Person.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
|
||||
verify(findPublisher, never()).projection(any());
|
||||
}
|
||||
@@ -442,8 +441,8 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
@Test // DATAMONGO-1719
|
||||
void doesNotApplyFieldsWhenTargetExtendsDomainType() {
|
||||
|
||||
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonExtended.class,
|
||||
FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonExtended.class, FindPublisherPreparer.NO_OP_PREPARER).subscribe();
|
||||
|
||||
verify(findPublisher, never()).projection(any());
|
||||
}
|
||||
@@ -632,6 +631,26 @@ public class ReactiveMongoTemplateUnitTests {
|
||||
verify(aggregatePublisher).collation(eq(com.mongodb.client.model.Collation.builder().locale("de_AT").build()));
|
||||
}
|
||||
|
||||
@Test // GH-4277
|
||||
void aggreateShouldUseReadConcern() {
|
||||
|
||||
AggregationOptions options = AggregationOptions.builder().readConcern(ReadConcern.SNAPSHOT).build();
|
||||
template.aggregate(newAggregation(Sith.class, project("id")).withOptions(options), AutogenerateableId.class,
|
||||
Document.class).subscribe();
|
||||
|
||||
verify(collection).withReadConcern(ReadConcern.SNAPSHOT);
|
||||
}
|
||||
|
||||
@Test // GH-4286
|
||||
void aggreateShouldUseReadReadPreference() {
|
||||
|
||||
AggregationOptions options = AggregationOptions.builder().readPreference(ReadPreference.primaryPreferred()).build();
|
||||
template.aggregate(newAggregation(Sith.class, project("id")).withOptions(options), AutogenerateableId.class,
|
||||
Document.class).subscribe();
|
||||
|
||||
verify(collection).withReadPreference(ReadPreference.primaryPreferred());
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1854
|
||||
void aggreateShouldUseCollationFromOptionsEvenIfDefaultCollationIsPresent() {
|
||||
|
||||
|
||||
@@ -342,7 +342,10 @@ class QueryTests {
|
||||
source.limit(10);
|
||||
source.setSortObject(new Document("_id", 1));
|
||||
|
||||
Query target = Query.of((Query) new ProxyFactory(source).getProxy());
|
||||
ProxyFactory proxyFactory = new ProxyFactory(source);
|
||||
proxyFactory.setInterfaces(new Class[0]);
|
||||
|
||||
Query target = Query.of((Query) proxyFactory.getProxy());
|
||||
|
||||
compareQueries(target, source);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user