diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Hint.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Hint.java
new file mode 100644
index 000000000..480b8085e
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Hint.java
@@ -0,0 +1,48 @@
+/*
+ * 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.repository;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.core.annotation.AliasFor;
+
+/**
+ * Annotation to declare index hints for repository query, update and aggregate operations. The index is specified by
+ * its name.
+ *
+ * @author Christoph Strobl
+ * @since 4.1
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
+@Documented
+public @interface Hint {
+
+ String value() default "";
+
+ /**
+ * The name of the index to use. In case of an {@literal aggregation} the index is evaluated against the initial
+ * collection or view. Specify the index either by the index name.
+ *
+ * @return the index name.
+ */
+ @AliasFor("value")
+ String indexName() default "";
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java
index 9f0031e79..18f8b269b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java
@@ -39,13 +39,14 @@ import org.springframework.data.mongodb.core.annotation.Collation;
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Documented
@QueryAnnotation
+@Hint
public @interface Query {
/**
* Takes a MongoDB JSON string to define the actual query to be executed. This one will take precedence over the
* method name then.
*
- * @return empty {@link String} by default.
+ * @return empty {@link String} by default.
*/
String value() default "";
@@ -53,7 +54,7 @@ public @interface Query {
* Defines the fields that should be returned for the given query. Note that only these fields will make it into the
* domain object returned.
*
- * @return empty {@link String} by default.
+ * @return empty {@link String} by default.
*/
String fields() default "";
@@ -129,4 +130,21 @@ public @interface Query {
*/
@AliasFor(annotation = Collation.class, attribute = "value")
String collation() default "";
+
+ /**
+ * The name of the index to use.
+ * {@code @Query(value = "...", hint = "lastname-idx")} can be used as shortcut for:
+ *
+ *
+ * @Query(...)
+ * @Hint("lastname-idx")
+ * List<User> findAllByLastname(String collation);
+ *
+ *
+ * @return the index name.
+ * @since 4.1
+ * @see Hint#indexName()
+ */
+ @AliasFor(annotation = Hint.class, attribute = "indexName")
+ String hint() default "";
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
index 930a73331..e4a533b7d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
@@ -135,6 +135,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
applyQueryMetaAttributesWhenPresent(query);
query = applyAnnotatedDefaultSortIfPresent(query);
query = applyAnnotatedCollationIfPresent(query, accessor);
+ query = applyHintIfPresent(query);
FindWithQuery> find = typeToRead == null //
? executableFind //
@@ -225,6 +226,21 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
accessor, getQueryMethod().getParameters(), expressionParser, evaluationContextProvider);
}
+ /**
+ * If present apply the hint from the {@link org.springframework.data.mongodb.repository.Hint} annotation.
+ *
+ * @param query must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 4.1
+ */
+ Query applyHintIfPresent(Query query) {
+
+ if(!method.hasAnnotatedHint()) {
+ return query;
+ }
+ return query.withHint(method.getAnnotatedHint());
+ }
+
/**
* Creates a {@link Query} instance using the given {@link ConvertingParameterAccessor}. Will delegate to
* {@link #createQuery(ConvertingParameterAccessor)} by default but allows customization of the count query to be
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
index fbb078b43..85f92d918 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
@@ -160,6 +160,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
applyQueryMetaAttributesWhenPresent(query);
query = applyAnnotatedDefaultSortIfPresent(query);
query = applyAnnotatedCollationIfPresent(query, accessor);
+ query = applyHintIfPresent(query);
FindWithQuery> find = typeToRead == null //
? findOperationWithProjection //
@@ -269,6 +270,21 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
accessor, getQueryMethod().getParameters(), expressionParser, evaluationContextProvider);
}
+ /**
+ * If present apply the hint from the {@link org.springframework.data.mongodb.repository.Hint} annotation.
+ *
+ * @param query must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 4.1
+ */
+ Query applyHintIfPresent(Query query) {
+
+ if(!method.hasAnnotatedHint()) {
+ return query;
+ }
+ return query.withHint(method.getAnnotatedHint());
+ }
+
/**
* Creates a {@link Query} instance using the given {@link ConvertingParameterAccessor}. Will delegate to
* {@link #createQuery(ConvertingParameterAccessor)} by default but allows customization of the count query to be
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
index 0ee488c33..d88655887 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
@@ -102,6 +102,21 @@ abstract class AggregationUtils {
return builder;
}
+ /**
+ * If present apply the hint from the {@link org.springframework.data.mongodb.repository.Hint} annotation.
+ *
+ * @param builder must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 4.1
+ */
+ static AggregationOptions.Builder applyHint(AggregationOptions.Builder builder, MongoQueryMethod queryMethod) {
+
+ if(!queryMethod.hasAnnotatedHint()) {
+ return builder;
+ }
+ return builder.hint(queryMethod.getAnnotatedHint());
+ }
+
/**
* Append {@code $sort} aggregation stage if {@link ConvertingParameterAccessor#getSort()} is present.
*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
index 1b8f6b6a5..8db045881 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java
@@ -33,6 +33,7 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.repository.Aggregation;
+import org.springframework.data.mongodb.repository.Hint;
import org.springframework.data.mongodb.repository.Meta;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.Tailable;
@@ -362,6 +363,27 @@ public class MongoQueryMethod extends QueryMethod {
"Expected to find @Aggregation annotation but did not; Make sure to check hasAnnotatedAggregation() before."));
}
+ /**
+ * @return {@literal true} if the {@link Hint} annotation is present and the index name is not empty.
+ * @since 4.1
+ */
+ public boolean hasAnnotatedHint() {
+ return StringUtils.hasText(getAnnotatedHint());
+ }
+
+ /**
+ * Returns the aggregation pipeline declared via a {@link Hint} annotation.
+ *
+ * @return the index name (might be empty) or {@literal null} if not present.
+ * @since 4.1
+ */
+ @Nullable
+ public String getAnnotatedHint() {
+
+ Optional hint = doFindAnnotation(Hint.class);
+ return hint.map(Hint::indexName).orElse(null);
+ }
+
private Optional findAnnotatedAggregation() {
return lookupAggregationAnnotation() //
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
index 13c49b521..1ebfdbfcf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
@@ -128,6 +128,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
AggregationUtils.applyCollation(builder, method.getAnnotatedCollation(), accessor, method.getParameters(),
expressionParser, evaluationContextProvider);
AggregationUtils.applyMeta(builder, method);
+ AggregationUtils.applyHint(builder, method);
return builder.build();
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
index a0ee58fd2..a3847a390 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
@@ -29,6 +29,7 @@ import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
+import org.springframework.data.mongodb.core.aggregation.AggregationOptions.Builder;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
import org.springframework.data.mongodb.core.convert.MongoConverter;
@@ -178,6 +179,7 @@ public class StringBasedAggregation extends AbstractMongoQuery {
AggregationUtils.applyCollation(builder, method.getAnnotatedCollation(), accessor, method.getParameters(),
expressionParser, evaluationContextProvider);
AggregationUtils.applyMeta(builder, method);
+ AggregationUtils.applyHint(builder, method);
return builder.build();
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
index 304bfadb9..a6c7d3ed9 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java
@@ -61,6 +61,7 @@ import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
+import org.springframework.data.mongodb.repository.Hint;
import org.springframework.data.mongodb.repository.Meta;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Update;
@@ -458,7 +459,7 @@ class AbstractMongoQueryUnitTests {
void updateExecutionCallsUpdateAllCorrectly() {
when(terminatingUpdate.all()).thenReturn(updateResultMock);
-
+
createQueryForMethod("findAndIncreaseVisitsByLastname", String.class, int.class) //
.execute(new Object[] { "dalinar", 100 });
@@ -469,6 +470,29 @@ class AbstractMongoQueryUnitTests {
assertThat(update.getValue().getUpdateObject()).isEqualTo(Document.parse("{ '$inc' : { 'visits' : 100 } }"));
}
+ @Test // GH-3230
+ void findShouldApplyHint() {
+
+ createQueryForMethod("findWithHintByFirstname", String.class).execute(new Object[] { "Jasna" });
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class);
+ verify(withQueryMock).matching(captor.capture());
+ assertThat(captor.getValue().getHint()).isEqualTo("idx-fn");
+ }
+
+ @Test // GH-3230
+ void updateShouldApplyHint() {
+
+ when(terminatingUpdate.all()).thenReturn(updateResultMock);
+
+ createQueryForMethod("findAndIncreaseVisitsByLastname", String.class, int.class) //
+ .execute(new Object[] { "dalinar", 100 });
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class);
+ verify(executableUpdate).matching(captor.capture());
+ assertThat(captor.getValue().getHint()).isEqualTo("idx-ln");
+ }
+
private MongoQueryFake createQueryForMethod(String methodName, Class>... paramTypes) {
return createQueryForMethod(Repo.class, methodName, paramTypes);
}
@@ -584,8 +608,12 @@ class AbstractMongoQueryUnitTests {
@org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : 'en_US' }")
List findWithWithCollationParameterAndAnnotationByFirstName(String firstname, Collation collation);
+ @Hint("idx-ln")
@Update("{ '$inc' : { 'visits' : ?1 } }")
void findAndIncreaseVisitsByLastname(String lastname, int value);
+
+ @Hint("idx-fn")
+ void findWithHintByFirstname(String firstname);
}
// DATAMONGO-1872
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java
index e74f841e1..e7fc85400 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java
@@ -18,6 +18,15 @@ package org.springframework.data.mongodb.repository.query;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
+import com.mongodb.MongoClientSettings;
+import com.mongodb.client.result.UpdateResult;
+import org.bson.codecs.configuration.CodecRegistry;
+import org.springframework.data.mongodb.core.ReactiveUpdateOperation.TerminatingUpdate;
+import org.springframework.data.mongodb.core.ReactiveUpdateOperation.ReactiveUpdate;
+import org.springframework.data.mongodb.core.ReactiveUpdateOperation.UpdateWithQuery;
+import org.springframework.data.mongodb.core.query.UpdateDefinition;
+import org.springframework.data.mongodb.repository.Hint;
+import org.springframework.data.mongodb.repository.Update;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -71,6 +80,9 @@ class AbstractReactiveMongoQueryUnitTests {
@Mock ReactiveFind> executableFind;
@Mock FindWithQuery> withQueryMock;
+ @Mock ReactiveUpdate executableUpdate;
+ @Mock UpdateWithQuery updateWithQuery;
+ @Mock TerminatingUpdate terminatingUpdate;
@BeforeEach
void setUp() {
@@ -91,6 +103,11 @@ class AbstractReactiveMongoQueryUnitTests {
doReturn(Flux.empty()).when(withQueryMock).all();
doReturn(Mono.empty()).when(withQueryMock).first();
doReturn(Mono.empty()).when(withQueryMock).one();
+
+ doReturn(executableUpdate).when(mongoOperationsMock).update(any());
+ doReturn(executableUpdate).when(executableUpdate).inCollection(anyString());
+ doReturn(updateWithQuery).when(executableUpdate).matching(any(Query.class));
+ doReturn(terminatingUpdate).when(updateWithQuery).apply(any(UpdateDefinition.class));
}
@Test // DATAMONGO-1854
@@ -223,6 +240,29 @@ class AbstractReactiveMongoQueryUnitTests {
.contains(Collation.of("en_US").toDocument());
}
+ @Test // GH-3230
+ void findShouldApplyHint() {
+
+ createQueryForMethod("findWithHintByFirstname", String.class).executeBlocking(new Object[] { "Jasna" });
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class);
+ verify(withQueryMock).matching(captor.capture());
+ assertThat(captor.getValue().getHint()).isEqualTo("idx-fn");
+ }
+
+ @Test // GH-3230
+ void updateShouldApplyHint() {
+
+ when(terminatingUpdate.all()).thenReturn(Mono.just(mock(UpdateResult.class)));
+
+ createQueryForMethod("findAndIncreaseVisitsByLastname", String.class, int.class) //
+ .executeBlocking(new Object[] { "dalinar", 100 });
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class);
+ verify(executableUpdate).matching(captor.capture());
+ assertThat(captor.getValue().getHint()).isEqualTo("idx-ln");
+ }
+
private ReactiveMongoQueryFake createQueryForMethod(String methodName, Class>... paramTypes) {
return createQueryForMethod(Repo.class, methodName, paramTypes);
}
@@ -291,6 +331,11 @@ class AbstractReactiveMongoQueryUnitTests {
isLimitingQuery = limitingQuery;
return this;
}
+
+ @Override
+ protected Mono getCodecRegistry() {
+ return Mono.just(MongoClientSettings.getDefaultCodecRegistry());
+ }
}
private interface Repo extends ReactiveMongoRepository {
@@ -315,5 +360,12 @@ class AbstractReactiveMongoQueryUnitTests {
@org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : 'en_US' }")
List findWithWithCollationParameterAndAnnotationByFirstName(String firstname, Collation collation);
+
+ @Hint("idx-ln")
+ @Update("{ '$inc' : { 'visits' : ?1 } }")
+ void findAndIncreaseVisitsByLastname(String lastname, int value);
+
+ @Hint("idx-fn")
+ void findWithHintByFirstname(String firstname);
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
index 8b4645765..99b0b241d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregationUnitTests.java
@@ -21,6 +21,7 @@ import static org.mockito.Mockito.*;
import lombok.Value;
import org.reactivestreams.Publisher;
+import org.springframework.data.mongodb.repository.Hint;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -173,6 +174,13 @@ public class ReactiveStringBasedAggregationUnitTests {
verify(operations).execute(any());
}
+ @Test // GH-3230
+ void aggregatePicksUpHintFromAnnotation() {
+
+ AggregationInvocation invocation = executeAggregation("withHint");
+ assertThat(hintOf(invocation)).isEqualTo("idx");
+ }
+
private AggregationInvocation executeAggregation(String name, Object... args) {
Class>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(size -> new Class>[size]);
@@ -216,6 +224,12 @@ public class ReactiveStringBasedAggregationUnitTests {
: null;
}
+ @Nullable
+ private Object hintOf(AggregationInvocation invocation) {
+ return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getHintObject().orElse(null)
+ : null;
+ }
+
private Class> targetTypeOf(AggregationInvocation invocation) {
return invocation.getTargetType();
}
@@ -243,6 +257,10 @@ public class ReactiveStringBasedAggregationUnitTests {
@Aggregation(pipeline = RAW_GROUP_BY_LASTNAME_STRING, collation = "de_AT")
Mono aggregateWithCollation(Collation collation);
+
+ @Hint("idx")
+ @Aggregation(RAW_GROUP_BY_LASTNAME_STRING)
+ String withHint();
}
static class PersonAggregate {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
index 62c699e92..a88534afd 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationUnitTests.java
@@ -58,6 +58,7 @@ import org.springframework.data.mongodb.core.convert.QueryMapper;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.repository.Aggregation;
+import org.springframework.data.mongodb.repository.Hint;
import org.springframework.data.mongodb.repository.Meta;
import org.springframework.data.mongodb.repository.Person;
import org.springframework.data.projection.ProjectionFactory;
@@ -260,6 +261,13 @@ public class StringBasedAggregationUnitTests {
.withMessageContaining("Page");
}
+ @Test // GH-3230
+ void aggregatePicksUpHintFromAnnotation() {
+
+ AggregationInvocation invocation = executeAggregation("withHint");
+ assertThat(hintOf(invocation)).isEqualTo("idx");
+ }
+
private AggregationInvocation executeAggregation(String name, Object... args) {
Class>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
@@ -302,6 +310,12 @@ public class StringBasedAggregationUnitTests {
: null;
}
+ @Nullable
+ private Object hintOf(AggregationInvocation invocation) {
+ return invocation.aggregation.getOptions() != null ? invocation.aggregation.getOptions().getHintObject().orElse(null)
+ : null;
+ }
+
private Class> targetTypeOf(AggregationInvocation invocation) {
return invocation.getTargetType();
}
@@ -350,6 +364,10 @@ public class StringBasedAggregationUnitTests {
@Aggregation(RAW_GROUP_BY_LASTNAME_STRING)
String simpleReturnType();
+
+ @Hint("idx")
+ @Aggregation(RAW_GROUP_BY_LASTNAME_STRING)
+ String withHint();
}
private interface UnsupportedRepository extends Repository {
diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc
index de46bdcdb..4090c8b8b 100644
--- a/src/main/asciidoc/reference/mongo-repositories.adoc
+++ b/src/main/asciidoc/reference/mongo-repositories.adoc
@@ -297,6 +297,25 @@ lower / upper bounds (`$gt` / `$gte` & `$lt` / `$lte`) according to `Range`
NOTE: If the property criterion compares a document, the order of the fields and exact equality in the document matters.
+[[mongodb.repositories.queries.hint]]
+=== Repository Index Hints
+
+The `@Hint` annotation allows to override MongoDB's default index selection and forces the database to use the specified index instead.
+
+.Example of index hints
+====
+[source,java]
+----
+@Hint("lastname-idx") <1>
+List findByLastname(String lastname);
+
+@Query(value = "{ 'firstname' : ?0 }", hint="firstname-idx") <2>
+List findByFirstname(String firstname);
+----
+<1> Use the index with name `lastname-idx`.
+<2> The `@Query` annotation defines the `hint` alias which is equivalent to explicitly adding the `@Hint` annotation.
+====
+
[[mongodb.repositories.queries.update]]
=== Repository Update Methods