diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 252871e29..08971acf3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -2030,6 +2030,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { } public DBCursor doInCollection(DBCollection collection) throws MongoException, DataAccessException { + if (fields == null || fields.toMap().isEmpty()) { return collection.find(query); } else { @@ -2185,11 +2186,11 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { } if (query.getSkip() <= 0 && query.getLimit() <= 0 && query.getSortObject() == null - && !StringUtils.hasText(query.getHint())) { + && !StringUtils.hasText(query.getHint()) && !query.getMeta().hasValues()) { return cursor; } - DBCursor cursorToUse = cursor; + DBCursor cursorToUse = cursor.copy(); try { if (query.getSkip() > 0) { @@ -2205,6 +2206,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware { if (StringUtils.hasText(query.getHint())) { cursorToUse = cursorToUse.hint(query.getHint()); } + if (query.getMeta().hasValues()) { + for (Entry entry : query.getMeta().values()) { + cursorToUse = cursorToUse.addSpecial(entry.getKey(), entry.getValue()); + } + } + } catch (RuntimeException e) { throw potentiallyConvertRuntimeException(e); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java new file mode 100644 index 000000000..4b364bc59 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java @@ -0,0 +1,193 @@ +/* + * Copyright 2014 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 + * + * http://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.query; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +import org.springframework.util.Assert; +import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; + +/** + * Meta-data for {@link Query} instances. + * + * @author Christoph Strobl + * @author Oliver Gierke + * @since 1.6 + */ +public class Meta { + + private enum MetaKey { + MAX_TIME_MS("$maxTimeMS"), MAX_SCAN("$maxScan"), COMMENT("$comment"), SNAPSHOT("$snapshot"); + + private String key; + + private MetaKey(String key) { + this.key = key; + } + } + + private final Map values = new LinkedHashMap(2); + + /** + * @return {@literal null} if not set. + */ + public Long getMaxTimeMsec() { + return getValue(MetaKey.MAX_TIME_MS.key); + } + + /** + * Set the maximum time limit in milliseconds for processing operations. + * + * @param maxTimeMsec + */ + public void setMaxTimeMsec(long maxTimeMsec) { + setMaxTime(maxTimeMsec, TimeUnit.MILLISECONDS); + } + + /** + * Set the maximum time limit for processing operations. + * + * @param timeout + * @param timeUnit + */ + public void setMaxTime(long timeout, TimeUnit timeUnit) { + setValue(MetaKey.MAX_TIME_MS.key, (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS).toMillis(timeout)); + } + + /** + * @return {@literal null} if not set. + */ + public Long getMaxScan() { + return getValue(MetaKey.MAX_SCAN.key); + } + + /** + * Only scan the specified number of documents. + * + * @param maxScan + */ + public void setMaxScan(long maxScan) { + setValue(MetaKey.MAX_SCAN.key, maxScan); + } + + /** + * Add a comment to the query. + * + * @param comment + */ + public void setComment(String comment) { + setValue(MetaKey.COMMENT.key, comment); + } + + /** + * @return {@literal null} if not set. + */ + public String getComment() { + return getValue(MetaKey.COMMENT.key); + } + + /** + * Using snapshot prevents the cursor from returning a document more than once. + * + * @param useSnapshot + */ + public void setSnapshot(boolean useSnapshot) { + setValue(MetaKey.SNAPSHOT.key, useSnapshot); + } + + /** + * @return {@literal null} if not set. + */ + public boolean getSnapshot() { + return getValue(MetaKey.SNAPSHOT.key, false); + } + + /** + * @return + */ + public boolean hasValues() { + return !this.values.isEmpty(); + } + + /** + * Get {@link Iterable} of set meta values. + * + * @return + */ + public Iterable> values() { + return Collections.unmodifiableSet(this.values.entrySet()); + } + + /** + * Sets or removes the value in case of {@literal null} or empty {@link String}. + * + * @param key must not be {@literal null} or empty. + * @param value + */ + private void setValue(String key, Object value) { + + Assert.hasText(key, "Meta key must not be 'null' or blank."); + + if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) { + this.values.remove(key); + } + this.values.put(key, value); + } + + @SuppressWarnings("unchecked") + private T getValue(String key) { + return (T) this.values.get(key); + } + + private T getValue(String key, T defaultValue) { + + T value = getValue(key); + return value != null ? value : defaultValue; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return ObjectUtils.nullSafeHashCode(this.values); + } + + /* + * (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + + if (this == obj) { + return true; + } + + if (!(obj instanceof Meta)) { + return false; + } + + Meta other = (Meta) obj; + return ObjectUtils.nullSafeEquals(this.values, other.values); + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java index 453fd596f..9156a342a 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java @@ -25,6 +25,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -53,6 +54,8 @@ public class Query { private int limit; private String hint; + private Meta meta = new Meta(); + /** * Static factory method to create a {@link Query} using the provided {@link CriteriaDefinition}. * @@ -275,6 +278,84 @@ public class Query { return hint; } + /** + * @param maxTimeMsec + * @return + * @see Meta#setMaxTimeMsec(long) + * @since 1.6 + */ + public Query maxTimeMsec(long maxTimeMsec) { + + meta.setMaxTimeMsec(maxTimeMsec); + return this; + } + + /** + * @param timeout + * @param timeUnit + * @return + * @see Meta#setMaxTime(long, TimeUnit) + * @since 1.6 + */ + public Query maxTime(long timeout, TimeUnit timeUnit) { + + meta.setMaxTime(timeout, timeUnit); + return this; + } + + /** + * @param maxScan + * @return + * @see Meta#setMaxScan(long) + * @since 1.6 + */ + public Query maxScan(long maxScan) { + + meta.setMaxScan(maxScan); + return this; + } + + /** + * @param comment + * @return + * @see Meta#setComment(String) + * @since 1.6 + */ + public Query comment(String comment) { + + meta.setComment(comment); + return this; + } + + /** + * @return + * @see Meta#setSnapshot(boolean) + * @since 1.6 + */ + public Query useSnapshot() { + + meta.setSnapshot(true); + return this; + } + + /** + * @return never {@literal null}. + * @since 1.6 + */ + public Meta getMeta() { + return meta; + } + + /** + * @param meta must not be {@literal null}. + * @since 1.6 + */ + public void setMeta(Meta meta) { + + Assert.notNull(meta, "Query meta might be empty but must not be null."); + this.meta = meta; + } + protected List getCriteria() { return new ArrayList(this.criteria.values()); } @@ -312,8 +393,9 @@ public class Query { boolean hintEqual = this.hint == null ? that.hint == null : this.hint.equals(that.hint); boolean skipEqual = this.skip == that.skip; boolean limitEqual = this.limit == that.limit; + boolean metaEqual = nullSafeEquals(this.meta, that.meta); - return criteriaEqual && fieldsEqual && sortEqual && hintEqual && skipEqual && limitEqual; + return criteriaEqual && fieldsEqual && sortEqual && hintEqual && skipEqual && limitEqual && metaEqual; } /* @@ -331,6 +413,7 @@ public class Query { result += 31 * nullSafeHashCode(hint); result += 31 * skip; result += 31 * limit; + result += 31 * nullSafeHashCode(meta); return result; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Meta.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Meta.java new file mode 100644 index 000000000..9fe6c7cc7 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Meta.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014 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 + * + * http://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.data.annotation.QueryAnnotation; + +/** + * @author Christoph Strobl + * @since 1.6 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +@QueryAnnotation +public @interface Meta { + + /** + * Set the maximum time limit in milliseconds for processing operations. + * + * @return + */ + long maxExcecutionTime() default -1; + + /** + * Only scan the specified number of documents. + * + * @return + */ + long maxScanDocuments() default -1; + + /** + * Add a comment to the query. + * + * @return + */ + String comment() default ""; + + /** + * Using snapshot prevents the cursor from returning a document more than once. + * + * @return + */ + boolean snapshot() default false; + +} 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 f9771dbc6..c608b7b6e 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 @@ -86,6 +86,8 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { MongoParameterAccessor accessor = new MongoParametersParameterAccessor(method, parameters); Query query = createQuery(new ConvertingParameterAccessor(operations.getConverter(), accessor)); + applyQueryMetaAttributesWhenPresent(query); + Object result = null; if (isDeleteQuery()) { @@ -121,6 +123,14 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { return CONVERSION_SERVICE.convert(result, expectedReturnType); } + private Query applyQueryMetaAttributesWhenPresent(Query query) { + + if (method.hasQueryMetaAttributes()) { + query.setMeta(method.getQueryMetaAttributes()); + } + return query; + } + /** * 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 @@ -130,7 +140,12 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { * @return */ protected Query createCountQuery(ConvertingParameterAccessor accessor) { - return createQuery(accessor); + + Query query = createQuery(accessor); + + applyQueryMetaAttributesWhenPresent(query); + + return query; } /** 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 0b020c9ae..2c25d186b 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 @@ -27,6 +27,7 @@ import org.springframework.data.geo.GeoResults; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; +import org.springframework.data.mongodb.repository.Meta; import org.springframework.data.mongodb.repository.Query; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.QueryMethod; @@ -182,4 +183,55 @@ public class MongoQueryMethod extends QueryMethod { TypeInformation getReturnType() { return ClassTypeInformation.fromReturnTypeOf(method); } + + /** + * @return return true if {@link Meta} annotation is available. + * @since 1.6 + */ + public boolean hasQueryMetaAttributes() { + return getMetaAnnotation() != null; + } + + /** + * Returns the {@link Meta} annotation that is applied to the method or {@code null} if not available. + * + * @return + * @since 1.6 + */ + Meta getMetaAnnotation() { + return method.getAnnotation(Meta.class); + } + + /** + * Returns the {@link org.springframework.data.mongodb.core.query.Meta} attributes to be applied. + * + * @return never {@literal null}. + * @since 1.6 + */ + public org.springframework.data.mongodb.core.query.Meta getQueryMetaAttributes() { + + Meta meta = getMetaAnnotation(); + if (meta == null) { + return new org.springframework.data.mongodb.core.query.Meta(); + } + + org.springframework.data.mongodb.core.query.Meta metaAttributes = new org.springframework.data.mongodb.core.query.Meta(); + if (meta.maxExcecutionTime() > 0) { + metaAttributes.setMaxTimeMsec(meta.maxExcecutionTime()); + } + + if (meta.maxScanDocuments() > 0) { + metaAttributes.setMaxScan(meta.maxScanDocuments()); + } + + if (StringUtils.hasText(meta.comment())) { + metaAttributes.setComment(meta.comment()); + } + + if (meta.snapshot()) { + metaAttributes.setSnapshot(meta.snapshot()); + } + + return metaAttributes; + } } 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 e31589ecd..08b6b0c87 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 @@ -89,6 +89,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests { @Before public void setUp() { + when(cursor.copy()).thenReturn(cursor); when(factory.getDb()).thenReturn(db); when(factory.getExceptionTranslator()).thenReturn(exceptionTranslator); when(db.getCollection(Mockito.any(String.class))).thenReturn(collection); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java index e7dff1527..333cfd7a7 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/QueryCursorPreparerUnitTests.java @@ -15,16 +15,21 @@ */ package org.springframework.data.mongodb.core; +import static org.mockito.Matchers.*; import static org.mockito.Mockito.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.Query.*; +import java.util.concurrent.TimeUnit; + +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.MongoTemplate.QueryCursorPreparer; +import org.springframework.data.mongodb.core.query.Meta; import org.springframework.data.mongodb.core.query.Query; import com.mongodb.DBCursor; @@ -41,6 +46,13 @@ public class QueryCursorPreparerUnitTests { @Mock MongoDbFactory factory; @Mock DBCursor cursor; + @Mock DBCursor cursorToUse; + + @Before + public void setUp() { + when(cursor.copy()).thenReturn(cursorToUse); + } + /** * @see DATAMONGO-185 */ @@ -49,9 +61,81 @@ public class QueryCursorPreparerUnitTests { Query query = query(where("foo").is("bar")).withHint("hint"); - CursorPreparer preparer = new MongoTemplate(factory).new QueryCursorPreparer(query, null); - preparer.prepare(cursor); + pepare(query); - verify(cursor).hint("hint"); + verify(cursorToUse).hint("hint"); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void doesNotApplyMetaWhenEmpty() { + + Query query = query(where("foo").is("bar")); + query.setMeta(new Meta()); + + pepare(query); + + verify(cursor, never()).copy(); + verify(cursorToUse, never()).addSpecial(any(String.class), anyObject()); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void appliesMaxScanCorrectly() { + + Query query = query(where("foo").is("bar")).maxScan(100); + + pepare(query); + + verify(cursorToUse).addSpecial(eq("$maxScan"), eq(100L)); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void appliesMaxTimeCorrectly() { + + Query query = query(where("foo").is("bar")).maxTime(1, TimeUnit.SECONDS); + + pepare(query); + + verify(cursorToUse).addSpecial(eq("$maxTimeMS"), eq(1000L)); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void appliesCommentCorrectly() { + + Query query = query(where("foo").is("bar")).comment("spring data"); + + pepare(query); + + verify(cursorToUse).addSpecial(eq("$comment"), eq("spring data")); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void appliesSnapshotCorrectly() { + + Query query = query(where("foo").is("bar")).useSnapshot(); + + pepare(query); + + verify(cursorToUse).addSpecial(eq("$snapshot"), eq(true)); + } + + private DBCursor pepare(Query query) { + + CursorPreparer preparer = new MongoTemplate(factory).new QueryCursorPreparer(query, null); + return preparer.prepare(cursor); } } 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 ea75a89c4..02db5463b 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 @@ -29,9 +29,13 @@ import org.hamcrest.core.Is; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.Person; @@ -42,6 +46,7 @@ import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.repository.Meta; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.repository.core.RepositoryMetadata; @@ -140,6 +145,70 @@ public class AbstractMongoQueryUnitTests { Matchers.eq("persons")); } + /** + * @see DATAMONGO-957 + */ + @Test + public void metadataShouldNotBeAddedToQueryWhenNotPresent() { + + MongoQueryFake query = createQueryForMethod("findByFirstname", String.class); + query.execute(new Object[] { "fake" }); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); + + verify(this.mongoOperationsMock, times(1)) + .find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons")); + + assertThat(captor.getValue().getMeta().getComment(), nullValue()); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void metadataShouldBeAddedToQueryCorrectly() { + + MongoQueryFake query = createQueryForMethod("findByFirstname", String.class, Pageable.class); + query.execute(new Object[] { "fake", new PageRequest(0, 10) }); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); + + verify(this.mongoOperationsMock, times(1)) + .find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons")); + assertThat(captor.getValue().getMeta().getComment(), is("comment")); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void metadataShouldBeAddedToCountQueryCorrectly() { + + MongoQueryFake query = createQueryForMethod("findByFirstname", String.class, Pageable.class); + query.execute(new Object[] { "fake", new PageRequest(0, 10) }); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); + + verify(this.mongoOperationsMock, times(1)).count(captor.capture(), Matchers.eq("persons")); + assertThat(captor.getValue().getMeta().getComment(), is("comment")); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void metadataShouldBeAddedToStringBasedQueryCorrectly() { + + MongoQueryFake query = createQueryForMethod("findByAnnotatedQuery", String.class, Pageable.class); + query.execute(new Object[] { "fake", new PageRequest(0, 10) }); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); + + verify(this.mongoOperationsMock, times(1)) + .find(captor.capture(), Matchers.eq(Person.class), Matchers.eq("persons")); + assertThat(captor.getValue().getMeta().getComment(), is("comment")); + } + private MongoQueryFake createQueryForMethod(String methodName, Class... paramTypes) { try { @@ -191,5 +260,15 @@ public class AbstractMongoQueryUnitTests { List deleteByLastname(String lastname); Long deletePersonByLastname(String lastname); + + List findByFirstname(String firstname); + + @Meta(comment = "comment") + Page findByFirstname(String firstnanme, Pageable pageable); + + @Meta(comment = "comment") + @org.springframework.data.mongodb.repository.Query("{}") + Page findByAnnotatedQuery(String firstnanme, Pageable pageable); + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java index 126e944b8..7266c5392 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryMethodUnitTests.java @@ -34,6 +34,7 @@ import org.springframework.data.mongodb.core.User; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.repository.Address; import org.springframework.data.mongodb.repository.Contact; +import org.springframework.data.mongodb.repository.Meta; import org.springframework.data.mongodb.repository.Person; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; @@ -121,6 +122,61 @@ public class MongoQueryMethodUnitTests { new MongoQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository2.class), context); } + /** + * @see DATAMONGO-957 + */ + @Test + public void createsMongoQueryMethodWithEmptyMetaCorrectly() throws Exception { + + MongoQueryMethod method = queryMethod("emptyMetaAnnotation"); + assertThat(method.hasQueryMetaAttributes(), is(true)); + assertThat(method.getQueryMetaAttributes().hasValues(), is(false)); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void createsMongoQueryMethodWithMaxExecutionTimeCorrectly() throws Exception { + + MongoQueryMethod method = queryMethod("metaWithMaxExecutionTime"); + assertThat(method.hasQueryMetaAttributes(), is(true)); + assertThat(method.getQueryMetaAttributes().getMaxTimeMsec(), is(100L)); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void createsMongoQueryMethodWithMaxScanCorrectly() throws Exception { + + MongoQueryMethod method = queryMethod("metaWithMaxScan"); + assertThat(method.hasQueryMetaAttributes(), is(true)); + assertThat(method.getQueryMetaAttributes().getMaxScan(), is(10L)); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void createsMongoQueryMethodWithCommentCorrectly() throws Exception { + + MongoQueryMethod method = queryMethod("metaWithComment"); + assertThat(method.hasQueryMetaAttributes(), is(true)); + assertThat(method.getQueryMetaAttributes().getComment(), is("foo bar")); + } + + /** + * @see DATAMONGO-957 + */ + @Test + public void createsMongoQueryMethodWithSnapshotCorrectly() throws Exception { + + MongoQueryMethod method = queryMethod("metaWithSnapshotUsage"); + assertThat(method.hasQueryMetaAttributes(), is(true)); + assertThat(method.getQueryMetaAttributes().getSnapshot(), is(true)); + } + private MongoQueryMethod queryMethod(String name, Class... parameters) throws Exception { Method method = PersonRepository.class.getMethod(name, parameters); return new MongoQueryMethod(method, new DefaultRepositoryMetadata(PersonRepository.class), context); @@ -138,6 +194,22 @@ public class MongoQueryMethodUnitTests { GeoResults findByFirstname(String firstname, Point location); Collection> findByLastname(String lastname, Point location); + + @Meta + List emptyMetaAnnotation(); + + @Meta(maxExcecutionTime = 100) + List metaWithMaxExecutionTime(); + + @Meta(maxScanDocuments = 10) + List metaWithMaxScan(); + + @Meta(comment = "foo bar") + List metaWithComment(); + + @Meta(snapshot = true) + List metaWithSnapshotUsage(); + } interface SampleRepository extends Repository { @@ -155,4 +227,5 @@ public class MongoQueryMethodUnitTests { interface Customer { } + }