Add support for replaceOne operation.
Add replace methods to MongoOperations and MongoTemplate that allow to replace the first matching document with a given value. Closes: #4462 Original Pull Request: #4463
This commit is contained in:
@@ -1753,6 +1753,122 @@ public interface MongoOperations extends FluentMongoOperations {
|
||||
*/
|
||||
<T> List<T> findAllAndRemove(Query query, Class<T> entityClass, String collectionName);
|
||||
|
||||
/**
|
||||
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
|
||||
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document.
|
||||
* <br />
|
||||
* The collection name is derived from the {@literal replacement} type. <br />
|
||||
* Options are defaulted to {@link ReplaceOptions#empty()}. <br />
|
||||
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification. Must not be {@literal null}.
|
||||
* @param replacement the replacement document. Must not be {@literal null}.
|
||||
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
|
||||
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
|
||||
* {@link #getCollectionName(Class) derived} from the given replacement value.
|
||||
*/
|
||||
default <T> UpdateResult replace(Query query, T replacement) {
|
||||
return replace(query, replacement, ReplaceOptions.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
|
||||
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement}
|
||||
* document.<br />
|
||||
* Options are defaulted to {@link ReplaceOptions#empty()}. <br />
|
||||
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification. Must not be {@literal null}.
|
||||
* @param replacement the replacement document. Must not be {@literal null}.
|
||||
* @param collectionName the collection to query. Must not be {@literal null}.
|
||||
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
|
||||
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
|
||||
* {@link #getCollectionName(Class) derived} from the given replacement value.
|
||||
*/
|
||||
default <T> UpdateResult replace(Query query, T replacement, String collectionName) {
|
||||
return replace(query, replacement, ReplaceOptions.empty(), collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
|
||||
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document
|
||||
* taking {@link ReplaceOptions} into account.<br />
|
||||
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification. Must not be {@literal null}.
|
||||
* @param replacement the replacement document. Must not be {@literal null}.
|
||||
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
|
||||
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
|
||||
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
|
||||
* {@link #getCollectionName(Class) derived} from the given replacement value.
|
||||
*/
|
||||
default <T> UpdateResult replace(Query query, T replacement, ReplaceOptions options) {
|
||||
return replace(query, replacement, options, getCollectionName(ClassUtils.getUserClass(replacement)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
|
||||
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document
|
||||
* taking {@link ReplaceOptions} into account.<br />
|
||||
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification. Must not be {@literal null}.
|
||||
* @param replacement the replacement document. Must not be {@literal null}.
|
||||
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
|
||||
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
|
||||
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
|
||||
* {@link #getCollectionName(Class) derived} from the given replacement value.
|
||||
*/
|
||||
default <T> UpdateResult replace(Query query, T replacement, ReplaceOptions options, String collectionName) {
|
||||
|
||||
Assert.notNull(replacement, "Replacement must not be null");
|
||||
return replace(query, replacement, options, (Class<T>) ClassUtils.getUserClass(replacement), collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
|
||||
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document
|
||||
* taking {@link ReplaceOptions} into account.<br />
|
||||
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification. Must not be {@literal null}.
|
||||
* @param replacement the replacement document. Must not be {@literal null}.
|
||||
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
|
||||
* @param entityType the type used for mapping the {@link Query} to domain type fields and deriving the collection
|
||||
* from. Must not be {@literal null}.
|
||||
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
|
||||
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
|
||||
* {@link #getCollectionName(Class) derived} from the given replacement value.
|
||||
*/
|
||||
default <S> UpdateResult replace(Query query, S replacement, ReplaceOptions options, Class<S> entityType) {
|
||||
|
||||
return replace(query, replacement, options, entityType, getCollectionName(ClassUtils.getUserClass(entityType)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a> to
|
||||
* replace a single document matching {@link Criteria} of given {@link Query} with the {@code replacement} document
|
||||
* taking {@link ReplaceOptions} into account.<br />
|
||||
* <strong>NOTE:</strong> The replacement entity must not hold an {@literal id}.
|
||||
*
|
||||
* @param query the {@link Query} class that specifies the {@link Criteria} used to find a record and also an optional
|
||||
* fields specification. Must not be {@literal null}.
|
||||
* @param replacement the replacement document. Must not be {@literal null}.
|
||||
* @param options the {@link FindAndModifyOptions} holding additional information. Must not be {@literal null}.
|
||||
* @param entityType the type used for mapping the {@link Query} to domain type fields. Must not be {@literal null}.
|
||||
* @param collectionName the collection to query. Must not be {@literal null}.
|
||||
* @return the {@link UpdateResult} which lets you access the results of the previous replacement.
|
||||
* @throws org.springframework.data.mapping.MappingException if the collection name cannot be
|
||||
* {@link #getCollectionName(Class) derived} from the given replacement value.
|
||||
*/
|
||||
<S> UpdateResult replace(Query query, S replacement, ReplaceOptions options, Class<S> entityType,
|
||||
String collectionName);
|
||||
|
||||
/**
|
||||
* Returns the underlying {@link MongoConverter}.
|
||||
*
|
||||
|
||||
@@ -178,6 +178,7 @@ import com.mongodb.client.result.UpdateResult;
|
||||
* @author Anton Barkan
|
||||
* @author Bartłomiej Mazur
|
||||
* @author Michael Krog
|
||||
* @author Jakub Zurawa
|
||||
*/
|
||||
public class MongoTemplate
|
||||
implements MongoOperations, ApplicationContextAware, IndexOperationsProvider, ReadPreferenceAware {
|
||||
@@ -1618,7 +1619,7 @@ public class MongoTemplate
|
||||
}
|
||||
}
|
||||
|
||||
collectionToUse.replaceOne(filter, replacement, new ReplaceOptions().upsert(true));
|
||||
collectionToUse.replaceOne(filter, replacement, new com.mongodb.client.model.ReplaceOptions().upsert(true));
|
||||
}
|
||||
return mapped.getId();
|
||||
});
|
||||
@@ -1749,7 +1750,7 @@ public class MongoTemplate
|
||||
}
|
||||
}
|
||||
|
||||
ReplaceOptions replaceOptions = updateContext.getReplaceOptions(entityClass);
|
||||
com.mongodb.client.model.ReplaceOptions replaceOptions = updateContext.getReplaceOptions(entityClass);
|
||||
return collection.replaceOne(filter, updateObj, replaceOptions);
|
||||
} else {
|
||||
return multi ? collection.updateMany(queryObj, updateObj, opts)
|
||||
@@ -3421,6 +3422,56 @@ public class MongoTemplate
|
||||
return mongoDbFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> UpdateResult replace(Query query, S replacement, ReplaceOptions options, Class<S> entityType,
|
||||
String collectionName) {
|
||||
Assert.notNull(query, "Query must not be null");
|
||||
Assert.notNull(replacement, "Replacement must not be null");
|
||||
Assert.notNull(options, "Options must not be null Use ReplaceOptions#empty() instead");
|
||||
Assert.notNull(entityType, "EntityType must not be null");
|
||||
Assert.notNull(collectionName, "CollectionName must not be null");
|
||||
|
||||
Assert.isTrue(query.getLimit() <= 1, "Query must not define a limit other than 1 ore none");
|
||||
Assert.isTrue(query.getSkip() <= 0, "Query must not define skip");
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityType);
|
||||
QueryContext queryContext = queryOperations.createQueryContext(query);
|
||||
|
||||
CollectionPreparerDelegate collectionPreparer = createDelegate(query);
|
||||
Document mappedQuery = queryContext.getMappedQuery(entity);
|
||||
|
||||
replacement = maybeCallBeforeConvert(replacement, collectionName);
|
||||
Document mappedReplacement = operations.forEntity(replacement).toMappedDocument(this.mongoConverter).getDocument();
|
||||
maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
|
||||
|
||||
maybeEmitEvent(new BeforeSaveEvent<>(replacement, mappedReplacement, collectionName));
|
||||
maybeCallBeforeSave(replacement, mappedReplacement, collectionName);
|
||||
|
||||
UpdateResult result = doReplace(options, entityType, collectionName, queryContext, collectionPreparer, mappedQuery,
|
||||
mappedReplacement);
|
||||
|
||||
if (result.wasAcknowledged()) {
|
||||
maybeEmitEvent(new AfterSaveEvent<>(replacement, mappedReplacement, collectionName));
|
||||
maybeCallAfterSave(replacement, mappedReplacement, collectionName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private <S> UpdateResult doReplace(ReplaceOptions options, Class<S> entityType, String collectionName,
|
||||
QueryContext queryContext, CollectionPreparerDelegate collectionPreparer, Document mappedQuery,
|
||||
Document replacement) {
|
||||
ReplaceCallback replaceCallback = new ReplaceCallback(collectionPreparer, mappedQuery, replacement,
|
||||
queryContext.getCollation(entityType).orElse(null), options);
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(
|
||||
String.format("findAndReplace using query: %s for class: %s and replacement: %s " + "in collection: %s",
|
||||
serializeToJsonSafely(mappedQuery), entityType, serializeToJsonSafely(replacement), collectionName));
|
||||
}
|
||||
|
||||
return execute(collectionName, replaceCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link CloseableIterator} that is backed by a MongoDB {@link MongoCollection}.
|
||||
*
|
||||
@@ -3555,4 +3606,33 @@ public class MongoTemplate
|
||||
long countDocuments(CollectionPreparer collectionPreparer, String collection, Document filter,
|
||||
CountOptions options);
|
||||
}
|
||||
|
||||
private static class ReplaceCallback implements CollectionCallback<UpdateResult> {
|
||||
|
||||
private final CollectionPreparer<MongoCollection<Document>> collectionPreparer;
|
||||
private final Document query;
|
||||
private final Document update;
|
||||
private final @Nullable com.mongodb.client.model.Collation collation;
|
||||
private final ReplaceOptions options;
|
||||
|
||||
ReplaceCallback(CollectionPreparer<MongoCollection<Document>> collectionPreparer, Document query, Document update,
|
||||
@Nullable com.mongodb.client.model.Collation collation, ReplaceOptions options) {
|
||||
this.collectionPreparer = collectionPreparer;
|
||||
this.query = query;
|
||||
this.update = update;
|
||||
this.options = options;
|
||||
this.collation = collation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateResult doInCollection(MongoCollection<Document> collection)
|
||||
throws MongoException, DataAccessException {
|
||||
com.mongodb.client.model.ReplaceOptions opts = new com.mongodb.client.model.ReplaceOptions();
|
||||
opts.collation(collation);
|
||||
|
||||
opts.upsert(options.isUpsert());
|
||||
|
||||
return collectionPreparer.prepare(collection).replaceOne(query, update, opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1782,7 +1782,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
|
||||
deferredFilter = Mono.just(filter);
|
||||
}
|
||||
|
||||
ReplaceOptions replaceOptions = updateContext.getReplaceOptions(entityClass);
|
||||
com.mongodb.client.model.ReplaceOptions replaceOptions = updateContext.getReplaceOptions(entityClass);
|
||||
return deferredFilter.flatMap(it -> Mono.from(collectionToUse.replaceOne(it, updateObj, replaceOptions)));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Options for
|
||||
* <a href="https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/">replaceOne</a>.
|
||||
* <br />
|
||||
* Defaults to
|
||||
* <dl>
|
||||
* <dt>upsert</dt>
|
||||
* <dd>false</dd>
|
||||
* </dl>
|
||||
*
|
||||
* @author Jakub Zurawa
|
||||
*/
|
||||
package org.springframework.data.mongodb.core;
|
||||
|
||||
public class ReplaceOptions {
|
||||
private boolean upsert;
|
||||
|
||||
private static final ReplaceOptions NONE = new ReplaceOptions() {
|
||||
|
||||
private static final String ERROR_MSG = "ReplaceOptions.none() cannot be changed; Please use ReplaceOptions.options() instead";
|
||||
|
||||
@Override
|
||||
public ReplaceOptions upsert() {
|
||||
throw new UnsupportedOperationException(ERROR_MSG);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Static factory method to create a {@link ReplaceOptions} instance.
|
||||
* <dl>
|
||||
* <dt>upsert</dt>
|
||||
* <dd>false</dd>
|
||||
* </dl>
|
||||
*
|
||||
* @return new instance of {@link ReplaceOptions}.
|
||||
*/
|
||||
public static ReplaceOptions options() {
|
||||
return new ReplaceOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method returning an unmodifiable {@link ReplaceOptions} instance.
|
||||
*
|
||||
* @return unmodifiable {@link ReplaceOptions} instance.
|
||||
* @since 2.2
|
||||
*/
|
||||
public static ReplaceOptions none() {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static factory method to create a {@link ReplaceOptions} instance with
|
||||
* <dl>
|
||||
* <dt>upsert</dt>
|
||||
* <dd>false</dd>
|
||||
* </dl>
|
||||
*
|
||||
* @return new instance of {@link ReplaceOptions}.
|
||||
*/
|
||||
public static ReplaceOptions empty() {
|
||||
return new ReplaceOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new document if not exists.
|
||||
*
|
||||
* @return this.
|
||||
*/
|
||||
public ReplaceOptions upsert() {
|
||||
|
||||
this.upsert = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bit indicating if to create a new document if not exists.
|
||||
*
|
||||
* @return {@literal true} if set.
|
||||
*/
|
||||
public boolean isUpsert() {
|
||||
return upsert;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -116,6 +116,7 @@ import com.mongodb.client.result.UpdateResult;
|
||||
* @author Mark Paluch
|
||||
* @author Laszlo Csontos
|
||||
* @author duozhilin
|
||||
* @author Jakub Zurawa
|
||||
*/
|
||||
@ExtendWith(MongoClientExtension.class)
|
||||
public class MongoTemplateTests {
|
||||
@@ -3872,6 +3873,21 @@ public class MongoTemplateTests {
|
||||
assertThat(loaded).isEqualTo(source2);
|
||||
}
|
||||
|
||||
@Test // GH-4300
|
||||
public void replaceShouldReplaceDocument() {
|
||||
|
||||
org.bson.Document doc = new org.bson.Document("foo", "bar");
|
||||
String collectionName = "replace";
|
||||
template.save(doc, collectionName);
|
||||
|
||||
org.bson.Document replacement = new org.bson.Document("foo", "baz");
|
||||
UpdateResult updateResult = template.replace(query(where("foo").is("bar")), replacement, ReplaceOptions.options(),
|
||||
collectionName);
|
||||
|
||||
assertThat(updateResult.wasAcknowledged()).isTrue();
|
||||
assertThat(template.findOne(query(where("foo").is("baz")), org.bson.Document.class, collectionName)).isNotNull();
|
||||
}
|
||||
|
||||
private AtomicReference<ImmutableVersioned> createAfterSaveReference() {
|
||||
|
||||
AtomicReference<ImmutableVersioned> saved = new AtomicReference<>();
|
||||
|
||||
@@ -131,6 +131,7 @@ import com.mongodb.client.result.UpdateResult;
|
||||
* @author Michael J. Simons
|
||||
* @author Roman Puchkovskiy
|
||||
* @author Yadhukrishna S Pai
|
||||
* @author Jakub Zurawa
|
||||
*/
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@@ -173,7 +174,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
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.replaceOne(any(), any(), any(com.mongodb.client.model.ReplaceOptions.class))).thenReturn(updateResult);
|
||||
when(collection.withWriteConcern(any())).thenReturn(collectionWithWriteConcern);
|
||||
when(collection.distinct(anyString(), any(Document.class), any())).thenReturn(distinctIterable);
|
||||
when(collectionWithWriteConcern.deleteOne(any(Bson.class), any())).thenReturn(deleteResult);
|
||||
@@ -845,8 +846,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // GH-4277
|
||||
void findShouldUseReadConcernWhenPresent() {
|
||||
|
||||
template.find(new BasicQuery("{'foo' : 'bar'}").withReadConcern(ReadConcern.SNAPSHOT),
|
||||
AutogenerateableId.class);
|
||||
template.find(new BasicQuery("{'foo' : 'bar'}").withReadConcern(ReadConcern.SNAPSHOT), AutogenerateableId.class);
|
||||
|
||||
verify(collection).withReadConcern(ReadConcern.SNAPSHOT);
|
||||
}
|
||||
@@ -1002,7 +1002,8 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
|
||||
template.updateFirst(new BasicQuery("{}").collation(Collation.of("fr")), new Update(), AutogenerateableId.class);
|
||||
|
||||
ArgumentCaptor<ReplaceOptions> options = ArgumentCaptor.forClass(ReplaceOptions.class);
|
||||
ArgumentCaptor<com.mongodb.client.model.ReplaceOptions> options = ArgumentCaptor
|
||||
.forClass(com.mongodb.client.model.ReplaceOptions.class);
|
||||
verify(collection).replaceOne(any(), any(), options.capture());
|
||||
|
||||
assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr");
|
||||
@@ -1129,8 +1130,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
void appliesFieldsWhenInterfaceProjectionIsClosedAndQueryDoesNotDefineFields() {
|
||||
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonProjection.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
PersonProjection.class, CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(new Document("firstname", 1)));
|
||||
}
|
||||
@@ -1139,8 +1139,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
void doesNotApplyFieldsWhenInterfaceProjectionIsClosedAndQueryDefinesFields() {
|
||||
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document("bar", 1), Person.class,
|
||||
PersonProjection.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
PersonProjection.class, CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(new Document("bar", 1)));
|
||||
}
|
||||
@@ -1149,8 +1148,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {
|
||||
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonSpELProjection.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
PersonSpELProjection.class, CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||
}
|
||||
@@ -1159,8 +1157,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
void appliesFieldsToDtoProjection() {
|
||||
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
Jedi.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
Jedi.class, CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(new Document("firstname", 1)));
|
||||
}
|
||||
@@ -1169,8 +1166,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
void doesNotApplyFieldsToDtoProjectionWhenQueryDefinesFields() {
|
||||
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document("bar", 1), Person.class,
|
||||
Jedi.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
Jedi.class, CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(new Document("bar", 1)));
|
||||
}
|
||||
@@ -1179,8 +1175,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
void doesNotApplyFieldsWhenTargetIsNotAProjection() {
|
||||
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
Person.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
Person.class, CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||
}
|
||||
@@ -1189,8 +1184,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
void doesNotApplyFieldsWhenTargetExtendsDomainType() {
|
||||
|
||||
template.doFind(CollectionPreparer.identity(), "star-wars", new Document(), new Document(), Person.class,
|
||||
PersonExtended.class,
|
||||
CursorPreparer.NO_OP_PREPARER);
|
||||
PersonExtended.class, CursorPreparer.NO_OP_PREPARER);
|
||||
|
||||
verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
|
||||
}
|
||||
@@ -1243,7 +1237,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
|
||||
template.save(entity);
|
||||
|
||||
verify(collection, times(1)).replaceOne(queryCaptor.capture(), updateCaptor.capture(), any(ReplaceOptions.class));
|
||||
verify(collection, times(1)).replaceOne(queryCaptor.capture(), updateCaptor.capture(), any(com.mongodb.client.model.ReplaceOptions.class));
|
||||
|
||||
assertThat(queryCaptor.getValue()).isEqualTo(new Document("_id", 1).append("version", 10));
|
||||
assertThat(updateCaptor.getValue())
|
||||
@@ -1785,7 +1779,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
|
||||
ArgumentCaptor<Document> captor = ArgumentCaptor.forClass(Document.class);
|
||||
|
||||
verify(collection).replaceOne(any(), captor.capture(), any(ReplaceOptions.class));
|
||||
verify(collection).replaceOne(any(), captor.capture(), any(com.mongodb.client.model.ReplaceOptions.class));
|
||||
assertThat(captor.getValue()).containsEntry("added-by", "callback");
|
||||
}
|
||||
|
||||
@@ -2005,7 +1999,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-2341
|
||||
void saveShouldAppendNonDefaultShardKeyToVersionedEntityIfNotPresentInFilter() {
|
||||
|
||||
when(collection.replaceOne(any(), any(), any(ReplaceOptions.class)))
|
||||
when(collection.replaceOne(any(), any(), any(com.mongodb.client.model.ReplaceOptions.class)))
|
||||
.thenReturn(UpdateResult.acknowledged(1, 1L, null));
|
||||
|
||||
template.save(new ShardedVersionedEntityWithNonDefaultShardKey("id-1", 1L, "AT", 4230));
|
||||
@@ -2093,7 +2087,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
@Test // DATAMONGO-2341
|
||||
void saveVersionedShouldProjectOnShardKeyWhenLoadingExistingDocument() {
|
||||
|
||||
when(collection.replaceOne(any(), any(), any(ReplaceOptions.class)))
|
||||
when(collection.replaceOne(any(), any(), any(com.mongodb.client.model.ReplaceOptions.class)))
|
||||
.thenReturn(UpdateResult.acknowledged(1, 1L, null));
|
||||
when(findIterable.first()).thenReturn(new Document("_id", "id-1").append("country", "US").append("userid", 4230));
|
||||
|
||||
@@ -2442,6 +2436,44 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
|
||||
any(FindOneAndReplaceOptions.class));
|
||||
}
|
||||
|
||||
@Test // GH-4300
|
||||
void replaceShouldUseCollationWhenPresent() {
|
||||
|
||||
template.replace(new BasicQuery("{}").collation(Collation.of("fr")), new AutogenerateableId());
|
||||
|
||||
ArgumentCaptor<com.mongodb.client.model.ReplaceOptions> options = ArgumentCaptor
|
||||
.forClass(com.mongodb.client.model.ReplaceOptions.class);
|
||||
verify(collection).replaceOne(any(), any(), options.capture());
|
||||
|
||||
assertThat(options.getValue().isUpsert()).isFalse();
|
||||
assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr");
|
||||
}
|
||||
|
||||
@Test // GH-4300
|
||||
void replaceShouldUpsert() {
|
||||
|
||||
template.replace(new BasicQuery("{}"), new Sith(), ReplaceOptions.options().upsert());
|
||||
|
||||
ArgumentCaptor<com.mongodb.client.model.ReplaceOptions> options = ArgumentCaptor
|
||||
.forClass(com.mongodb.client.model.ReplaceOptions.class);
|
||||
verify(collection).replaceOne(any(), any(), options.capture());
|
||||
|
||||
assertThat(options.getValue().isUpsert()).isTrue();
|
||||
|
||||
}
|
||||
|
||||
@Test // GH-4300
|
||||
void replaceShouldUseDefaultCollationWhenPresent() {
|
||||
|
||||
template.replace(new BasicQuery("{}"), new Sith(), ReplaceOptions.options());
|
||||
|
||||
ArgumentCaptor<com.mongodb.client.model.ReplaceOptions> options = ArgumentCaptor
|
||||
.forClass(com.mongodb.client.model.ReplaceOptions.class);
|
||||
verify(collection).replaceOne(any(), any(), options.capture());
|
||||
|
||||
assertThat(options.getValue().getCollation().getLocale()).isEqualTo("de_AT");
|
||||
}
|
||||
|
||||
class AutogenerateableId {
|
||||
|
||||
@Id BigInteger id;
|
||||
|
||||
Reference in New Issue
Block a user