diff --git a/pom.xml b/pom.xml index dfb64f9ba3..0bcd005366 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1480-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index ae0a5d6c8f..a667639a06 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1480-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1480-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 2d02722262..50fff77434 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1480-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml index ee5e3336db..bc1bc34ce7 100644 --- a/spring-data-mongodb-log4j/pom.xml +++ b/spring-data-mongodb-log4j/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1480-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 8072d3f665..e8d7bf18dc 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1480-SNAPSHOT ../pom.xml 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 11960330b4..e26cd4d413 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 @@ -93,6 +93,7 @@ import org.springframework.data.mongodb.core.mapreduce.MapReduceOptions; import org.springframework.data.mongodb.core.mapreduce.MapReduceResults; import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Meta; import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; @@ -139,6 +140,7 @@ * @author Christoph Strobl * @author Doménique Tilleuil * @author Niko Schmuck + * @author Mark Paluch */ @SuppressWarnings("deprecation") public class MongoTemplate implements MongoOperations, ApplicationContextAware { @@ -2249,7 +2251,7 @@ public DBObject doInCollection(DBCollection collection) throws MongoException, D * @author Thomas Darimont */ - static interface DbObjectCallback { + interface DbObjectCallback { T doWith(DBObject object); } @@ -2347,23 +2349,49 @@ public DBCursor prepare(DBCursor cursor) { DBCursor cursorToUse = cursor.copy(); try { + if (query.getSkip() > 0) { cursorToUse = cursorToUse.skip(query.getSkip()); } + if (query.getLimit() > 0) { cursorToUse = cursorToUse.limit(query.getLimit()); } + if (query.getSortObject() != null) { DBObject sortDbo = type != null ? getMappedSortObject(query, type) : query.getSortObject(); cursorToUse = cursorToUse.sort(sortDbo); } + 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()); } + + for (Meta.CursorOption option : query.getMeta().getFlags()) { + + switch (option) { + case EXHAUST: + cursorToUse = cursorToUse.addOption(Bytes.QUERYOPTION_EXHAUST); + break; + case NO_TIMEOUT: + cursorToUse = cursorToUse.addOption(Bytes.QUERYOPTION_NOTIMEOUT); + break; + case PARTIAL: + cursorToUse = cursorToUse.addOption(Bytes.QUERYOPTION_PARTIAL); + break; + case SLAVE_OK: + cursorToUse = cursorToUse.addOption(Bytes.QUERYOPTION_SLAVEOK); + break; + default: + throw new IllegalArgumentException(String.format("%s is no supported flag.", option)); + } + } } } catch (RuntimeException 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 index 4b364bc596..0543966d3c 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2016 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. @@ -17,8 +17,10 @@ import java.util.Collections; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.util.Assert; @@ -30,6 +32,7 @@ * * @author Christoph Strobl * @author Oliver Gierke + * @author Mark Paluch * @since 1.6 */ public class Meta { @@ -39,12 +42,13 @@ private enum MetaKey { private String key; - private MetaKey(String key) { + MetaKey(String key) { this.key = key; } } private final Map values = new LinkedHashMap(2); + private final Set flags = new LinkedHashSet(); /** * @return {@literal null} if not set. @@ -120,11 +124,32 @@ public boolean getSnapshot() { return getValue(MetaKey.SNAPSHOT.key, false); } + /** + * Add {@link CursorOption} influencing behavior of the {@link com.mongodb.DBCursor}. + * + * @param option must not be {@literal null}. + * @return + * @since 1.10 + */ + public boolean addFlag(CursorOption option) { + + Assert.notNull(option, "CursorOption must not be null!"); + return this.flags.add(option); + } + + /** + * @return never {@literal null}. + * @since 1.10 + */ + public Set getFlags() { + return flags; + } + /** * @return */ public boolean hasValues() { - return !this.values.isEmpty(); + return !this.values.isEmpty() || !this.flags.isEmpty(); } /** @@ -169,7 +194,10 @@ private T getValue(String key, T defaultValue) { */ @Override public int hashCode() { - return ObjectUtils.nullSafeHashCode(this.values); + + int hash = ObjectUtils.nullSafeHashCode(this.values); + hash += ObjectUtils.nullSafeHashCode(this.flags); + return hash; } /* @@ -188,6 +216,35 @@ public boolean equals(Object obj) { } Meta other = (Meta) obj; - return ObjectUtils.nullSafeEquals(this.values, other.values); + if (!ObjectUtils.nullSafeEquals(this.values, other.values)) { + return false; + } + return ObjectUtils.nullSafeEquals(this.flags, other.flags); + } + + /** + * {@link CursorOption} represents {@code OP_QUERY} wire protocol flags to change the behavior of queries. + * + * @author Christoph Strobl + * @since 1.10 + */ + public enum CursorOption { + + /** Prevents the server from timing out idle cursors. */ + NO_TIMEOUT, + + /** + * Sets the cursor to return all data returned by the query at once rather than splitting the results into batches. + */ + EXHAUST, + + /** Allows querying of a replica slave. */ + SLAVE_OK, + + /** + * Sets the cursor to return partial data from a query against a sharded cluster in which some shards do not respond + * rather than throwing an error. + */ + PARTIAL } } 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 b8bb5957bd..cd2668baee 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 @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 the original author or authors. + * Copyright 2010-2016 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. @@ -41,6 +41,7 @@ * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ public class Query { @@ -336,6 +337,50 @@ public Query useSnapshot() { return this; } + /** + * @return + * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#NO_TIMEOUT + * @since 1.10 + */ + public Query noCursorTimeout() { + + meta.addFlag(Meta.CursorOption.NO_TIMEOUT); + return this; + } + + /** + * @return + * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#EXHAUST + * @since 1.10 + */ + public Query exhaust() { + + meta.addFlag(Meta.CursorOption.EXHAUST); + return this; + } + + /** + * @return + * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#SLAVE_OK + * @since 1.10 + */ + public Query slaveOk() { + + meta.addFlag(Meta.CursorOption.SLAVE_OK); + return this; + } + + /** + * @return + * @see org.springframework.data.mongodb.core.query.Meta.CursorOption#PARTIAL + * @since 1.10 + */ + public Query partialResults() { + + meta.addFlag(Meta.CursorOption.PARTIAL); + return this; + } + /** * @return never {@literal null}. * @since 1.6 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 index 61890370bf..c953b7d3c9 100644 --- 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 @@ -75,4 +75,12 @@ */ boolean snapshot() default false; + /** + * Set {@link org.springframework.data.mongodb.core.query.Meta.CursorOption} to be used when executing query. + * + * @return never {@literal null}. + * @since 1.10 + */ + org.springframework.data.mongodb.core.query.Meta.CursorOption[] flags() default {}; + } 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 afbecf83d3..067928b095 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 @@ -37,6 +37,7 @@ import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -248,6 +249,13 @@ public org.springframework.data.mongodb.core.query.Meta getQueryMetaAttributes() metaAttributes.setSnapshot(meta.snapshot()); } + if (!ObjectUtils.isEmpty(meta.flags())) { + + for (org.springframework.data.mongodb.core.query.Meta.CursorOption option : meta.flags()) { + metaAttributes.addFlag(option); + } + } + return metaAttributes; } } 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 333cfd7a72..21ca3f8c03 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 @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2016 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. @@ -32,6 +32,7 @@ import org.springframework.data.mongodb.core.query.Meta; import org.springframework.data.mongodb.core.query.Query; +import com.mongodb.Bytes; import com.mongodb.DBCursor; /** @@ -39,6 +40,7 @@ * * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch */ @RunWith(MockitoJUnitRunner.class) public class QueryCursorPreparerUnitTests { @@ -61,7 +63,7 @@ public void appliesHintsCorrectly() { Query query = query(where("foo").is("bar")).withHint("hint"); - pepare(query); + prepare(query); verify(cursorToUse).hint("hint"); } @@ -75,7 +77,7 @@ public void doesNotApplyMetaWhenEmpty() { Query query = query(where("foo").is("bar")); query.setMeta(new Meta()); - pepare(query); + prepare(query); verify(cursor, never()).copy(); verify(cursorToUse, never()).addSpecial(any(String.class), anyObject()); @@ -89,7 +91,7 @@ public void appliesMaxScanCorrectly() { Query query = query(where("foo").is("bar")).maxScan(100); - pepare(query); + prepare(query); verify(cursorToUse).addSpecial(eq("$maxScan"), eq(100L)); } @@ -102,7 +104,7 @@ public void appliesMaxTimeCorrectly() { Query query = query(where("foo").is("bar")).maxTime(1, TimeUnit.SECONDS); - pepare(query); + prepare(query); verify(cursorToUse).addSpecial(eq("$maxTimeMS"), eq(1000L)); } @@ -115,7 +117,7 @@ public void appliesCommentCorrectly() { Query query = query(where("foo").is("bar")).comment("spring data"); - pepare(query); + prepare(query); verify(cursorToUse).addSpecial(eq("$comment"), eq("spring data")); } @@ -128,12 +130,25 @@ public void appliesSnapshotCorrectly() { Query query = query(where("foo").is("bar")).useSnapshot(); - pepare(query); + prepare(query); verify(cursorToUse).addSpecial(eq("$snapshot"), eq(true)); } - private DBCursor pepare(Query query) { + /** + * @see DATAMONGO-1480 + */ + @Test + public void appliesNoCursorTimeoutCorrectly() { + + Query query = query(where("foo").is("bar")).noCursorTimeout(); + + prepare(query); + + verify(cursorToUse).addOption(Bytes.QUERYOPTION_NOTIMEOUT); + } + + private DBCursor prepare(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 981428dfbf..7406e63341 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 @@ -347,7 +347,7 @@ private interface Repo extends MongoRepository { List findByFirstname(String firstname); - @Meta(comment = "comment") + @Meta(comment = "comment", flags = {org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT}) Page findByFirstname(String firstnanme, Pageable pageable); @Meta(comment = "comment") 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 efea28a23b..c45bc31eed 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 @@ -152,7 +152,6 @@ public void createsMongoQueryMethodWithMaxExecutionTimeCorrectly() throws Except assertThat(method.getQueryMetaAttributes().getMaxTimeMsec(), is(100L)); } - /** * @see DATAMONGO-1403 */ @@ -201,6 +200,19 @@ public void createsMongoQueryMethodWithSnapshotCorrectly() throws Exception { assertThat(method.getQueryMetaAttributes().getSnapshot(), is(true)); } + /** + * @see DATAMONGO-1480 + */ + @Test + public void createsMongoQueryMethodWithNoCursorTimeoutCorrectly() throws Exception { + + MongoQueryMethod method = queryMethod(PersonRepository.class, "metaWithNoCursorTimeout"); + + assertThat(method.hasQueryMetaAttributes(), is(true)); + assertThat(method.getQueryMetaAttributes().getFlags(), + containsInAnyOrder(org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT)); + } + /** * @see DATAMONGO-1266 */ @@ -250,6 +262,9 @@ interface PersonRepository extends Repository { @Meta(snapshot = true) List metaWithSnapshotUsage(); + @Meta(flags = { org.springframework.data.mongodb.core.query.Meta.CursorOption.NO_TIMEOUT }) + List metaWithNoCursorTimeout(); + /** * @see DATAMONGO-1266 */