diff --git a/pom.xml b/pom.xml index cb4e66d37e..95b902a010 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1687-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index 4a49168713..4c42d258e1 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 - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1687-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1687-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 750ed23aa8..0e111d68d5 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 - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1687-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml index 50d0a6454a..bad2fab3bc 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 - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1687-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 886bcca7b6..212f0fb6fc 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 2.0.0.BUILD-SNAPSHOT + 2.0.0.DATAMONGO-1687-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java index 469fc1c947..0d6b2ac39f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/CollectionOptions.java @@ -28,10 +28,10 @@ */ public class CollectionOptions { - private Integer maxDocuments; - private Integer size; + private Long maxDocuments; + private Long size; private Boolean capped; - private Optional collation; + private Collation collation; /** * Constructs a new CollectionOptions instance. @@ -40,12 +40,14 @@ public class CollectionOptions { * @param maxDocuments the maximum number of documents in the collection. * @param capped true to created a "capped" collection (fixed size with auto-FIFO behavior based on insertion order), * false otherwise. + * @deprecated since 2.0 please use {@link CollectionOptions#empty()} as entry point. */ - public CollectionOptions(Integer size, Integer maxDocuments, Boolean capped) { - this(size, maxDocuments, capped, Optional.empty()); + @Deprecated + public CollectionOptions(Long size, Long maxDocuments, Boolean capped) { + this(size, maxDocuments, capped, null); } - private CollectionOptions(Integer size, Integer maxDocuments, Boolean capped, Optional collation) { + private CollectionOptions(Long size, Long maxDocuments, Boolean capped, Collation collation) { this.maxDocuments = maxDocuments; this.size = size; @@ -53,8 +55,6 @@ private CollectionOptions(Integer size, Integer maxDocuments, Boolean capped, Op this.collation = collation; } - private CollectionOptions() {} - /** * Create new {@link CollectionOptions} by just providing the {@link Collation} to use. * @@ -66,9 +66,7 @@ public static CollectionOptions just(Collation collation) { Assert.notNull(collation, "Collation must not be null!"); - CollectionOptions options = new CollectionOptions(); - options.setCollation(collation); - return options; + return new CollectionOptions(null, null, null, collation); } /** @@ -78,17 +76,17 @@ public static CollectionOptions just(Collation collation) { * @since 2.0 */ public static CollectionOptions empty() { - return new CollectionOptions(); + return new CollectionOptions(null, null, null, null); } /** - * Create new {@link CollectionOptions} with already given settings and capped set to {@literal true}. + * Create new {@link CollectionOptions} with already given settings and capped set to {@literal true}.
+ * NOTE Using capped collections requires defining {@link #size(int)}. * - * @param size the collection size in bytes, this data space is preallocated. * @return new {@link CollectionOptions}. * @since 2.0 */ - public CollectionOptions capped(int size) { + public CollectionOptions capped() { return new CollectionOptions(size, maxDocuments, true, collation); } @@ -99,7 +97,7 @@ public CollectionOptions capped(int size) { * @return new {@link CollectionOptions}. * @since 2.0 */ - public CollectionOptions maxDocuments(Integer maxDocuments) { + public CollectionOptions maxDocuments(long maxDocuments) { return new CollectionOptions(size, maxDocuments, capped, collation); } @@ -110,7 +108,7 @@ public CollectionOptions maxDocuments(Integer maxDocuments) { * @return new {@link CollectionOptions}. * @since 2.0 */ - public CollectionOptions size(int size) { + public CollectionOptions size(long size) { return new CollectionOptions(size, maxDocuments, capped, collation); } @@ -122,50 +120,44 @@ public CollectionOptions size(int size) { * @since 2.0 */ public CollectionOptions collation(Collation collation) { - return new CollectionOptions(size, maxDocuments, capped, Optional.ofNullable(collation)); - } - - public Integer getMaxDocuments() { - return maxDocuments; - } - - public void setMaxDocuments(Integer maxDocuments) { - this.maxDocuments = maxDocuments; - } - - public Integer getSize() { - return size; - } - - public void setSize(Integer size) { - this.size = size; + return new CollectionOptions(size, maxDocuments, capped, collation); } - public Boolean getCapped() { - return capped; + /** + * Get the max number of documents the collection should be limited to. + * + * @return {@link Optional#empty()} if not set. + */ + public Optional getMaxDocuments() { + return Optional.ofNullable(maxDocuments); } - public void setCapped(Boolean capped) { - this.capped = capped; + /** + * Get the {@literal size} in bytes the collection should be limited to. + * + * @return {@link Optional#empty()} if not set. + */ + public Optional getSize() { + return Optional.ofNullable(size); } /** - * Set {@link Collation} options. + * Get if the collection should be capped. * - * @param collation + * @return {@link Optional#empty()} if not set. * @since 2.0 */ - public void setCollation(Collation collation) { - this.collation = Optional.ofNullable(collation); + public Optional getCapped() { + return Optional.ofNullable(capped); } /** * Get the {@link Collation} settings. * - * @return + * @return {@link Optional#empty()} if not set. * @since 2.0 */ public Optional getCollation() { - return collation; + return Optional.ofNullable(collation); } } 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 7b418aafaf..ade633bf5f 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 @@ -20,8 +20,19 @@ import static org.springframework.data.util.Optionals.*; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; +import java.util.Scanner; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.bson.Document; @@ -1951,16 +1962,10 @@ protected Document convertToDocument(CollectionOptions collectionOptions) { Document document = new Document(); if (collectionOptions != null) { - if (collectionOptions.getCapped() != null) { - document.put("capped", collectionOptions.getCapped().booleanValue()); - } - if (collectionOptions.getSize() != null) { - document.put("size", collectionOptions.getSize().intValue()); - } - if (collectionOptions.getMaxDocuments() != null) { - document.put("max", collectionOptions.getMaxDocuments().intValue()); - } + collectionOptions.getCapped().ifPresent(val -> document.put("capped", val)); + collectionOptions.getSize().ifPresent(val -> document.put("size", val)); + collectionOptions.getMaxDocuments().ifPresent(val -> document.put("max", val)); collectionOptions.getCollation().ifPresent(val -> document.append("collation", val.toDocument())); } return document; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index c658e428c0..0f98dc5928 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -1626,17 +1626,10 @@ protected CreateCollectionOptions convertToCreateCollectionOptions(CollectionOpt CreateCollectionOptions result = new CreateCollectionOptions(); if (collectionOptions != null) { - if (collectionOptions.getCapped() != null) { - result = result.capped(collectionOptions.getCapped()); - } - - if (collectionOptions.getSize() != null) { - result = result.sizeInBytes(collectionOptions.getSize()); - } - - if (collectionOptions.getMaxDocuments() != null) { - result = result.maxDocuments(collectionOptions.getMaxDocuments()); - } + collectionOptions.getCapped().ifPresent(result::capped); + collectionOptions.getSize().ifPresent(result::sizeInBytes); + collectionOptions.getMaxDocuments().ifPresent(result::maxDocuments); + collectionOptions.getCollation().map(Collation::toMongoCollation).ifPresent(result::collation); } return result; } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java index ef71988a96..97529ac2fa 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoOperationsUnitTests.java @@ -46,6 +46,7 @@ * * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ @RunWith(MockitoJUnitRunner.class) public abstract class MongoOperationsUnitTests { @@ -137,7 +138,7 @@ public void convertsExceptionForCreateCollection2() { new Execution() { @Override public void doWith(MongoOperations operations) { - operations.createCollection("foo", new CollectionOptions(1, 1, true)); + operations.createCollection("foo", CollectionOptions.empty().size(1).maxDocuments(1).capped()); } }.assertDataAccessException(); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java index 3d208270a4..5cb6c43df2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java @@ -114,8 +114,6 @@ public class MongoTemplateTests { private static final org.springframework.data.util.Version TWO_DOT_FOUR = org.springframework.data.util.Version .parse("2.4"); - private static final org.springframework.data.util.Version TWO_DOT_EIGHT = org.springframework.data.util.Version - .parse("2.8"); private static final org.springframework.data.util.Version THREE_DOT_FOUR = org.springframework.data.util.Version .parse("3.4"); @@ -333,6 +331,26 @@ public void rejectsDuplicateIdInInsertAll() { template.insertAll(records); } + @Test // DATAMONGO-1687 + public void createCappedCollection() { + + template.createCollection(Person.class, CollectionOptions.empty().capped().size(1000).maxDocuments(1000)); + + org.bson.Document collectionOptions = getCollectionInfo(template.getCollectionName(Person.class)).get("options", + org.bson.Document.class); + assertThat(collectionOptions.get("capped"), is(true)); + } + + private org.bson.Document getCollectionInfo(String collectionName) { + + return template.execute(db -> { + + org.bson.Document result = db.runCommand(new org.bson.Document().append("listCollections", 1).append("filter", + new org.bson.Document("name", collectionName))); + return (org.bson.Document) result.get("cursor", org.bson.Document.class).get("firstBatch", List.class).get(0); + }); + } + @Test @SuppressWarnings("deprecation") public void testEnsureIndex() throws Exception { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateCollationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateCollationTests.java new file mode 100644 index 0000000000..5a737e343a --- /dev/null +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateCollationTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2017 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; + +import static org.assertj.core.api.Assertions.*; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.util.List; + +import org.bson.Document; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.mongodb.config.AbstractReactiveMongoConfiguration; +import org.springframework.data.mongodb.test.util.MongoVersionRule; +import org.springframework.data.util.Version; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.mongodb.reactivestreams.client.MongoClients; + +/** + * @author Mark Paluch + */ +@RunWith(SpringJUnit4ClassRunner.class) +public class ReactiveMongoTemplateCollationTests { + + public static @ClassRule MongoVersionRule REQUIRES_AT_LEAST_3_4_0 = MongoVersionRule.atLeast(Version.parse("3.4.0")); + public static final String COLLECTION_NAME = "collation-1"; + + @Configuration + static class Config extends AbstractReactiveMongoConfiguration { + + @Override + public com.mongodb.reactivestreams.client.MongoClient mongoClient() { + return MongoClients.create(); + } + + @Override + protected String getDatabaseName() { + return "collation-tests"; + } + } + + @Autowired ReactiveMongoTemplate template; + + @Before + public void setUp() { + StepVerifier.create(template.dropCollection(COLLECTION_NAME)).verifyComplete(); + } + + @Test // DATAMONGO-1693 + public void createCollectionWithCollation() { + + StepVerifier.create(template.createCollection(COLLECTION_NAME, CollectionOptions.just(Collation.of("en_US")))) // + .expectNextCount(1) // + .verifyComplete(); + + Mono collation = getCollationInfo(COLLECTION_NAME); + StepVerifier.create(collation) // + .consumeNextWith(document -> assertThat(document.get("locale")).isEqualTo("en_US")) // + .verifyComplete(); + + } + + private Mono getCollationInfo(String collectionName) { + + return getCollectionInfo(collectionName) // + .map(it -> it.get("options", Document.class)) // + .map(it -> it.get("collation", Document.class)); + } + + @SuppressWarnings("unchecked") + private Mono getCollectionInfo(String collectionName) { + + return template.execute(db -> { + + return Flux + .from(db.runCommand(new Document() // + .append("listCollections", 1) // + .append("filter", new Document("name", collectionName)))) // + .map(it -> it.get("cursor", Document.class)) + .flatMapIterable(it -> (List) it.get("firstBatch", List.class)); + }).next(); + } + +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java index 9a6e0d06bf..0ae2b6cf0a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveMongoTemplateTests.java @@ -803,7 +803,7 @@ public void tailStreamsData() throws InterruptedException { StepVerifier.create(template.dropCollection("capped") .then(template.createCollection("capped", // - new CollectionOptions(1000, 10, true))) + CollectionOptions.empty().size(1000).maxDocuments(10).capped())) .then(template.insert(new Document("random", Math.random()).append("key", "value"), // "capped"))) .expectNextCount(1).verifyComplete(); @@ -825,7 +825,7 @@ public void tailStreamsDataUntilCancellation() throws InterruptedException { StepVerifier.create(template.dropCollection("capped") .then(template.createCollection("capped", // - new CollectionOptions(1000, 10, true))) + CollectionOptions.empty().size(1000).maxDocuments(10).capped())) .then(template.insert(new Document("random", Math.random()).append("key", "value"), // "capped"))) .expectNextCount(1).verifyComplete(); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java index f732810e35..caecbda015 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/ReactiveMongoRepositoryTests.java @@ -63,6 +63,7 @@ * Test for {@link ReactiveMongoRepository} query methods. * * @author Mark Paluch + * @author Christoph Strobl */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:reactive-infrastructure.xml") @@ -175,7 +176,7 @@ public void shouldUseTailableCursor() throws Exception { StepVerifier .create(template.dropCollection(Capped.class) // .then(template.createCollection(Capped.class, // - new CollectionOptions(1000, 100, true)))) // + CollectionOptions.empty().size(1000).maxDocuments(100).capped()))) // .expectNextCount(1) // .verifyComplete(); @@ -200,7 +201,7 @@ public void shouldUseTailableCursorWithProjection() throws Exception { StepVerifier .create(template.dropCollection(Capped.class) // .then(template.createCollection(Capped.class, // - new CollectionOptions(1000, 100, true)))) // + CollectionOptions.empty().size(1000).maxDocuments(100).capped()))) // .expectNextCount(1) // .verifyComplete();