Compare commits

..

14 Commits

Author SHA1 Message Date
Christoph Strobl
b6c732718b Release version 3.4.12 (2021.2.12).
See #4366
2023-05-12 10:18:02 +02:00
Christoph Strobl
2bf17d3999 Prepare 3.4.12 (2021.2.12).
See #4366
2023-05-12 10:17:43 +02:00
Mark Paluch
7778077a9b Polishing.
Introduce has…() and getRequired…() methods for comment and max time limit to remove code duplications.

See #4374
Original pull request: #4378
2023-05-11 10:24:24 +02:00
Christoph Strobl
ec55e28cb3 Fix missing query options when calling MongoOperations#count.
This commit makes sure to forward maxTimeMsec and comment options from the query to the CountOptions.

Closes: #4374
Original pull request: #4378
2023-05-11 10:15:32 +02:00
Christoph Strobl
b9af92059d Fix regression in value to String mapping.
Previous versions allow arbitrary values to be mapped to an string property by calling the ObjectToString converter. This behaviour got lost and is not reestablished.

Closes #4371
Original pull request #4373
2023-05-10 14:54:51 +02:00
Greg L. Turnquist
fb43e85ea5 After release cleanups.
See #4335
2023-04-14 09:26:03 -05:00
Greg L. Turnquist
4fb7ada2cc Prepare next development iteration.
See #4335
2023-04-14 09:25:57 -05:00
Greg L. Turnquist
86d6ba9cc9 Release version 3.4.11 (2021.2.11).
See #4335
2023-04-14 09:19:55 -05:00
Greg L. Turnquist
ec8db3db95 Prepare 3.4.11 (2021.2.11).
See #4335
2023-04-14 09:19:14 -05:00
Mark Paluch
4af3ba458c Upgrade to Maven Wrapper 3.9.1.
See #4358
2023-04-06 16:18:30 +02:00
Mark Paluch
5f5b059ca0 Adopt to Mockito 5.1 changes.
Closes #4290
2023-03-27 12:26:52 +02:00
Christoph Strobl
c8c3136e89 Update visibility of ConversionContext.
The ConversionContext should not be package private due to its usage in protected method signatures.

Closes: #4345
2023-03-24 13:45:50 +01:00
Christoph Strobl
87a5182c33 After release cleanups.
See #4313
2023-03-20 13:48:10 +01:00
Christoph Strobl
90ca12856f Prepare next development iteration.
See #4313
2023-03-20 13:48:08 +01:00
17 changed files with 172 additions and 58 deletions

View File

@@ -1,2 +1,2 @@
#Mon Feb 20 12:01:58 CET 2023
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.0/apache-maven-3.9.0-bin.zip
#Thu Apr 06 16:18:30 CEST 2023
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.4.10</version>
<version>3.4.12</version>
<packaging>pom</packaging>
<name>Spring Data MongoDB</name>
@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.7.10</version>
<version>2.7.12</version>
</parent>
<modules>
@@ -26,7 +26,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.7.10</springdata.commons>
<springdata.commons>2.7.12</springdata.commons>
<mongo>4.6.1</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.4.10</version>
<version>3.4.12</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -14,7 +14,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.4.10</version>
<version>3.4.12</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -11,7 +11,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.4.10</version>
<version>3.4.12</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -1918,7 +1918,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
if (query.getLimit() > 0 && mapReduceOptions != null && mapReduceOptions.getLimit() == null) {
mapReduce = mapReduce.limit(query.getLimit());
}
if (query.getMeta().getMaxTimeMsec() != null) {
if (query.getMeta().hasMaxTime()) {
mapReduce = mapReduce.maxTime(query.getMeta().getMaxTimeMsec(), TimeUnit.MILLISECONDS);
}
@@ -3411,12 +3411,12 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
if (meta.hasValues()) {
if (StringUtils.hasText(meta.getComment())) {
cursorToUse = cursorToUse.comment(meta.getComment());
if (meta.hasComment()) {
cursorToUse = cursorToUse.comment(meta.getRequiredComment());
}
if (meta.getMaxTimeMsec() != null) {
cursorToUse = cursorToUse.maxTime(meta.getMaxTimeMsec(), TimeUnit.MILLISECONDS);
if (meta.hasMaxTime()) {
cursorToUse = cursorToUse.maxTime(meta.getRequiredMaxTimeMsec(), TimeUnit.MILLISECONDS);
}
if (meta.getCursorBatchSize() != null) {

View File

@@ -21,6 +21,7 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -52,6 +53,7 @@ import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.ShardKey;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Collation;
import org.springframework.data.mongodb.core.query.Meta;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.UpdateDefinition;
import org.springframework.data.mongodb.core.query.UpdateDefinition.ArrayFilter;
@@ -388,7 +390,7 @@ class QueryOperations {
for (Entry<String, Object> entry : fields.entrySet()) {
if (entry.getValue() instanceof MongoExpression) {
if (entry.getValue()instanceof MongoExpression) {
AggregationOperationContext ctx = entity == null ? Aggregation.DEFAULT_CONTEXT
: new RelaxedTypeBasedAggregationOperationContext(entity.getType(), mappingContext, queryMapper);
@@ -564,9 +566,23 @@ class QueryOperations {
if (query.getLimit() > 0) {
options.limit(query.getLimit());
}
if (query.getSkip() > 0) {
options.skip((int) query.getSkip());
}
Meta meta = query.getMeta();
if (meta.hasValues()) {
if (meta.hasMaxTime()) {
options.maxTime(meta.getRequiredMaxTimeMsec(), TimeUnit.MILLISECONDS);
}
if (meta.hasComment()) {
options.comment(meta.getComment());
}
}
if (StringUtils.hasText(query.getHint())) {
String hint = query.getHint();

View File

@@ -2305,8 +2305,9 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
publisher.sort(mappedSort);
}
if (filterQuery.getMeta().getMaxTimeMsec() != null) {
publisher.maxTime(filterQuery.getMeta().getMaxTimeMsec(), TimeUnit.MILLISECONDS);
Meta meta = filterQuery.getMeta();
if (meta.hasMaxTime()) {
publisher.maxTime(meta.getRequiredMaxTimeMsec(), TimeUnit.MILLISECONDS);
}
if (filterQuery.getLimit() > 0 || (options.getLimit() != null)) {
@@ -3454,12 +3455,12 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
if (meta.hasValues()) {
if (StringUtils.hasText(meta.getComment())) {
findPublisherToUse = findPublisherToUse.comment(meta.getComment());
if (meta.hasComment()) {
findPublisherToUse = findPublisherToUse.comment(meta.getRequiredComment());
}
if (meta.getMaxTimeMsec() != null) {
findPublisherToUse = findPublisherToUse.maxTime(meta.getMaxTimeMsec(), TimeUnit.MILLISECONDS);
if (meta.hasMaxTime()) {
findPublisherToUse = findPublisherToUse.maxTime(meta.getRequiredMaxTimeMsec(), TimeUnit.MILLISECONDS);
}
if (meta.getCursorBatchSize() != null) {

View File

@@ -2161,7 +2161,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
*
* @since 3.4.3
*/
interface ConversionContext {
protected interface ConversionContext {
/**
* Converts a source object into {@link TypeInformation target}.
@@ -2329,8 +2329,10 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
if (source instanceof Collection) {
Class<?> rawType = typeHint.getType();
if (!Object.class.equals(rawType)) {
if (!Object.class.equals(rawType) && !String.class.equals(rawType)) {
if (!rawType.isArray() && !ClassUtils.isAssignable(Iterable.class, rawType)) {
throw new MappingException(
String.format(INCOMPATIBLE_TYPES, source, source.getClass(), rawType, getPath()));
}
@@ -2359,11 +2361,6 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return (S) dbRefConverter.convert(context, (DBRef) source, typeHint);
}
if (source instanceof Collection) {
throw new MappingException(
String.format(INCOMPATIBLE_TYPES, source, BasicDBList.class, typeHint.getType(), getPath()));
}
if (BsonUtils.supportsBson(source)) {
return (S) documentConverter.convert(context, BsonUtils.asBson(source), typeHint);
}

View File

@@ -70,6 +70,19 @@ public class Meta {
this.allowDiskUse = source.allowDiskUse;
}
/**
* Return whether the maximum time limit for processing operations is set.
*
* @return {@code true} if set; {@code false} otherwise.
* @since 3.4.12
*/
public boolean hasMaxTime() {
Long maxTimeMsec = getMaxTimeMsec();
return maxTimeMsec != null && maxTimeMsec > 0;
}
/**
* @return {@literal null} if not set.
*/
@@ -78,6 +91,26 @@ public class Meta {
return getValue(MetaKey.MAX_TIME_MS.key);
}
/**
* Returns the required maximum time limit in milliseconds or throws {@link IllegalStateException} if the maximum time
* limit is not set.
*
* @return the maximum time limit in milliseconds for processing operations.
* @throws IllegalStateException if the maximum time limit is not set
* @see #hasMaxTime()
* @since 3.4.12
*/
public Long getRequiredMaxTimeMsec() {
Long maxTimeMsec = getMaxTimeMsec();
if (maxTimeMsec == null) {
throw new IllegalStateException("Maximum time limit in milliseconds not set");
}
return maxTimeMsec;
}
/**
* Set the maximum time limit in milliseconds for processing operations.
*
@@ -112,12 +145,13 @@ public class Meta {
}
/**
* Add a comment to the query that is propagated to the profile log.
* Return whether the comment is set.
*
* @param comment
* @return {@code true} if set; {@code false} otherwise.
* @since 3.4.12
*/
public void setComment(String comment) {
setValue(MetaKey.COMMENT.key, comment);
public boolean hasComment() {
return StringUtils.hasText(getComment());
}
/**
@@ -128,6 +162,34 @@ public class Meta {
return getValue(MetaKey.COMMENT.key);
}
/**
* Returns the required comment or throws {@link IllegalStateException} if the comment is not set.
*
* @return the comment.
* @throws IllegalStateException if the comment is not set
* @see #hasComment()
* @since 3.4.12
*/
public String getRequiredComment() {
String comment = getComment();
if (comment == null) {
throw new IllegalStateException("Comment not set");
}
return comment;
}
/**
* Add a comment to the query that is propagated to the profile log.
*
* @param comment
*/
public void setComment(String comment) {
setValue(MetaKey.COMMENT.key, comment);
}
/**
* @return {@literal null} if not set.
* @since 2.1

View File

@@ -36,7 +36,6 @@ import org.springframework.expression.ExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Internal utility class to help avoid duplicate code required in both the reactive and the sync {@link Aggregation}
@@ -83,7 +82,7 @@ abstract class AggregationUtils {
Meta meta = queryMethod.getQueryMetaAttributes();
if (StringUtils.hasText(meta.getComment())) {
if (meta.hasComment()) {
builder.comment(meta.getComment());
}
@@ -91,8 +90,8 @@ abstract class AggregationUtils {
builder.cursorBatchSize(meta.getCursorBatchSize());
}
if (meta.getMaxTimeMsec() != null && meta.getMaxTimeMsec() > 0) {
builder.maxTime(Duration.ofMillis(meta.getMaxTimeMsec()));
if (meta.hasMaxTime()) {
builder.maxTime(Duration.ofMillis(meta.getRequiredMaxTimeMsec()));
}
if (meta.getAllowDiskUse() != null) {

View File

@@ -51,7 +51,6 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
@@ -122,16 +121,7 @@ import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.CountOptions;
import com.mongodb.client.model.CreateCollectionOptions;
import com.mongodb.client.model.DeleteOptions;
import com.mongodb.client.model.FindOneAndDeleteOptions;
import com.mongodb.client.model.FindOneAndReplaceOptions;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.MapReduceAction;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.model.TimeSeriesGranularity;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.model.*;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
@@ -2317,6 +2307,26 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
verify(collection).countDocuments(any(Document.class), any());
}
@Test // GH-4374
void countConsidersMaxTimeMs() {
template.count(new BasicQuery("{ 'spring' : 'data-mongodb' }").maxTimeMsec(5000), Human.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).countDocuments(any(Document.class), options.capture());
assertThat(options.getValue().getMaxTime(TimeUnit.MILLISECONDS)).isEqualTo(5000);
}
@Test // GH-4374
void countPassesOnComment() {
template.count(new BasicQuery("{ 'spring' : 'data-mongodb' }").comment("rocks!"), Human.class);
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).countDocuments(any(Document.class), options.capture());
assertThat(options.getValue().getComment()).isEqualTo(BsonUtils.simpleToBsonValue("rocks!"));
}
@Test // GH-3984
void templatePassesOnTimeSeriesOptionsWhenNoTypeGiven() {
@@ -2479,8 +2489,7 @@ public class MongoTemplateUnitTests extends MongoOperationsUnitTests {
*/
@Override
protected MongoOperations getOperationsForExceptionHandling() {
MongoTemplate template = spy(this.template);
lenient().when(template.getDb()).thenThrow(new MongoException("Error!"));
when(template.getMongoDatabaseFactory().getMongoDatabase()).thenThrow(new MongoException("Error"));
return template;
}

View File

@@ -23,6 +23,7 @@ import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.mongodb.util.BsonUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@@ -1456,6 +1457,26 @@ public class ReactiveMongoTemplateUnitTests {
verify(collection).countDocuments(any(Document.class), any());
}
@Test // GH-4374
void countConsidersMaxTimeMs() {
template.count(new BasicQuery("{ 'spring' : 'data-mongodb' }").maxTimeMsec(5000), Person.class).subscribe();
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).countDocuments(any(Document.class), options.capture());
assertThat(options.getValue().getMaxTime(TimeUnit.MILLISECONDS)).isEqualTo(5000);
}
@Test // GH-4374
void countPassesOnComment() {
template.count(new BasicQuery("{ 'spring' : 'data-mongodb' }").comment("rocks!"), Person.class).subscribe();
ArgumentCaptor<CountOptions> options = ArgumentCaptor.forClass(CountOptions.class);
verify(collection).countDocuments(any(Document.class), options.capture());
assertThat(options.getValue().getComment()).isEqualTo(BsonUtils.simpleToBsonValue("rocks!"));
}
@Test // GH-2911
void insertErrorsOnPublisher() {

View File

@@ -2858,6 +2858,14 @@ class MappingMongoConverterUnitTests {
assertThat(converter.read(Cyclic.class, source).cycle.value).isEqualTo("v2");
}
@Test // GH-4371
void shouldConvertTypesToStringTargetType() {
org.bson.Document source = org.bson.Document.parse("{\n" + " city : [\"Gotham\", \"Metropolis\"]\n" + "}\n");
assertThat(converter.read(Address.class, source).city).isEqualTo("Gotham,Metropolis");
}
static class GenericType<T> {
T content;
}

View File

@@ -272,7 +272,8 @@ public class MongoQueryMethodUnitTests {
void queryCreationForUpdateMethodFailsOnInvalidReturnType() throws Exception {
assertThatExceptionOfType(IllegalStateException.class) //
.isThrownBy(() -> queryMethod(InvalidUpdateMethodRepo.class, "findAndIncrementVisitsByFirstname", String.class).verify()) //
.isThrownBy(() -> queryMethod(InvalidUpdateMethodRepo.class, "findAndIncrementVisitsByFirstname", String.class)
.verify()) //
.withMessageContaining("Update") //
.withMessageContaining("numeric") //
.withMessageContaining("findAndIncrementVisitsByFirstname");

View File

@@ -28,7 +28,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
@@ -43,8 +42,6 @@ import org.springframework.data.mongodb.repository.query.MongoEntityInformation;
class SimpleReactiveMongoRepositoryUnitTests {
private SimpleReactiveMongoRepository<Object, String> repository;
@Mock Mono mono;
@Mock Flux flux;
@Mock ReactiveMongoOperations mongoOperations;
@Mock MongoEntityInformation<Object, String> entityInformation;
@@ -56,7 +53,7 @@ class SimpleReactiveMongoRepositoryUnitTests {
@Test // DATAMONGO-1854
void shouldAddDefaultCollationToCountForExampleIfPresent() {
when(mongoOperations.count(any(), any(), any())).thenReturn(mono);
when(mongoOperations.count(any(), any(), any())).thenReturn(Mono.empty());
Collation collation = Collation.of("en_US");
@@ -72,7 +69,7 @@ class SimpleReactiveMongoRepositoryUnitTests {
@Test // DATAMONGO-1854
void shouldAddDefaultCollationToExistsForExampleIfPresent() {
when(mongoOperations.exists(any(), any(), any())).thenReturn(mono);
when(mongoOperations.exists(any(), any(), any())).thenReturn(Mono.empty());
Collation collation = Collation.of("en_US");
@@ -88,7 +85,7 @@ class SimpleReactiveMongoRepositoryUnitTests {
@Test // DATAMONGO-1854
void shouldAddDefaultCollationToFindForExampleIfPresent() {
when(mongoOperations.find(any(), any(), any())).thenReturn(flux);
when(mongoOperations.find(any(), any(), any())).thenReturn(Flux.empty());
Collation collation = Collation.of("en_US");
@@ -104,7 +101,7 @@ class SimpleReactiveMongoRepositoryUnitTests {
@Test // DATAMONGO-1854
void shouldAddDefaultCollationToFindWithSortForExampleIfPresent() {
when(mongoOperations.find(any(), any(), any())).thenReturn(flux);
when(mongoOperations.find(any(), any(), any())).thenReturn(Flux.empty());
Collation collation = Collation.of("en_US");
@@ -120,7 +117,8 @@ class SimpleReactiveMongoRepositoryUnitTests {
@Test // DATAMONGO-1854
void shouldAddDefaultCollationToFindOneForExampleIfPresent() {
when(mongoOperations.find(any(), any(), any())).thenReturn(flux);
when(entityInformation.getCollectionName()).thenReturn("testdummy");
doReturn(Flux.empty()).when(mongoOperations).find(any(Query.class), eq(TestDummy.class), eq("testdummy"));
Collation collation = Collation.of("en_US");

View File

@@ -1,4 +1,4 @@
Spring Data MongoDB 3.4.10 (2021.2.10)
Spring Data MongoDB 3.4.12 (2021.2.12)
Copyright (c) [2010-2019] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -45,6 +45,8 @@ conditions of the subcomponent's license, as noted in the LICENSE file.