diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/DurationStyle.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/DurationStyle.java new file mode 100644 index 000000000..c7f702691 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/DurationStyle.java @@ -0,0 +1,216 @@ +/* + * Copyright 2012-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.index; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * Duration format styles. + *

+ * Fork of {@code org.springframework.boot.convert.DurationStyle}. + * + * @author Phillip Webb + * @since 2.2 + */ +enum DurationStyle { + + /** + * Simple formatting, for example '1s'. + */ + SIMPLE("^([\\+\\-]?\\d+)([a-zA-Z]{0,2})$") { + + @Override + public Duration parse(String value, @Nullable ChronoUnit unit) { + try { + Matcher matcher = matcher(value); + Assert.state(matcher.matches(), "Does not match simple duration pattern"); + String suffix = matcher.group(2); + return (StringUtils.hasLength(suffix) ? Unit.fromSuffix(suffix) : Unit.fromChronoUnit(unit)) + .parse(matcher.group(1)); + } catch (Exception ex) { + throw new IllegalArgumentException("'" + value + "' is not a valid simple duration", ex); + } + } + }, + + /** + * ISO-8601 formatting. + */ + ISO8601("^[\\+\\-]?P.*$") { + + @Override + public Duration parse(String value, @Nullable ChronoUnit unit) { + try { + return Duration.parse(value); + } catch (Exception ex) { + throw new IllegalArgumentException("'" + value + "' is not a valid ISO-8601 duration", ex); + } + } + }; + + private final Pattern pattern; + + DurationStyle(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + protected final boolean matches(String value) { + return this.pattern.matcher(value).matches(); + } + + protected final Matcher matcher(String value) { + return this.pattern.matcher(value); + } + + /** + * Parse the given value to a duration. + * + * @param value the value to parse + * @return a duration + */ + public Duration parse(String value) { + return parse(value, null); + } + + /** + * Parse the given value to a duration. + * + * @param value the value to parse + * @param unit the duration unit to use if the value doesn't specify one ({@code null} will default to ms) + * @return a duration + */ + public abstract Duration parse(String value, @Nullable ChronoUnit unit); + + /** + * Detect the style then parse the value to return a duration. + * + * @param value the value to parse + * @return the parsed duration + * @throws IllegalStateException if the value is not a known style or cannot be parsed + */ + public static Duration detectAndParse(String value) { + return detectAndParse(value, null); + } + + /** + * Detect the style then parse the value to return a duration. + * + * @param value the value to parse + * @param unit the duration unit to use if the value doesn't specify one ({@code null} will default to ms) + * @return the parsed duration + * @throws IllegalStateException if the value is not a known style or cannot be parsed + */ + public static Duration detectAndParse(String value, @Nullable ChronoUnit unit) { + return detect(value).parse(value, unit); + } + + /** + * Detect the style from the given source value. + * + * @param value the source value + * @return the duration style + * @throws IllegalStateException if the value is not a known style + */ + public static DurationStyle detect(String value) { + Assert.notNull(value, "Value must not be null"); + for (DurationStyle candidate : values()) { + if (candidate.matches(value)) { + return candidate; + } + } + throw new IllegalArgumentException("'" + value + "' is not a valid duration"); + } + + /** + * Units that we support. + */ + enum Unit { + + /** + * Milliseconds. + */ + MILLIS(ChronoUnit.MILLIS, "ms", Duration::toMillis), + + /** + * Seconds. + */ + SECONDS(ChronoUnit.SECONDS, "s", Duration::getSeconds), + + /** + * Minutes. + */ + MINUTES(ChronoUnit.MINUTES, "m", Duration::toMinutes), + + /** + * Hours. + */ + HOURS(ChronoUnit.HOURS, "h", Duration::toHours), + + /** + * Days. + */ + DAYS(ChronoUnit.DAYS, "d", Duration::toDays); + + private final ChronoUnit chronoUnit; + + private final String suffix; + + private Function longValue; + + Unit(ChronoUnit chronoUnit, String suffix, Function toUnit) { + this.chronoUnit = chronoUnit; + this.suffix = suffix; + this.longValue = toUnit; + } + + public Duration parse(String value) { + return Duration.of(Long.valueOf(value), this.chronoUnit); + } + + public long longValue(Duration value) { + return this.longValue.apply(value); + } + + public static Unit fromChronoUnit(ChronoUnit chronoUnit) { + if (chronoUnit == null) { + return Unit.MILLIS; + } + for (Unit candidate : values()) { + if (candidate.chronoUnit == chronoUnit) { + return candidate; + } + } + throw new IllegalArgumentException("Unknown unit " + chronoUnit); + } + + public static Unit fromSuffix(String suffix) { + for (Unit candidate : values()) { + if (candidate.suffix.equalsIgnoreCase(suffix)) { + return candidate; + } + } + throw new IllegalArgumentException("Unknown unit '" + suffix + "'"); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java index 29b5756c1..5549260f6 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/Indexed.java @@ -30,6 +30,7 @@ import java.lang.annotation.Target; * @author Thomas Darimont * @author Christoph Strobl * @author Jordi Llach + * @author Mark Paluch */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @@ -136,27 +137,32 @@ public @interface Indexed { int expireAfterSeconds() default -1; /** - * Alternative for {@link #expireAfterSeconds()} to configure the timeout after which the collection should expire. - * Defaults to an empty String for no expiry. Accepts numeric values followed by their unit of measure (d(ays), - * h(ours), m(inutes), s(seconds)) or a Spring {@literal template expression}. The expression can result in a a valid - * expiration {@link String} following the conventions already mentioned or a {@link java.time.Duration}. + * Alternative for {@link #expireAfterSeconds()} to configure the timeout after which the document should expire. + * Defaults to an empty {@link String} for no expiry. Accepts numeric values followed by their unit of measure: + *

+ * Supports ISO-8601 style. * - *
-	 *     
+	 * 
 	 *
-	 * @Indexed(expireAfter = "10s")
-	 * String expireAfterTenSeconds;
+	 * @Indexed(expireAfter = "10s") String expireAfterTenSeconds;
 	 *
-	 * @Indexed(expireAfter = "1d")
-	 * String expireAfterOneDay;
+	 * @Indexed(expireAfter = "1d") String expireAfterOneDay;
 	 *
-	 * @Indexed(expireAfter = "#{@mySpringBean.timeout}")
-	 * String expireAfterTimeoutObtainedFromSpringBean;
-	 *     
+	 * @Indexed(expireAfter = "P2D") String expireAfterTwoDays;
+	 *
+	 * @Indexed(expireAfter = "#{@mySpringBean.timeout}") String expireAfterTimeoutObtainedFromSpringBean;
 	 * 
* - * @return {@literal 0s} by default. + * @return empty by default. * @since 2.2 */ - String expireAfter() default "0s"; + String expireAfter() default ""; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java index fdcdd5d2e..041241c0f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolver.java @@ -29,12 +29,11 @@ import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Sort; import org.springframework.data.mapping.Association; @@ -62,7 +61,6 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -81,7 +79,6 @@ import org.springframework.util.StringUtils; public class MongoPersistentEntityIndexResolver implements IndexResolver { private static final Logger LOGGER = LoggerFactory.getLogger(MongoPersistentEntityIndexResolver.class); - private static final Pattern TIMEOUT_PATTERN = Pattern.compile("(\\d+)(\\W+)?([dhms])"); private static final SpelExpressionParser PARSER = new SpelExpressionParser(); private final MongoMappingContext mappingContext; @@ -353,7 +350,6 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { return indexDefinitions; } - @SuppressWarnings("deprecation") protected IndexDefinitionHolder createCompoundIndexDefinition(String dotPath, String collection, CompoundIndex index, MongoPersistentEntity entity) { @@ -390,8 +386,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { return new org.bson.Document(dotPath, 1); } - Object keyDefToUse = evaluatePotentialTemplateExpression(keyDefinitionString, - getEvaluationContextForProperty(entity)); + Object keyDefToUse = evaluate(keyDefinitionString, getEvaluationContextForProperty(entity)); org.bson.Document dbo = (keyDefToUse instanceof org.bson.Document) ? (org.bson.Document) keyDefToUse : org.bson.Document.parse(ObjectUtils.nullSafeToString(keyDefToUse)); @@ -450,11 +445,11 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { indexDefinition.expire(index.expireAfterSeconds(), TimeUnit.SECONDS); } - if (!index.expireAfter().isEmpty() && !index.expireAfter().equals("0s")) { + if (StringUtils.hasText(index.expireAfter())) { if (index.expireAfterSeconds() >= 0) { throw new IllegalStateException(String.format( - "@Indexed already defines an expiration timeout of %s sec. via Indexed#expireAfterSeconds. Please make to use either expireAfterSeconds or expireAfter.", + "@Indexed already defines an expiration timeout of %s seconds via Indexed#expireAfterSeconds. Please make to use either expireAfterSeconds or expireAfter.", index.expireAfterSeconds())); } @@ -480,7 +475,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { /** * Get the {@link EvaluationContext} for a given {@link PersistentEntity entity} the default one. - * + * * @param persistentEntity can be {@literal null} * @return */ @@ -546,10 +541,15 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { private String pathAwareIndexName(String indexName, String dotPath, @Nullable PersistentEntity entity, @Nullable MongoPersistentProperty property) { - String nameToUse = StringUtils.hasText(indexName) - ? ObjectUtils - .nullSafeToString(evaluatePotentialTemplateExpression(indexName, getEvaluationContextForProperty(entity))) - : ""; + String nameToUse = ""; + if (StringUtils.hasText(indexName)) { + + Object result = evaluate(indexName, getEvaluationContextForProperty(entity)); + + if (result != null) { + nameToUse = ObjectUtils.nullSafeToString(result); + } + } if (!StringUtils.hasText(dotPath) || (property != null && dotPath.equals(property.getFieldName()))) { return StringUtils.hasText(nameToUse) ? nameToUse : dotPath; @@ -607,7 +607,7 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { */ private static Duration computeIndexTimeout(String timeoutValue, EvaluationContext evaluationContext) { - Object evaluatedTimeout = evaluatePotentialTemplateExpression(timeoutValue, evaluationContext); + Object evaluatedTimeout = evaluate(timeoutValue, evaluationContext); if (evaluatedTimeout == null) { return Duration.ZERO; @@ -623,30 +623,11 @@ public class MongoPersistentEntityIndexResolver implements IndexResolver { return Duration.ZERO; } - Matcher matcher = TIMEOUT_PATTERN.matcher(val); - if (matcher.find()) { - - Long timeout = NumberUtils.parseNumber(matcher.group(1), Long.class); - String unit = matcher.group(3); - - switch (unit) { - case "d": - return Duration.ofDays(timeout); - case "h": - return Duration.ofHours(timeout); - case "m": - return Duration.ofMinutes(timeout); - case "s": - return Duration.ofSeconds(timeout); - } - } - - throw new IllegalArgumentException( - String.format("Index timeout %s cannot be parsed. Please use the following pattern '\\d+\\W?[dhms]'.", val)); + return DurationStyle.detectAndParse(val); } @Nullable - private static Object evaluatePotentialTemplateExpression(String value, EvaluationContext evaluationContext) { + private static Object evaluate(String value, EvaluationContext evaluationContext) { Expression expression = PARSER.parseExpression(value, ParserContext.TEMPLATE_EXPRESSION); if (expression instanceof LiteralExpression) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java index 76fc212f9..e2c8fe522 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/IndexingIntegrationTests.java @@ -15,8 +15,7 @@ */ package org.springframework.data.mongodb.core.index; -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import static org.springframework.data.mongodb.test.util.Assertions.*; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -32,6 +31,7 @@ import java.util.Optional; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; @@ -46,7 +46,6 @@ import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Field; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; -import org.springframework.data.mongodb.test.util.Assertions; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -99,7 +98,7 @@ public class IndexingIntegrationTests { operations.getConverter().getMappingContext().getPersistentEntity(IndexedPerson.class); - assertThat(hasIndex("_firstname", IndexedPerson.class), is(true)); + assertThat(hasIndex("_firstname", IndexedPerson.class)).isTrue(); } @Test // DATAMONGO-2188 @@ -114,7 +113,7 @@ public class IndexingIntegrationTests { template.getConverter().getMappingContext().getPersistentEntity(IndexedPerson.class); - assertThat(hasIndex("_firstname", MongoCollectionUtils.getPreferredCollectionName(IndexedPerson.class)), is(false)); + assertThat(hasIndex("_firstname", MongoCollectionUtils.getPreferredCollectionName(IndexedPerson.class))).isFalse(); } @Test // DATAMONGO-1163 @@ -123,7 +122,7 @@ public class IndexingIntegrationTests { operations.getConverter().getMappingContext().getPersistentEntity(IndexedPerson.class); - assertThat(hasIndex("_lastname", IndexedPerson.class), is(true)); + assertThat(hasIndex("_lastname", IndexedPerson.class)).isTrue(); } @Test // DATAMONGO-2112 @@ -140,8 +139,8 @@ public class IndexingIntegrationTests { .findFirst(); }); - Assertions.assertThat(indexInfo).isPresent(); - Assertions.assertThat(indexInfo.get()).containsEntry("expireAfterSeconds", 11L); + assertThat(indexInfo).isPresent(); + assertThat(indexInfo.get()).containsEntry("expireAfterSeconds", 11L); } @Target({ ElementType.FIELD }) diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java index 1b35616c0..7bac84fe0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/index/MongoPersistentEntityIndexResolverUnitTests.java @@ -15,10 +15,8 @@ */ package org.springframework.data.mongodb.core.index; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import static org.springframework.data.mongodb.test.util.IsBsonObject.*; +import static org.springframework.data.mongodb.test.util.Assertions.*; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -27,11 +25,11 @@ import java.lang.annotation.Target; import java.util.Collections; import java.util.List; -import org.assertj.core.api.Assertions; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; + import org.springframework.core.annotation.AliasFor; import org.springframework.data.annotation.Id; import org.springframework.data.geo.Point; @@ -53,12 +51,15 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.util.ClassTypeInformation; /** + * Tests for {@link MongoPersistentEntityIndexResolver}. + * * @author Christoph Strobl * @author Mark Paluch */ @RunWith(Suite.class) @SuiteClasses({ IndexResolutionTests.class, GeoSpatialIndexResolutionTests.class, CompoundIndexResolutionTests.class, TextIndexedResolutionTests.class, MixedIndexResolutionTests.class }) +@SuppressWarnings("unused") public class MongoPersistentEntityIndexResolverUnitTests { /** @@ -75,7 +76,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( IndexOnLevelZero.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection("indexedProperty", "Zero", indexDefinitions.get(0)); } @@ -84,7 +85,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelOne.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection("zero.indexedProperty", "One", indexDefinitions.get(0)); } @@ -95,7 +96,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { IndexResolver indexResolver = IndexResolver.create(mappingContext); Iterable definitions = indexResolver.resolveIndexFor(IndexOnLevelOne.class); - assertThat(definitions.iterator().hasNext(), is(true)); + assertThat(definitions).isNotEmpty(); } @Test // DATAMONGO-899 @@ -103,7 +104,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType(IndexOnLevelTwo.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection("one.zero.indexedProperty", "Two", indexDefinitions.get(0)); } @@ -113,7 +114,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( IndexOnLevelOneWithExplicitlyNamedField.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection("customZero.customFieldName", "indexOnLevelOneWithExplicitlyNamedField", indexDefinitions.get(0)); } @@ -125,7 +126,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { IndexOnLevelZero.class); IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions(), equalTo(new org.bson.Document().append("name", "indexedProperty"))); + assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document("name", "indexedProperty")); } @Test // DATAMONGO-899 @@ -135,8 +136,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { WithOptionsOnIndexedProperty.class); IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions(), equalTo(new org.bson.Document().append("name", "indexedProperty") - .append("unique", true).append("sparse", true).append("background", true).append("expireAfterSeconds", 10L))); + assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document().append("name", "indexedProperty") + .append("unique", true).append("sparse", true).append("background", true).append("expireAfterSeconds", 10L)); } @Test // DATAMONGO-1297 @@ -144,9 +145,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType(WithDbRef.class); - assertThat(indexDefinitions, hasSize(1)); - assertThat(indexDefinitions.get(0).getCollection(), equalTo("withDbRef")); - assertThat(indexDefinitions.get(0).getIndexKeys(), equalTo(new org.bson.Document().append("indexedDbRef", 1))); + assertThat(indexDefinitions).hasSize(1); + assertThat(indexDefinitions.get(0).getCollection()).isEqualTo("withDbRef"); + assertThat(indexDefinitions.get(0).getIndexKeys()).isEqualTo(new org.bson.Document("indexedDbRef", 1)); } @Test // DATAMONGO-1297 @@ -155,10 +156,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( WrapperOfWithDbRef.class); - assertThat(indexDefinitions, hasSize(1)); - assertThat(indexDefinitions.get(0).getCollection(), equalTo("wrapperOfWithDbRef")); - assertThat(indexDefinitions.get(0).getIndexKeys(), - equalTo(new org.bson.Document().append("nested.indexedDbRef", 1))); + assertThat(indexDefinitions).hasSize(1); + assertThat(indexDefinitions.get(0).getCollection()).isEqualTo("wrapperOfWithDbRef"); + assertThat(indexDefinitions.get(0).getIndexKeys()).isEqualTo(new org.bson.Document("nested.indexedDbRef", 1)); } @Test // DATAMONGO-1163 @@ -167,9 +167,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( IndexOnMetaAnnotatedField.class); - assertThat(indexDefinitions, hasSize(1)); - assertThat(indexDefinitions.get(0).getCollection(), equalTo("indexOnMetaAnnotatedField")); - assertThat(indexDefinitions.get(0).getIndexOptions(), equalTo(new org.bson.Document().append("name", "_name"))); + assertThat(indexDefinitions).hasSize(1); + assertThat(indexDefinitions.get(0).getCollection()).isEqualTo("indexOnMetaAnnotatedField"); + assertThat(indexDefinitions.get(0).getIndexOptions()).isEqualTo(new org.bson.Document("name", "_name")); } @Test // DATAMONGO-1373 @@ -178,13 +178,15 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( IndexedDocumentWithComposedAnnotations.class); - assertThat(indexDefinitions, hasSize(2)); + assertThat(indexDefinitions).hasSize(2); IndexDefinitionHolder indexDefinitionHolder = indexDefinitions.get(1); - assertThat(indexDefinitionHolder.getIndexKeys(), isBsonObject().containing("fieldWithMyIndexName", 1)); - assertThat(indexDefinitionHolder.getIndexOptions(), - isBsonObject().containing("sparse", true).containing("unique", true).containing("name", "my_index_name")); + assertThat(indexDefinitionHolder.getIndexKeys()).containsEntry("fieldWithMyIndexName", 1); + assertThat(indexDefinitionHolder.getIndexOptions()) // + .containsEntry("sparse", true) // + .containsEntry("unique", true) // + .containsEntry("name", "my_index_name"); } @Test // DATAMONGO-1373 @@ -193,13 +195,15 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( IndexedDocumentWithComposedAnnotations.class); - assertThat(indexDefinitions, hasSize(2)); + assertThat(indexDefinitions).hasSize(2); IndexDefinitionHolder indexDefinitionHolder = indexDefinitions.get(0); - assertThat(indexDefinitionHolder.getIndexKeys(), isBsonObject().containing("fieldWithDifferentIndexName", 1)); - assertThat(indexDefinitionHolder.getIndexOptions(), - isBsonObject().containing("sparse", true).containing("name", "different_name").notContaining("unique")); + assertThat(indexDefinitionHolder.getIndexKeys()).containsEntry("fieldWithDifferentIndexName", 1); + assertThat(indexDefinitionHolder.getIndexOptions()) // + .containsEntry("sparse", true) // + .containsEntry("name", "different_name") // + .doesNotContainKey("unique"); } @Test // DATAMONGO-2112 @@ -208,7 +212,16 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( WithExpireAfterAsPlainString.class); - Assertions.assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 600L); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 600L); + } + + @Test // DATAMONGO-2112 + public void shouldResolveTimeoutFromIso8601String() { + + List indexDefinitions = prepareMappingContextAndResolveIndexForType( + WithIso8601Style.class); + + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 86400L); } @Test // DATAMONGO-2112 @@ -217,7 +230,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( WithExpireAfterAsExpression.class); - Assertions.assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 11L); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 11L); } @Test // DATAMONGO-2112 @@ -226,7 +239,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( WithExpireAfterAsExpressionResultingInDuration.class); - Assertions.assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 100L); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("expireAfterSeconds", 100L); } @Test // DATAMONGO-2112 @@ -235,9 +248,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { MongoMappingContext mappingContext = prepareMappingContext(WithInvalidExpireAfter.class); MongoPersistentEntityIndexResolver indexResolver = new MongoPersistentEntityIndexResolver(mappingContext); - Assertions.assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> indexResolver + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> indexResolver .resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(WithInvalidExpireAfter.class))); - } @Test // DATAMONGO-2112 @@ -246,7 +258,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { MongoMappingContext mappingContext = prepareMappingContext(WithDuplicateExpiry.class); MongoPersistentEntityIndexResolver indexResolver = new MongoPersistentEntityIndexResolver(mappingContext); - Assertions.assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> indexResolver + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> indexResolver .resolveIndexForEntity(mappingContext.getRequiredPersistentEntity(WithDuplicateExpiry.class))); } @@ -256,7 +268,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( WithIndexNameAsExpression.class); - Assertions.assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "my1st"); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "my1st"); } @Document("Zero") @@ -322,13 +334,13 @@ public class MongoPersistentEntityIndexResolverUnitTests { @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD }) @ComposedIndexedAnnotation(indexName = "different_name", beUnique = false) - static @interface CustomIndexedAnnotation { + @interface CustomIndexedAnnotation { } @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE }) @Indexed - static @interface ComposedIndexedAnnotation { + @interface ComposedIndexedAnnotation { @AliasFor(annotation = Indexed.class, attribute = "unique") boolean beUnique() default true; @@ -343,7 +355,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @org.springframework.data.mongodb.core.mapping.Field - static @interface ComposedFieldAnnotation { + @interface ComposedFieldAnnotation { @AliasFor(annotation = org.springframework.data.mongodb.core.mapping.Field.class, attribute = "value") String name() default "_id"; @@ -354,6 +366,11 @@ public class MongoPersistentEntityIndexResolverUnitTests { @Indexed(expireAfter = "10m") String withTimeout; } + @Document + class WithIso8601Style { + @Indexed(expireAfter = "P1D") String withTimeout; + } + @Document static class WithExpireAfterAsExpression { @Indexed(expireAfter = "#{10 + 1 + 's'}") String withTimeout; @@ -384,7 +401,6 @@ public class MongoPersistentEntityIndexResolverUnitTests { @Retention(RetentionPolicy.RUNTIME) @Indexed @interface IndexedFieldAnnotation { - } @Document @@ -405,7 +421,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( GeoSpatialIndexOnLevelZero.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection("geoIndexedProperty", "Zero", indexDefinitions.get(0)); } @@ -415,7 +431,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( GeoSpatialIndexOnLevelOne.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection("zero.geoIndexedProperty", "One", indexDefinitions.get(0)); } @@ -425,7 +441,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( GeoSpatialIndexOnLevelTwo.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection("one.zero.geoIndexedProperty", "Two", indexDefinitions.get(0)); } @@ -437,8 +453,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions(), equalTo( - new org.bson.Document().append("name", "location").append("min", 1).append("max", 100).append("bits", 2))); + assertThat(indexDefinition.getIndexOptions()).isEqualTo( + new org.bson.Document().append("name", "location").append("min", 1).append("max", 100).append("bits", 2)); } @Test // DATAMONGO-1373 @@ -449,10 +465,10 @@ public class MongoPersistentEntityIndexResolverUnitTests { IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexKeys(), - isBsonObject().containing("location", "geoHaystack").containing("What light?", 1)); - assertThat(indexDefinition.getIndexOptions(), - isBsonObject().containing("name", "my_geo_index_name").containing("bucketSize", 2.0)); + assertThat(indexDefinition.getIndexKeys()).containsEntry("location", "geoHaystack").containsEntry("What light?", + 1); + assertThat(indexDefinition.getIndexOptions()).containsEntry("name", "my_geo_index_name") + .containsEntry("bucketSize", 2.0); } @Test // DATAMONGO-2112 @@ -461,7 +477,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( GeoIndexWithNameAsExpression.class); - Assertions.assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "my1st"); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "my1st"); } @Document("Zero") @@ -531,7 +547,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( CompoundIndexOnLevelZero.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "foo", "bar" }, "CompoundIndexOnLevelZero", indexDefinitions.get(0)); } @@ -542,9 +558,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { CompoundIndexOnLevelZero.class); IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions(), equalTo(new org.bson.Document().append("name", "compound_index") - .append("unique", true).append("sparse", true).append("background", true))); - assertThat(indexDefinition.getIndexKeys(), equalTo(new org.bson.Document().append("foo", 1).append("bar", -1))); + assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document("name", "compound_index") + .append("unique", true).append("sparse", true).append("background", true)); + assertThat(indexDefinition.getIndexKeys()).isEqualTo(new org.bson.Document().append("foo", 1).append("bar", -1)); } @Test // DATAMONGO-909 @@ -554,9 +570,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { IndexDefinedOnSuperClass.class); IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions(), equalTo(new org.bson.Document().append("name", "compound_index") - .append("unique", true).append("sparse", true).append("background", true))); - assertThat(indexDefinition.getIndexKeys(), equalTo(new org.bson.Document().append("foo", 1).append("bar", -1))); + assertThat(indexDefinition.getIndexOptions()).isEqualTo(new org.bson.Document().append("name", "compound_index") + .append("unique", true).append("sparse", true).append("background", true)); + assertThat(indexDefinition.getIndexKeys()).isEqualTo(new org.bson.Document().append("foo", 1).append("bar", -1)); } @Test // DATAMONGO-827 @@ -566,9 +582,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { ComountIndexWithAutogeneratedName.class); IndexDefinition indexDefinition = indexDefinitions.get(0).getIndexDefinition(); - assertThat(indexDefinition.getIndexOptions(), - equalTo(new org.bson.Document().append("unique", true).append("sparse", true).append("background", true))); - assertThat(indexDefinition.getIndexKeys(), equalTo(new org.bson.Document().append("foo", 1).append("bar", -1))); + assertThat(indexDefinition.getIndexOptions()) + .isEqualTo(new org.bson.Document().append("unique", true).append("sparse", true).append("background", true)); + assertThat(indexDefinition.getIndexKeys()).isEqualTo(new org.bson.Document().append("foo", 1).append("bar", -1)); } @Test // DATAMONGO-929 @@ -577,7 +593,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( CompoundIndexOnLevelOne.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "zero.foo", "zero.bar" }, "CompoundIndexOnLevelOne", indexDefinitions.get(0)); } @@ -588,7 +604,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( CompoundIndexOnLevelOneWithEmptyIndexDefinition.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "zero" }, "CompoundIndexOnLevelZeroWithEmptyIndexDef", indexDefinitions.get(0)); } @@ -599,7 +615,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( SingleCompoundIndex.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "foo", "bar" }, "CompoundIndexOnLevelZero", indexDefinitions.get(0)); } @@ -609,10 +625,10 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( CompoundIndexDocumentWithComposedAnnotation.class); - assertThat(indexDefinitions, hasSize(1)); - assertThat(indexDefinitions.get(0).getIndexKeys(), isBsonObject().containing("foo", 1).containing("bar", -1)); - assertThat(indexDefinitions.get(0).getIndexOptions(), isBsonObject().containing("name", "my_compound_index_name") - .containing("unique", true).containing("background", true)); + assertThat(indexDefinitions).hasSize(1); + assertThat(indexDefinitions.get(0).getIndexKeys()).containsEntry("foo", 1).containsEntry("bar", -1); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "my_compound_index_name") + .containsEntry("unique", true).containsEntry("background", true); } @Test // DATAMONGO-2112 @@ -621,7 +637,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( CompoundIndexWithNameExpression.class); - Assertions.assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "cmp2name"); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "cmp2name"); } @Test // DATAMONGO-2112 @@ -630,7 +646,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( CompoundIndexWithDefExpression.class); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "foo", "bar" }, "compoundIndexWithDefExpression", indexDefinitions.get(0)); } @@ -660,22 +676,16 @@ public class MongoPersistentEntityIndexResolverUnitTests { unique = true) static class SingleCompoundIndex {} - static class IndexDefinedOnSuperClass extends CompoundIndexOnLevelZero { - - } + static class IndexDefinedOnSuperClass extends CompoundIndexOnLevelZero {} @Document("ComountIndexWithAutogeneratedName") @CompoundIndexes({ @CompoundIndex(useGeneratedName = true, def = "{'foo': 1, 'bar': -1}", background = true, sparse = true, unique = true) }) - static class ComountIndexWithAutogeneratedName { - - } + static class ComountIndexWithAutogeneratedName {} @Document("WithComposedAnnotation") @ComposedCompoundIndex - static class CompoundIndexDocumentWithComposedAnnotation { - - } + static class CompoundIndexDocumentWithComposedAnnotation {} @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE }) @@ -716,7 +726,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( TextIndexOnSinglePropertyInRoot.class); - assertThat(indexDefinitions.size(), equalTo(1)); + + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection("bar", "textIndexOnSinglePropertyInRoot", indexDefinitions.get(0)); } @@ -725,7 +736,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( TextIndexOnMutiplePropertiesInRoot.class); - assertThat(indexDefinitions.size(), equalTo(1)); + + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "foo", "bar" }, "textIndexOnMutiplePropertiesInRoot", indexDefinitions.get(0)); } @@ -735,7 +747,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( TextIndexOnNestedRoot.class); - assertThat(indexDefinitions.size(), equalTo(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "nested.foo" }, "textIndexOnNestedRoot", indexDefinitions.get(0)); } @@ -744,12 +756,12 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( TextIndexOnNestedWithWeightRoot.class); - assertThat(indexDefinitions.size(), equalTo(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "nested.foo" }, "textIndexOnNestedWithWeightRoot", indexDefinitions.get(0)); org.bson.Document weights = DocumentTestUtils.getAsDocument(indexDefinitions.get(0).getIndexOptions(), "weights"); - assertThat(weights.get("nested.foo"), is((Object) 5F)); + assertThat(weights.get("nested.foo")).isEqualTo(5F); } @Test // DATAMONGO-937 @@ -757,13 +769,13 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( TextIndexOnNestedWithMostSpecificValueRoot.class); - assertThat(indexDefinitions.size(), equalTo(1)); + assertThat(indexDefinitions).hasSize(1); assertIndexPathAndCollection(new String[] { "nested.foo", "nested.bar" }, "textIndexOnNestedWithMostSpecificValueRoot", indexDefinitions.get(0)); org.bson.Document weights = DocumentTestUtils.getAsDocument(indexDefinitions.get(0).getIndexOptions(), "weights"); - assertThat(weights.get("nested.foo"), is((Object) 5F)); - assertThat(weights.get("nested.bar"), is((Object) 10F)); + assertThat(weights.get("nested.foo")).isEqualTo(5F); + assertThat(weights.get("nested.bar")).isEqualTo(10F); } @Test // DATAMONGO-937 @@ -771,7 +783,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithDefaultLanguage.class); - assertThat(indexDefinitions.get(0).getIndexOptions().get("default_language"), is((Object) "spanish")); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("default_language", "spanish"); } @Test // DATAMONGO-937, DATAMONGO-1049 @@ -779,7 +791,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithLanguageOverride.class); - assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is((Object) "lang")); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("language_override", "lang"); } @Test // DATAMONGO-1049 @@ -787,7 +799,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithLanguageOverrideOnNestedElement.class); - assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is(nullValue())); + assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override")).isNull(); } @Test // DATAMONGO-1049 @@ -795,7 +807,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithNoTextIndexPropertyButReservedFieldLanguage.class); - assertThat(indexDefinitions, is(empty())); + + assertThat(indexDefinitions).isEmpty(); } @Test // DATAMONGO-1049 @@ -803,7 +816,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithNoTextIndexPropertyButReservedFieldLanguageAnnotated.class); - assertThat(indexDefinitions, is(empty())); + + assertThat(indexDefinitions).isEmpty(); } @Test // DATAMONGO-1049 @@ -811,7 +825,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithOverlappingLanguageProps.class); - assertThat(indexDefinitions.get(0).getIndexOptions().get("language_override"), is((Object) "lang")); + + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("language_override", "lang"); } @Test // DATAMONGO-1373 @@ -821,7 +836,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { TextIndexedDocumentWithComposedAnnotation.class); org.bson.Document weights = DocumentTestUtils.getAsDocument(indexDefinitions.get(0).getIndexOptions(), "weights"); - assertThat(weights, isBsonObject().containing("foo", 99f)); + assertThat(weights).containsEntry("foo", 99f); } @Document @@ -919,7 +934,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE }) @TextIndexed - static @interface ComposedTextIndexedAnnotation { + @interface ComposedTextIndexedAnnotation { @AliasFor(annotation = TextIndexed.class, attribute = "weight") float heavyweight() default 99f; @@ -933,25 +948,27 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType(MixedIndexRoot.class); - assertThat(indexDefinitions, hasSize(2)); - assertThat(indexDefinitions.get(0).getIndexDefinition(), instanceOf(Index.class)); - assertThat(indexDefinitions.get(1).getIndexDefinition(), instanceOf(GeospatialIndex.class)); + assertThat(indexDefinitions).hasSize(2); + assertThat(indexDefinitions.get(0).getIndexDefinition()).isInstanceOf(Index.class); + assertThat(indexDefinitions.get(1).getIndexDefinition()).isInstanceOf(GeospatialIndex.class); } @Test // DATAMONGO-899 public void cyclicPropertyReferenceOverDBRefShouldNotBeTraversed() { List indexDefinitions = prepareMappingContextAndResolveIndexForType(Inner.class); - assertThat(indexDefinitions, hasSize(1)); - assertThat(indexDefinitions.get(0).getIndexDefinition().getIndexKeys(), - equalTo(new org.bson.Document().append("outer", 1))); + + assertThat(indexDefinitions).hasSize(1); + assertThat(indexDefinitions.get(0).getIndexDefinition().getIndexKeys()) + .isEqualTo(new org.bson.Document().append("outer", 1)); } @Test // DATAMONGO-899 public void associationsShouldNotBeTraversed() { List indexDefinitions = prepareMappingContextAndResolveIndexForType(Outer.class); - assertThat(indexDefinitions, empty()); + + assertThat(indexDefinitions).isEmpty(); } @Test // DATAMONGO-926 @@ -959,7 +976,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( CycleStartingInBetween.class); - assertThat(indexDefinitions, hasSize(1)); + + assertThat(indexDefinitions).hasSize(1); } @Test // DATAMONGO-926 @@ -968,7 +986,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType(CycleLevelZero.class); assertIndexPathAndCollection("indexedProperty", "cycleLevelZero", indexDefinitions.get(0)); assertIndexPathAndCollection("cyclicReference.indexedProperty", "cycleLevelZero", indexDefinitions.get(1)); - assertThat(indexDefinitions, hasSize(2)); + assertThat(indexDefinitions).hasSize(2); } @Test // DATAMONGO-926 @@ -976,7 +994,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType(CycleOnLevelOne.class); assertIndexPathAndCollection("reference.indexedProperty", "cycleOnLevelOne", indexDefinitions.get(0)); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); } @Test // DATAMONGO-926 @@ -984,11 +1002,12 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( NoCycleButIdenticallyNamedProperties.class); + + assertThat(indexDefinitions).hasSize(3); assertIndexPathAndCollection("foo", "noCycleButIdenticallyNamedProperties", indexDefinitions.get(0)); assertIndexPathAndCollection("reference.foo", "noCycleButIdenticallyNamedProperties", indexDefinitions.get(1)); assertIndexPathAndCollection("reference.deep.foo", "noCycleButIdenticallyNamedProperties", indexDefinitions.get(2)); - assertThat(indexDefinitions, hasSize(3)); } @Test // DATAMONGO-949 @@ -997,7 +1016,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( SimilarityHolingBean.class); assertIndexPathAndCollection("norm", "similarityHolingBean", indexDefinitions.get(0)); - assertThat(indexDefinitions, hasSize(1)); + assertThat(indexDefinitions).hasSize(1); } @Test // DATAMONGO-962 @@ -1005,7 +1024,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( SelfCyclingViaCollectionType.class); - assertThat(indexDefinitions, empty()); + + assertThat(indexDefinitions).isEmpty(); } @Test // DATAMONGO-962 @@ -1013,14 +1033,15 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( MultipleObjectsOfSameType.class); - assertThat(indexDefinitions, empty()); + + assertThat(indexDefinitions).isEmpty(); } @Test // DATAMONGO-962 @SuppressWarnings({ "rawtypes", "unchecked" }) public void shouldCatchCyclicReferenceExceptionOnRoot() { - MongoPersistentEntity entity = new BasicMongoPersistentEntity(ClassTypeInformation.from(Object.class)); + MongoPersistentEntity entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Object.class)); MongoPersistentProperty propertyMock = mock(MongoPersistentProperty.class); when(propertyMock.isEntity()).thenReturn(true); @@ -1028,7 +1049,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { when(propertyMock.getActualType()).thenThrow( new MongoPersistentEntityIndexResolver.CyclicPropertyReferenceException("foo", Object.class, "bar")); - MongoPersistentEntity selfCyclingEntity = new BasicMongoPersistentEntity( + MongoPersistentEntity selfCyclingEntity = new BasicMongoPersistentEntity<>( ClassTypeInformation.from(SelfCyclingViaCollectionType.class)); new MongoPersistentEntityIndexResolver(prepareMappingContext(SelfCyclingViaCollectionType.class)) @@ -1041,9 +1062,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( NoCycleManyPathsToDeepValueObject.class); + assertThat(indexDefinitions).hasSize(2); assertIndexPathAndCollection("l3.valueObject.value", "rules", indexDefinitions.get(0)); assertIndexPathAndCollection("l2.l3.valueObject.value", "rules", indexDefinitions.get(1)); - assertThat(indexDefinitions, hasSize(2)); } @Test // DATAMONGO-1025 @@ -1051,8 +1072,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithNestedDocumentHavingNamedCompoundIndex.class); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), - equalTo("propertyOfTypeHavingNamedCompoundIndex.c_index")); + + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", + "propertyOfTypeHavingNamedCompoundIndex.c_index"); } @Test // DATAMONGO-1025 @@ -1060,8 +1082,8 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithNestedTypeHavingNamedCompoundIndex.class); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), - equalTo("propertyOfTypeHavingNamedCompoundIndex.c_index")); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", + "propertyOfTypeHavingNamedCompoundIndex.c_index"); } @Test // DATAMONGO-1025 @@ -1069,8 +1091,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithNestedDocumentHavingNamedIndex.class); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), - equalTo("propertyOfTypeHavingNamedIndex.property_index")); + + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", + "propertyOfTypeHavingNamedIndex.property_index"); } @Test // DATAMONGO-1025 @@ -1078,8 +1101,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithNestedTypeHavingNamedIndex.class); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), - equalTo("propertyOfTypeHavingNamedIndex.property_index")); + + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", + "propertyOfTypeHavingNamedIndex.property_index"); } @Test // DATAMONGO-1025 @@ -1087,7 +1111,7 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( DocumentWithNamedIndex.class); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("property_index")); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "property_index"); } @Test // DATAMONGO-1087 @@ -1096,9 +1120,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( MultiplePropertiesOfSameTypeWithMatchingStartLetters.class); - assertThat(indexDefinitions, hasSize(2)); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("name.component")); - assertThat((String) indexDefinitions.get(1).getIndexOptions().get("name"), equalTo("nameLast.component")); + assertThat(indexDefinitions).hasSize(2); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "name.component"); + assertThat(indexDefinitions.get(1).getIndexOptions()).containsEntry("name", "nameLast.component"); } @Test // DATAMONGO-1087 @@ -1107,9 +1131,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( MultiplePropertiesOfSameTypeWithMatchingStartLettersOnNestedProperty.class); - assertThat(indexDefinitions, hasSize(2)); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("component.nameLast")); - assertThat((String) indexDefinitions.get(1).getIndexOptions().get("name"), equalTo("component.name")); + assertThat(indexDefinitions).hasSize(2); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "component.nameLast"); + assertThat(indexDefinitions.get(1).getIndexOptions()).containsEntry("name", "component.name"); } @Test // DATAMONGO-1121 @@ -1118,11 +1142,10 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( OuterDocumentReferingToIndexedPropertyViaDifferentNonCyclingPaths.class); - assertThat(indexDefinitions, hasSize(2)); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), equalTo("path1.foo")); - assertThat((String) indexDefinitions.get(1).getIndexOptions().get("name"), - equalTo("path2.propertyWithIndexedStructure.foo")); - + assertThat(indexDefinitions).hasSize(2); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", "path1.foo"); + assertThat(indexDefinitions.get(1).getIndexOptions()).containsEntry("name", + "path2.propertyWithIndexedStructure.foo"); } @Test // DATAMONGO-1263 @@ -1131,9 +1154,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { List indexDefinitions = prepareMappingContextAndResolveIndexForType( EntityWithGenericTypeWrapperAsElement.class); - assertThat(indexDefinitions, hasSize(1)); - assertThat((String) indexDefinitions.get(0).getIndexOptions().get("name"), - equalTo("listWithGeneircTypeElement.entity.property_index")); + assertThat(indexDefinitions).hasSize(1); + assertThat(indexDefinitions.get(0).getIndexOptions()).containsEntry("name", + "listWithGeneircTypeElement.entity.property_index"); } @Document @@ -1365,9 +1388,9 @@ public class MongoPersistentEntityIndexResolverUnitTests { IndexDefinitionHolder holder) { for (String expectedPath : expectedPaths) { - assertThat(holder.getIndexDefinition().getIndexKeys().containsKey(expectedPath), equalTo(true)); + assertThat(holder.getIndexDefinition().getIndexKeys()).containsKey(expectedPath); } - assertThat(holder.getCollection(), equalTo(expectedCollection)); + assertThat(holder.getCollection()).isEqualTo(expectedCollection); } } diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index 56d296206..a7098caa8 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -14,6 +14,7 @@ * Kotlin extension methods accepting `KClass` are deprecated now in favor of `reified` methods. * Support of array filters in `Update` operations. * <> from domain types. +* SpEL support in for expressions in `@Indexed`. [[new-features.2-1-0]] == What's New in Spring Data MongoDB 2.1