Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f560f2ec3 | ||
|
|
752d8c821e | ||
|
|
b478e7068b | ||
|
|
02fe73d052 | ||
|
|
29021d132f | ||
|
|
4d51d27cda | ||
|
|
e2dc76eea3 | ||
|
|
aecfd45968 | ||
|
|
0c36929833 | ||
|
|
bedd94fe17 | ||
|
|
b85b53443b | ||
|
|
99070162bb | ||
|
|
9218b22d12 | ||
|
|
e5aab51add |
4
.mvn/wrapper/maven-wrapper.properties
vendored
4
.mvn/wrapper/maven-wrapper.properties
vendored
@@ -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
2
Jenkinsfile
vendored
@@ -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
36
pom.xml
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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}.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
|
||||
@@ -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>
|
||||
----
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user