diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java index c9b5514b2..c4e07f5b3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/HintFunction.java @@ -27,6 +27,7 @@ import org.springframework.util.StringUtils; * Function object to apply a query hint. Can be an index name or a BSON document. * * @author Mark Paluch + * @author Christoph Strobl * @since 4.1 */ class HintFunction { @@ -67,6 +68,23 @@ class HintFunction { return (hint instanceof String hintString && StringUtils.hasText(hintString)) || hint instanceof Bson; } + /** + * Apply the hint to consumers depending on the hint format if {@link #isPresent() present}. + * + * @param registryProvider + * @param stringConsumer + * @param bsonConsumer + * @param + */ + public void ifPresent(@Nullable CodecRegistryProvider registryProvider, Function stringConsumer, + Function bsonConsumer) { + + if (!isPresent()) { + return; + } + apply(registryProvider, stringConsumer, bsonConsumer); + } + /** * Apply the hint to consumers depending on the hint format. * diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java index 2e86b8008..05aeda069 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java @@ -715,6 +715,7 @@ class QueryOperations { .arrayFilters(update.getArrayFilters().stream().map(ArrayFilter::asDocument).collect(Collectors.toList())); } + HintFunction.from(getQuery().getHint()).ifPresent(codecRegistryProvider, options::hintString, options::hint); applyCollation(domainType, options::collation); if (callback != null) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java index 6ae652ef8..056efc971 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java @@ -978,6 +978,28 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); } + @Test // GH-3218 + void updateUsesHintStringFromQuery() { + + template.updateFirst(new Query().withHint("index-1"), new Update().set("spring", "data"), Human.class); + + ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); + verify(collection).updateOne(any(Bson.class), any(Bson.class), options.capture()); + + assertThat(options.getValue().getHintString()).isEqualTo("index-1"); + } + + @Test // GH-3218 + void updateUsesHintDocumentFromQuery() { + + template.updateFirst(new Query().withHint("{ name : 1 }"), new Update().set("spring", "data"), Human.class); + + ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); + verify(collection).updateOne(any(Bson.class), any(Bson.class), options.capture()); + + assertThat(options.getValue().getHint()).isEqualTo(new Document("name", 1)); + } + @Test // DATAMONGO-1518 void replaceOneShouldUseCollationWhenPresent() { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java index ec4e9e90f..48b48a2e2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateUnitTests.java @@ -350,7 +350,28 @@ public class ReactiveMongoTemplateUnitTests { verify(collection).updateMany(any(), any(Bson.class), options.capture()); assertThat(options.getValue().getCollation().getLocale()).isEqualTo("fr"); + } + + @Test // GH-3218 + void updateUsesHintStringFromQuery() { + + template.updateFirst(new Query().withHint("index-1"), new Update().set("spring", "data"), Person.class).subscribe(); + + ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); + verify(collection).updateOne(any(Bson.class), any(Bson.class), options.capture()); + + assertThat(options.getValue().getHintString()).isEqualTo("index-1"); + } + + @Test // GH-3218 + void updateUsesHintDocumentFromQuery() { + + template.updateFirst(new Query().withHint("{ firstname : 1 }"), new Update().set("spring", "data"), Person.class).subscribe(); + + ArgumentCaptor options = ArgumentCaptor.forClass(UpdateOptions.class); + verify(collection).updateOne(any(Bson.class), any(Bson.class), options.capture()); + assertThat(options.getValue().getHint()).isEqualTo(new Document("firstname", 1)); } @Test // DATAMONGO-1518 diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index ebfc97e75..45f54b6ca 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -922,6 +922,7 @@ Most methods return the `Update` object to provide a fluent style for the API. * *updateMulti*: Updates all objects that match the query document criteria with the updated document. WARNING: `updateFirst` does not support ordering. Please use <> to apply `Sort`. +NOTE: Index hints for the update operation can be provided via `Query.withHint(...)`. [[mongodb-template-update.update]] ==== Methods in the `Update` Class