Compare commits

..

14 Commits
4.1.0 ... 4.1.1

Author SHA1 Message Date
John Blum
4f560f2ec3 Release version 4.1.1 (2023.0.1).
See #4386
2023-06-16 08:05:36 -07:00
John Blum
752d8c821e Prepare 4.1.1 (2023.0.1).
See #4386
2023-06-16 08:05:06 -07:00
Mark Paluch
b478e7068b Retain scroll direction across keyset scroll requests.
Closes #4413
2023-06-15 15:22:17 +02:00
Christoph Strobl
02fe73d052 Accept expression as input for filter aggregation operator.
Closes #4394
Original pull request: #4395
2023-06-14 14:19:34 +02:00
Christoph Strobl
29021d132f Fix converter registration when using driver native time codec.
This commit prevents converters from being used as writing converter causing asymmetric write/read operations.

Closes #4390
Original pull request: #4392
2023-06-14 11:03:49 +02:00
Mark Paluch
4d51d27cda Polishing.
Use extended switch syntax.

See #4404
Original pull request: #4412
2023-06-14 10:00:25 +02:00
Christoph Strobl
e2dc76eea3 Polishing.
Mark method potentially returning null as such and remove unused imports.

See #4404
Original pull request: #4412
2023-06-14 10:00:24 +02:00
Christoph Strobl
aecfd45968 Use exact matching for IN clause with ignore case.
Prior to this change the generated pattern would have matched more entries than it should have. The behavior is now aligned to its counterpart not using the IgnoreCase flag.

Closes #4404
Original pull request: #4412
2023-06-14 10:00:24 +02:00
Mark Paluch
0c36929833 Upgrade to Maven Wrapper 3.9.2.
See #4409
2023-06-13 08:53:53 +02:00
Mark Paluch
bedd94fe17 Use snapshot and milestone repositories instead of libs-snapshot and libs-milestone.
Closes #4401
2023-06-06 09:47:17 +02:00
Mark Paluch
b85b53443b Polishing.
Add missing Override annotations.
2023-05-26 14:49:41 +02:00
Mark Paluch
99070162bb Update Jenkins triggers after GA release.
See #4369
2023-05-12 14:48:20 +02:00
Christoph Strobl
9218b22d12 After release cleanups.
See #4369
2023-05-12 14:19:11 +02:00
Christoph Strobl
e5aab51add Prepare next development iteration.
See #4369
2023-05-12 14:19:09 +02:00
22 changed files with 274 additions and 117 deletions

View File

@@ -1,2 +1,2 @@
#Thu Apr 06 16:16:28 CEST 2023
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.1/apache-maven-3.9.1-bin.zip
#Tue Jun 13 08:53:53 CEST 2023
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip

2
Jenkinsfile vendored
View File

@@ -9,7 +9,7 @@ pipeline {
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS)
upstream(upstreamProjects: "spring-data-commons/3.1.x", threshold: hudson.model.Result.SUCCESS)
}
options {

36
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.1.0</version>
<version>4.1.1</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>3.1.0</version>
<version>3.1.1</version>
</parent>
<modules>
@@ -26,7 +26,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>3.1.0</springdata.commons>
<springdata.commons>3.1.1</springdata.commons>
<mongo>4.9.1</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
@@ -144,34 +144,8 @@
</dependencies>
<repositories>
<repository>
<id>spring-libs-release</id>
<url>https://repo.spring.io/libs-release</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>sonatype-libs-snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
<pluginRepository>
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
</pluginRepository>
</pluginRepositories>
</project>

View File

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

View File

@@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.1.0</version>
<version>4.1.1</version>
<relativePath>../pom.xml</relativePath>
</parent>
@@ -80,15 +80,4 @@
</build>
<pluginRepositories>
<pluginRepository>
<id>spring-plugins-release</id>
<url>https://repo.spring.io/plugins-release</url>
</pluginRepository>
<pluginRepository>
<id>spring-plugins-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</pluginRepository>
</pluginRepositories>
</project>

View File

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

View File

@@ -61,7 +61,8 @@ class ScrollUtils {
Document sortObject = query.getSortObject();
KeysetScrollPosition keyset = query.getKeyset();
KeysetScrollDirector director = KeysetScrollDirector.of(keyset.getDirection());
Direction direction = keyset.getDirection();
KeysetScrollDirector director = KeysetScrollDirector.of(direction);
List<T> resultsToUse = director.postPostProcessResults(result, query.getLimit());
@@ -71,7 +72,7 @@ class ScrollUtils {
Entity<T> entity = operations.forEntity(last);
Map<String, Object> keys = entity.extractKeys(sortObject, sourceType);
return ScrollPosition.forward(keys);
return ScrollPosition.of(keys, direction);
};
return Window.from(resultsToUse, positionFunction, hasMoreElements(result, query.getLimit()));

View File

@@ -79,7 +79,7 @@ public class ArrayOperators {
private final @Nullable String fieldReference;
private final @Nullable AggregationExpression expression;
private final @Nullable Collection values;
private final @Nullable Collection<?> values;
/**
* Creates new {@link ArrayOperatorFactory} for given {@literal fieldReference}.
@@ -214,6 +214,10 @@ public class ArrayOperators {
return Filter.filter(fieldReference);
}
if (usesExpression()) {
return Filter.filter(expression);
}
Assert.state(values != null, "Values must not be null");
return Filter.filter(new ArrayList<>(values));
}
@@ -317,7 +321,8 @@ public class ArrayOperators {
}
/**
* Creates new {@link AggregationExpression} that takes the associated array and sorts it by the given {@link Sort order}.
* Creates new {@link AggregationExpression} that takes the associated array and sorts it by the given {@link Sort
* order}.
*
* @return new instance of {@link SortArray}.
* @since 4.0
@@ -397,8 +402,8 @@ public class ArrayOperators {
}
/**
* Creates new {@link AggregationExpression} that return the last element in the given array.
* <strong>NOTE:</strong> Requires MongoDB 4.4 or later.
* Creates new {@link AggregationExpression} that return the last element in the given array. <strong>NOTE:</strong>
* Requires MongoDB 4.4 or later.
*
* @return new instance of {@link Last}.
* @since 3.4
@@ -649,6 +654,19 @@ public class ArrayOperators {
return new FilterExpressionBuilder().filter(field);
}
/**
* Set the {@link AggregationExpression} resolving to an arry to apply the {@code $filter} to.
*
* @param expression must not be {@literal null}.
* @return never {@literal null}.
* @since 4.2
*/
public static AsBuilder filter(AggregationExpression expression) {
Assert.notNull(expression, "Field must not be null");
return new FilterExpressionBuilder().filter(expression);
}
/**
* Set the {@literal values} to apply the {@code $filter} to.
*
@@ -681,7 +699,16 @@ public class ArrayOperators {
}
private Object getMappedInput(AggregationOperationContext context) {
return input instanceof Field field ? context.getReference(field).toString() : input;
if (input instanceof Field field) {
return context.getReference(field).toString();
}
if (input instanceof AggregationExpression expression) {
return expression.toDocument(context);
}
return input;
}
private Object getMappedCondition(AggregationOperationContext context) {
@@ -715,6 +742,15 @@ public class ArrayOperators {
* @return
*/
AsBuilder filter(Field field);
/**
* Set the {@link AggregationExpression} resolving to an array to apply the {@code $filter} to.
*
* @param expression must not be {@literal null}.
* @return
* @since 4.1.1
*/
AsBuilder filter(AggregationExpression expression);
}
/**
@@ -797,6 +833,14 @@ public class ArrayOperators {
return this;
}
@Override
public AsBuilder filter(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null");
filter.input = expression;
return this;
}
@Override
public ConditionBuilder as(String variableName) {
@@ -1333,7 +1377,7 @@ public class ArrayOperators {
Assert.notNull(expressions, "PropertyExpressions must not be null");
return new Reduce(Fields.field(fieldReference), initialValue,
Arrays.<AggregationExpression>asList(expressions));
Arrays.<AggregationExpression> asList(expressions));
}
};
}
@@ -1690,7 +1734,7 @@ public class ArrayOperators {
* @author Christoph Strobl
* @author Shashank Sharma
* @see <a href=
* "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/</a>
* "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/</a>
* @since 2.2
*/
public static class In extends AbstractAggregationExpression {
@@ -1779,7 +1823,7 @@ public class ArrayOperators {
*
* @author Christoph Strobl
* @see <a href=
* "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/</a>
* "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/</a>
* @since 2.1
*/
public static class ArrayToObject extends AbstractAggregationExpression {
@@ -1976,7 +2020,7 @@ public class ArrayOperators {
/**
* Set the order to put elements in.
*
*
* @param sort must not be {@literal null}.
* @return new instance of {@link SortArray}.
*/

View File

@@ -41,6 +41,7 @@ import org.springframework.data.convert.PropertyValueConversions;
import org.springframework.data.convert.PropertyValueConverter;
import org.springframework.data.convert.PropertyValueConverterFactory;
import org.springframework.data.convert.PropertyValueConverterRegistrar;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.SimplePropertyValueConversions;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.model.SimpleTypeHolder;
@@ -361,6 +362,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus
}, this.propertyValueConversions);
}
@ReadingConverter
private enum DateToUtcLocalDateTimeConverter implements Converter<Date, LocalDateTime> {
INSTANCE;
@@ -370,6 +372,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus
}
}
@ReadingConverter
private enum DateToUtcLocalTimeConverter implements Converter<Date, LocalTime> {
INSTANCE;
@@ -379,6 +382,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus
}
}
@ReadingConverter
private enum DateToUtcLocalDateConverter implements Converter<Date, LocalDate> {
INSTANCE;

View File

@@ -89,25 +89,22 @@ public enum MongoRegexCreator {
String regex = prepareAndEscapeStringBeforeApplyingLikeRegex(source, matcherType);
switch (matcherType) {
case STARTING_WITH:
return String.format("^%s", regex);
case ENDING_WITH:
return String.format("%s$", regex);
case CONTAINING:
return String.format(".*%s.*", regex);
case EXACT:
return String.format("^%s$", regex);
default:
return regex;
}
return switch (matcherType) {
case STARTING_WITH -> String.format("^%s", regex);
case ENDING_WITH -> String.format("%s$", regex);
case CONTAINING -> String.format(".*%s.*", regex);
case EXACT -> String.format("^%s$", regex);
default -> regex;
};
}
/**
* @param source
* @return
* @since 2.2.14
* @deprecated since 4.1.1
*/
@Deprecated(since = "4.1.1", forRemoval = true)
public Object toCaseInsensitiveMatch(Object source) {
return source instanceof String stringValue ? new BsonRegularExpression(Pattern.quote(stringValue), "i") : source;
}

View File

@@ -87,11 +87,14 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe
this.bucket = bucket;
}
@Override
public ObjectId store(InputStream content, @Nullable String filename, @Nullable String contentType,
@Nullable Object metadata) {
return store(content, filename, contentType, toDocument(metadata));
}
@Override
@SuppressWarnings("unchecked")
public <T> T store(GridFsObject<T, InputStream> upload) {
GridFSUploadOptions uploadOptions = computeUploadOptionsFor(upload.getOptions().getContentType(),
@@ -110,6 +113,7 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe
return upload.getFileId();
}
@Override
public GridFSFindIterable find(Query query) {
Assert.notNull(query, "Query must not be null");
@@ -130,10 +134,12 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe
return iterable;
}
@Override
public GridFSFile findOne(Query query) {
return find(query).first();
}
@Override
public void delete(Query query) {
for (GridFSFile gridFSFile : find(query)) {
@@ -141,10 +147,12 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe
}
}
@Override
public ClassLoader getClassLoader() {
return dbFactory.getClass().getClassLoader();
}
@Override
public GridFsResource getResource(String location) {
return Optional.ofNullable(findOne(query(whereFilename().is(location)))) //
@@ -152,6 +160,7 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe
.orElseGet(() -> GridFsResource.absent(location));
}
@Override
public GridFsResource getResource(GridFSFile file) {
Assert.notNull(file, "GridFSFile must not be null");
@@ -159,6 +168,7 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe
return new GridFsResource(file, getGridFs().openDownloadStream(file.getId()));
}
@Override
public GridFsResource[] getResources(String locationPattern) {
if (!StringUtils.hasText(locationPattern)) {
@@ -184,6 +194,8 @@ public class GridFsTemplate extends GridFsOperationsSupport implements GridFsOpe
private GridFSBucket getGridFs() {
Assert.notNull(dbFactory, "MongoDatabaseFactory must not be null");
MongoDatabase db = dbFactory.getMongoDatabase();
return bucket == null ? GridFSBuckets.create(db) : GridFSBuckets.create(db, bucket);
}

View File

@@ -82,7 +82,7 @@ public class ReactiveGridFsTemplate extends GridFsOperationsSupport implements R
*
* @param dbFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param bucket
* @param bucket can be {@literal null}.
*/
public ReactiveGridFsTemplate(ReactiveMongoDatabaseFactory dbFactory, MongoConverter converter,
@Nullable String bucket) {
@@ -96,7 +96,7 @@ public class ReactiveGridFsTemplate extends GridFsOperationsSupport implements R
* @param dataBufferFactory must not be {@literal null}.
* @param dbFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param bucket
* @param bucket can be {@literal null}.
*/
public ReactiveGridFsTemplate(DataBufferFactory dataBufferFactory, ReactiveMongoDatabaseFactory dbFactory,
MongoConverter converter, @Nullable String bucket) {
@@ -117,6 +117,8 @@ public class ReactiveGridFsTemplate extends GridFsOperationsSupport implements R
return store(content, filename, contentType, toDocument(metadata));
}
@Override
@SuppressWarnings("unchecked")
public <T> Mono<T> store(GridFsObject<T, Publisher<DataBuffer>> upload) {
GridFSUploadOptions uploadOptions = computeUploadOptionsFor(upload.getOptions().getContentType(),
@@ -274,6 +276,7 @@ public class ReactiveGridFsTemplate extends GridFsOperationsSupport implements R
this.sortObject = sortObject;
}
@Override
public GridFSFindPublisher doInBucket(GridFSBucket bucket) {
GridFSFindPublisher findPublisher = bucket.find(queryObject).sort(sortObject);
@@ -311,21 +314,8 @@ public class ReactiveGridFsTemplate extends GridFsOperationsSupport implements R
}
}
private static class UploadCallback implements ReactiveBucketCallback<Void> {
private final BsonValue fileId;
private final String filename;
private final Publisher<ByteBuffer> source;
private final GridFSUploadOptions uploadOptions;
public UploadCallback(BsonValue fileId, String filename, Publisher<ByteBuffer> source,
GridFSUploadOptions uploadOptions) {
this.fileId = fileId;
this.filename = filename;
this.source = source;
this.uploadOptions = uploadOptions;
}
private record UploadCallback(BsonValue fileId, String filename, Publisher<ByteBuffer> source,
GridFSUploadOptions uploadOptions) implements ReactiveBucketCallback<Void> {
@Override
public GridFSUploadPublisher<Void> doInBucket(GridFSBucket bucket) {
@@ -333,19 +323,8 @@ public class ReactiveGridFsTemplate extends GridFsOperationsSupport implements R
}
}
private static class AutoIdCreatingUploadCallback implements ReactiveBucketCallback<ObjectId> {
private final String filename;
private final Publisher<ByteBuffer> source;
private final GridFSUploadOptions uploadOptions;
public AutoIdCreatingUploadCallback(String filename, Publisher<ByteBuffer> source,
GridFSUploadOptions uploadOptions) {
this.filename = filename;
this.source = source;
this.uploadOptions = uploadOptions;
}
private record AutoIdCreatingUploadCallback(String filename, Publisher<ByteBuffer> source,
GridFSUploadOptions uploadOptions) implements ReactiveBucketCallback<ObjectId> {
@Override
public GridFSUploadPublisher<ObjectId> doInBucket(GridFSBucket bucket) {
@@ -353,13 +332,7 @@ public class ReactiveGridFsTemplate extends GridFsOperationsSupport implements R
}
}
private static class DeleteCallback implements ReactiveBucketCallback<Void> {
private final BsonValue id;
public DeleteCallback(BsonValue id) {
this.id = id;
}
private record DeleteCallback(BsonValue id) implements ReactiveBucketCallback<Void> {
@Override
public Publisher<Void> doInBucket(GridFSBucket bucket) {

View File

@@ -25,7 +25,7 @@ import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.BsonRegularExpression;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Range.Bound;
import org.springframework.data.domain.Sort;
@@ -52,6 +52,7 @@ import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -352,6 +353,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
* @param part
* @return the regex options or {@literal null}.
*/
@Nullable
private String toRegexOptions(Part part) {
String regexOptions = null;
@@ -390,7 +392,18 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
Streamable<?> streamable = asStreamable(iterator.next());
if (!isSimpleComparisionPossible(part)) {
streamable = streamable.map(MongoRegexCreator.INSTANCE::toCaseInsensitiveMatch);
MatchMode matchMode = toMatchMode(part.getType());
String regexOptions = toRegexOptions(part);
streamable = streamable.map(it -> {
if (it instanceof String value) {
return new BsonRegularExpression(MongoRegexCreator.INSTANCE.toRegularExpression(value, matchMode),
regexOptions);
}
return it;
});
}
return streamable.toList();
@@ -481,6 +494,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
return MatchMode.REGEX;
case NEGATING_SIMPLE_PROPERTY:
case SIMPLE_PROPERTY:
case IN:
return MatchMode.EXACT;
default:
return MatchMode.DEFAULT;

View File

@@ -193,9 +193,30 @@ class MongoTemplateScrollTests {
window = template.scroll(q.with(window.positionAt(0)).limit(2), Person.class);
assertThat(window).hasSize(2);
assertThat(window).containsOnly(john20, john40_1);
assertThat(window.hasNext()).isTrue();
assertThat(window.isLast()).isFalse();
assertThat(window).containsOnly(jane_20, jane_40);
assertThat(window.hasNext()).isFalse();
assertThat(window.isLast()).isTrue();
}
@Test // GH-4413
void shouldAllowInitialBackwardSort() {
Person jane_20 = new Person("Jane", 20);
Person jane_40 = new Person("Jane", 40);
Person jane_42 = new Person("Jane", 42);
Person john20 = new Person("John", 20);
Person john40_1 = new Person("John", 40);
Person john40_2 = new Person("John", 40);
template.insertAll(Arrays.asList(john20, john40_1, john40_2, jane_20, jane_40, jane_42));
Query q = new Query(where("firstName").regex("J.*")).with(Sort.by("firstName", "age"));
q.with(ScrollPosition.keyset().backward()).limit(3);
Window<Person> window = template.scroll(q, Person.class);
assertThat(window).containsExactly(john20, john40_1, john40_2);
window = template.scroll(q.with(window.positionAt(0)).limit(3), Person.class);
assertThat(window).containsExactly(jane_20, jane_40, jane_42);
}
@ParameterizedTest // GH-4308

View File

@@ -69,6 +69,8 @@ import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.BulkOperations.BulkMode;
import org.springframework.data.mongodb.core.aggregation.StringOperators;
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
import org.springframework.data.mongodb.core.convert.MongoCustomConversions.MongoConverterConfigurationAdapter;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.IndexField;
@@ -1789,6 +1791,30 @@ public class MongoTemplateTests {
assertThat(result.get(0).date).isNotNull();
}
@Test // GH-4390
void nativeDriverDateTimeCodecShouldBeApplied/*when configured*/() {
MongoTestTemplate ops = new MongoTestTemplate(cfg -> {
cfg.configureConversion(conversion -> {
conversion.customConversions(
MongoCustomConversions.create(MongoConverterConfigurationAdapter::useNativeDriverJavaTimeCodecs));
});
});
TypeWithDate source = new TypeWithDate();
source.id = "id-1";
source.date = Date.from(Instant.now());
ops.save(source);
var dbDate = ops.execute(TypeWithDate.class,
collection -> collection.find(new org.bson.Document("_id", source.id)).first().get("date"));
TypeWithDate target = ops.findOne(query(where("date").is(source.date)), TypeWithDate.class);
assertThat(target.date).isEqualTo(source.date).isEqualTo(dbDate);
}
@Test // DATAMONGO-540
public void findOneAfterUpsertForNonExistingObjectReturnsTheInsertedObject() {

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2023 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;
import static org.assertj.core.api.AssertionsForClassTypes.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.KeysetScrollPosition;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Window;
import org.springframework.data.mongodb.core.EntityOperations.Entity;
import org.springframework.data.mongodb.core.query.Query;
/**
* Unit tests for {@link ScrollUtils}.
*
* @author Mark Paluch
*/
class ScrollUtilsUnitTests {
@Test // GH-4413
void positionShouldRetainScrollDirection() {
Query query = new Query();
query.with(ScrollPosition.keyset().backward());
EntityOperations entityOperationsMock = mock(EntityOperations.class);
Entity entityMock = mock(Entity.class);
when(entityOperationsMock.forEntity(any())).thenReturn(entityMock);
when(entityMock.extractKeys(any(), any())).thenReturn(Map.of("k", "v"));
Window<Integer> window = ScrollUtils.createWindow(query, new ArrayList<>(List.of(1, 2, 3)), Integer.class,
entityOperationsMock);
assertThat(window.positionAt(0)).isInstanceOf(KeysetScrollPosition.class);
assertThat(((KeysetScrollPosition) window.positionAt(0)).scrollsBackward()).isTrue();
}
}

View File

@@ -117,6 +117,23 @@ class FilterExpressionUnitTests {
assertThat($filter).isEqualTo(new Document(expected));
}
@Test // GH-4394
void filterShouldAcceptExpression() {
Document $filter = ArrayOperators.arrayOf(ObjectOperators.valueOf("data.metadata").toArray()).filter().as("item")
.by(ComparisonOperators.valueOf("item.price").greaterThan("field-1")).toDocument(Aggregation.DEFAULT_CONTEXT);
Document expected = Document.parse("""
{ $filter : {
input: { $objectToArray: "$data.metadata" },
as: "item",
cond: { $gt: [ "$$item.price", "$field-1" ] }
}}
""");
assertThat($filter).isEqualTo(expected);
}
private Document extractFilterOperatorFromDocument(Document source) {
List<Object> pipeline = DocumentTestUtils.getAsDBList(source, "pipeline");

View File

@@ -64,6 +64,16 @@ class MongoCustomConversionsUnitTests {
assertThat(conversions.getPropertyValueConversions().hasValueConverter(persistentProperty)).isTrue();
}
@Test // GH-4390
void doesNotReturnConverterForNativeTimeTimeIfUsingDriverCodec() {
MongoCustomConversions conversions = MongoCustomConversions.create(config -> {
config.useNativeDriverJavaTimeCodecs();
});
assertThat(conversions.getCustomWriteTarget(Date.class)).isEmpty();
}
static class DateToZonedDateTimeConverter implements Converter<Date, ZonedDateTime> {
@Override

View File

@@ -1510,9 +1510,16 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie
assertThat(result.get(0).getId().equals(bart.getId()));
}
@Test // GH-3395
@Test // GH-3395, GH-4404
void caseInSensitiveInClause() {
assertThat(repository.findByLastnameIgnoreCaseIn("bEAuFoRd", "maTTheWs")).hasSize(3);
repository.save(new Person("the-first", "The First"));
repository.save(new Person("the-first-one", "The First One"));
repository.save(new Person("the-second", "The Second"));
assertThat(repository.findByLastnameIgnoreCaseIn("tHE fIRsT")).hasSize(1);
}
@Test // GH-3395

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.mongodb.repository.query;
import static org.mockito.Mockito.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.repository.query.StubParameterAccessor.*;
@@ -25,6 +24,7 @@ import java.lang.reflect.Method;
import java.util.List;
import java.util.regex.Pattern;
import org.bson.BsonRegularExpression;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach;
@@ -273,6 +273,17 @@ class MongoQueryCreatorUnitTests {
assertThat(query).isEqualTo(query(where("firstName").regex("^dave$", "i")));
}
@Test // GH-4404
void createsQueryWithFindByInClauseHavingIgnoreCaseCorrectly() {
PartTree tree = new PartTree("findAllByFirstNameInIgnoreCase", Person.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, List.of("da've", "carter")), context);
Query query = creator.createQuery();
assertThat(query).isEqualTo(query(where("firstName")
.in(List.of(new BsonRegularExpression("^\\Qda've\\E$", "i"), new BsonRegularExpression("^carter$", "i")))));
}
@Test // DATAMONGO-770
void createsQueryWithFindByNotIgnoreCaseCorrectly() {

View File

@@ -57,7 +57,7 @@ To create a Spring project in STS:
<repository>
<id>spring-milestone</id>
<name>Spring Maven MILESTONE Repository</name>
<url>https://repo.spring.io/libs-milestone</url>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
----

View File

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