Add Hint annotation.
This commit introduces the new `@Hint` annotation that allows to override MongoDB's default index selection for repository query, update and aggregate operations.
```
@Hint("lastname-idx")
List<Person> findByLastname(String lastname);
@Query(value = "{ 'firstname' : ?0 }", hint="firstname-idx")
List<Person> findByFirstname(String firstname);
```
Closes: #3230
Original pull request: #4339
This commit is contained in:
committed by
Mark Paluch
parent
af2076d4a5
commit
7b44f78133
@@ -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 "";
|
||||
}
|
||||
@@ -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. <br />
|
||||
* {@code @Query(value = "...", hint = "lastname-idx")} can be used as shortcut for:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Query(...)
|
||||
* @Hint("lastname-idx")
|
||||
* List<User> findAllByLastname(String collation);
|
||||
* </pre>
|
||||
*
|
||||
* @return the index name.
|
||||
* @since 4.1
|
||||
* @see Hint#indexName()
|
||||
*/
|
||||
@AliasFor(annotation = Hint.class, attribute = "indexName")
|
||||
String hint() default "";
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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> hint = doFindAnnotation(Hint.class);
|
||||
return hint.map(Hint::indexName).orElse(null);
|
||||
}
|
||||
|
||||
private Optional<String[]> findAnnotatedAggregation() {
|
||||
|
||||
return lookupAggregationAnnotation() //
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<Query> 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<Query> 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<Person> 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
|
||||
|
||||
@@ -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<Query> 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<Query> 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<CodecRegistry> getCodecRegistry() {
|
||||
return Mono.just(MongoClientSettings.getDefaultCodecRegistry());
|
||||
}
|
||||
}
|
||||
|
||||
private interface Repo extends ReactiveMongoRepository<Person, Long> {
|
||||
@@ -315,5 +360,12 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
|
||||
@org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : 'en_US' }")
|
||||
List<Person> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PersonAggregate> aggregateWithCollation(Collation collation);
|
||||
|
||||
@Hint("idx")
|
||||
@Aggregation(RAW_GROUP_BY_LASTNAME_STRING)
|
||||
String withHint();
|
||||
}
|
||||
|
||||
static class PersonAggregate {
|
||||
|
||||
@@ -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<Person, Long> {
|
||||
|
||||
@@ -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<Person> findByLastname(String lastname);
|
||||
|
||||
@Query(value = "{ 'firstname' : ?0 }", hint="firstname-idx") <2>
|
||||
List<Person> 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user