DATAMONGO-957 - Add support for query modifiers.
Using Meta allows to the define $comment, $maxScan, $maxTimeMS and $snapshot on query. When executed we add the meta information to the cursor in use. We’ve introduced the @Meta annotation that allows to the define $comment, $maxScan, $maxTimeMS and $snapshot on a repository finder method. Added tests to verify proper invocation of template methods Use DBCursor.copy() for CursorPreparer. Original pull request: #216.
This commit is contained in:
committed by
Thomas Darimont
parent
3597194742
commit
a9306b99ec
@@ -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<String, Object> entry : query.getMeta().values()) {
|
||||
cursorToUse = cursorToUse.addSpecial(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
throw potentiallyConvertRuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -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<String, Object> values = new LinkedHashMap<String, Object>(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<Entry<String, Object>> 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> T getValue(String key) {
|
||||
return (T) this.values.get(key);
|
||||
}
|
||||
|
||||
private <T> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<CriteriaDefinition> getCriteria() {
|
||||
return new ArrayList<CriteriaDefinition>(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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Query> 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<Query> 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<Query> 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<Query> 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<Person> deleteByLastname(String lastname);
|
||||
|
||||
Long deletePersonByLastname(String lastname);
|
||||
|
||||
List<Person> findByFirstname(String firstname);
|
||||
|
||||
@Meta(comment = "comment")
|
||||
Page<Person> findByFirstname(String firstnanme, Pageable pageable);
|
||||
|
||||
@Meta(comment = "comment")
|
||||
@org.springframework.data.mongodb.repository.Query("{}")
|
||||
Page<Person> findByAnnotatedQuery(String firstnanme, Pageable pageable);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<User> findByFirstname(String firstname, Point location);
|
||||
|
||||
Collection<GeoResult<User>> findByLastname(String lastname, Point location);
|
||||
|
||||
@Meta
|
||||
List<User> emptyMetaAnnotation();
|
||||
|
||||
@Meta(maxExcecutionTime = 100)
|
||||
List<User> metaWithMaxExecutionTime();
|
||||
|
||||
@Meta(maxScanDocuments = 10)
|
||||
List<User> metaWithMaxScan();
|
||||
|
||||
@Meta(comment = "foo bar")
|
||||
List<User> metaWithComment();
|
||||
|
||||
@Meta(snapshot = true)
|
||||
List<User> metaWithSnapshotUsage();
|
||||
|
||||
}
|
||||
|
||||
interface SampleRepository extends Repository<Contact, Long> {
|
||||
@@ -155,4 +227,5 @@ public class MongoQueryMethodUnitTests {
|
||||
interface Customer {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user