Compare commits

...

13 Commits

Author SHA1 Message Date
Christoph Strobl
32c73b7117 DATAMONGO-2499 - Hacking
Works, still not sure if that's a good solution.
2020-04-15 11:11:32 +02:00
Christoph Strobl
b68f3b9510 DATAMONGO-2499 - Prepare issue branch. 2020-04-14 13:26:09 +02:00
Christoph Strobl
e1df28797a DATAMONGO-2507 - Polishing.
Add 'matching' default method also to imperative variant (ExecutableFindOperation), fix & add tests using 'distinct'.
Update Javadoc and rename input arg 'criteriaDefinition' to 'criteria'.

Original Pull Request: #852
2020-04-14 10:22:01 +02:00
Juergen Zimmermann
c6630aa279 DATAMONGO-2507 - Add default method matching(CriteriaDefinition) to ReactiveFindOperation.DistinctWithQuery.
Original Pull Request: #852
2020-04-14 09:51:23 +02:00
Greg Turnquist
bf921cdbd7 DATAMONGO-2510 - Use JDK 14 for Java.NEXT CI testing. 2020-04-09 14:29:16 -05:00
Christoph Strobl
a1b4e6df59 DATAMONGO-2508 - Upgrade to MongoDB 4.0.2 Drivers. 2020-04-08 11:38:19 +02:00
Christoph Strobl
4b3312998a DATAMONGO-2506 - Provide meaningful error message when using unsupported return type in repository aggregation method.
We improved the error message for unsupported return types instead of running into an IllegalArgumentException for unique results.

Original pull request: #851.
2020-04-07 14:58:38 +02:00
Mark Paluch
c5501db577 DATAMONGO-2502 - Polishing.
Extend tests. Fix generics. Consistently use compiled patterns for positional placeholder removal.

Original pull request: #847.
2020-04-07 14:10:20 +02:00
Christoph Strobl
936a0d35f7 DATAMONGO-2502 - Fix nested array path mapping for updates.
Original pull request: #847.
2020-04-07 14:10:09 +02:00
Christoph Strobl
5dd91d0b6d DATAMONGO-1677 - Polishing.
Switch tests to JUnit Jupiter.

Original Pull Request: #849
2020-04-06 08:51:30 +02:00
Jens Schauder
28510de6c8 DATAMONGO-1677 - Adds a test to confirm a query with more than 10 arguments works just fine.
Original Pull Request: #849
2020-04-06 08:50:56 +02:00
Mark Paluch
4bbf4cd5cf DATAMONGO-2492 - After release cleanups. 2020-03-31 15:08:05 +02:00
Mark Paluch
90bd3f0f18 DATAMONGO-2492 - Prepare next development iteration. 2020-03-31 15:08:04 +02:00
29 changed files with 477 additions and 229 deletions

10
Jenkinsfile vendored
View File

@@ -46,16 +46,16 @@ pipeline {
}
}
}
stage('Publish JDK 13 + MongoDB 4.2') {
stage('Publish JDK 14 + MongoDB 4.2') {
when {
changeset "ci/openjdk13-mongodb-4.2/**"
changeset "ci/openjdk14-mongodb-4.2/**"
}
agent { label 'data' }
options { timeout(time: 30, unit: 'MINUTES') }
steps {
script {
def image = docker.build("springci/spring-data-openjdk13-with-mongodb-4.2.0", "ci/openjdk13-mongodb-4.2/")
def image = docker.build("springci/spring-data-openjdk14-with-mongodb-4.2.0", "ci/openjdk14-mongodb-4.2/")
docker.withRegistry('', 'hub.docker.com-springbuildmaster') {
image.push()
}
@@ -139,10 +139,10 @@ pipeline {
}
}
stage("test: baseline (jdk13)") {
stage("test: baseline (jdk14)") {
agent {
docker {
image 'springci/spring-data-openjdk13-with-mongodb-4.2.0:latest'
image 'springci/spring-data-openjdk14-with-mongodb-4.2.0:latest'
label 'data'
args '-v $HOME:/tmp/jenkins-home'
}

View File

@@ -1,4 +1,4 @@
FROM adoptopenjdk/openjdk13:latest
FROM adoptopenjdk/openjdk14:latest
ENV TZ=Etc/UTC
ENV DEBIAN_FRONTEND=noninteractive

12
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.0.0.RC1</version>
<version>3.0.0.DATAMONGO-2499-SNAPSHOT</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.3.0.RC1</version>
<version>2.3.0.BUILD-SNAPSHOT</version>
</parent>
<modules>
@@ -26,8 +26,8 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>2.3.0.RC1</springdata.commons>
<mongo>4.0.1</mongo>
<springdata.commons>2.3.0.BUILD-SNAPSHOT</springdata.commons>
<mongo>4.0.2</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
</properties>
@@ -134,8 +134,8 @@
<repositories>
<repository>
<id>spring-libs-milestone</id>
<url>https://repo.spring.io/libs-milestone</url>
<id>spring-libs-snapshot</id>
<url>https://repo.spring.io/libs-snapshot</url>
</repository>
<repository>
<id>sonatype-libs-snapshot</id>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>3.0.0.RC1</version>
<version>3.0.0.DATAMONGO-2499-SNAPSHOT</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.0.0.RC1</version>
<version>3.0.0.DATAMONGO-2499-SNAPSHOT</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.0.0.RC1</version>
<version>3.0.0.DATAMONGO-2499-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -44,7 +44,7 @@ import com.mongodb.client.MongoCollection;
* query(Human.class)
* .inCollection("star-wars")
* .as(Jedi.class)
* .matching(query(where("firstname").is("luke")))
* .matching(where("firstname").is("luke"))
* .all();
* </code>
* </pre>
@@ -174,13 +174,13 @@ public interface ExecutableFindOperation {
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteriaDefinition must not be {@literal null}.
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingFind}.
* @throws IllegalArgumentException if query is {@literal null}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingFind<T> matching(CriteriaDefinition criteriaDefinition) {
return matching(Query.query(criteriaDefinition));
default TerminatingFind<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
/**
@@ -304,9 +304,21 @@ public interface ExecutableFindOperation {
*
* @param query must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if resultType is {@literal null}.
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingDistinct<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingDistinct<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -152,13 +152,13 @@ public interface ExecutableMapReduceOperation {
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteriaDefinition must not be {@literal null}.
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingMapReduce}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingMapReduce<T> matching(CriteriaDefinition criteriaDefinition) {
return matching(Query.query(criteriaDefinition));
default TerminatingMapReduce<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}

View File

@@ -124,13 +124,13 @@ public interface ExecutableRemoveOperation {
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteriaDefinition must not be {@literal null}.
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingRemove}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingRemove<T> matching(CriteriaDefinition criteriaDefinition) {
return matching(Query.query(criteriaDefinition));
default TerminatingRemove<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}

View File

@@ -215,13 +215,13 @@ public interface ExecutableUpdateOperation {
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteriaDefinition must not be {@literal null}.
* @param criteria must not be {@literal null}.
* @return new instance of {@link UpdateWithUpdate}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default UpdateWithUpdate<T> matching(CriteriaDefinition criteriaDefinition) {
return matching(Query.query(criteriaDefinition));
default UpdateWithUpdate<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}

View File

@@ -39,13 +39,14 @@ import org.springframework.data.mongodb.core.query.Query;
* query(Human.class)
* .inCollection("star-wars")
* .as(Jedi.class)
* .matching(query(where("firstname").is("luke")))
* .matching(where("firstname").is("luke"))
* .all();
* </code>
* </pre>
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Juergen Zimmermann
* @since 2.0
*/
public interface ReactiveFindOperation {
@@ -148,13 +149,13 @@ public interface ReactiveFindOperation {
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteriaDefinition must not be {@literal null}.
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingFind}.
* @throws IllegalArgumentException if query is {@literal null}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingFind<T> matching(CriteriaDefinition criteriaDefinition) {
return matching(Query.query(criteriaDefinition));
default TerminatingFind<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
/**
@@ -272,9 +273,21 @@ public interface ReactiveFindOperation {
*
* @param query must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if resultType is {@literal null}.
* @throws IllegalArgumentException if query is {@literal null}.
*/
TerminatingDistinct<T> matching(Query query);
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingDistinct}.
* @throws IllegalArgumentException if criteria is {@literal null}.
* @since 3.0
*/
default TerminatingDistinct<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}
/**

View File

@@ -151,13 +151,13 @@ public interface ReactiveMapReduceOperation {
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteriaDefinition must not be {@literal null}.
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingMapReduce}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingMapReduce<T> matching(CriteriaDefinition criteriaDefinition) {
return matching(Query.query(criteriaDefinition));
default TerminatingMapReduce<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}

View File

@@ -111,13 +111,13 @@ public interface ReactiveRemoveOperation {
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteriaDefinition must not be {@literal null}.
* @param criteria must not be {@literal null}.
* @return new instance of {@link TerminatingRemove}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default TerminatingRemove<T> matching(CriteriaDefinition criteriaDefinition) {
return matching(Query.query(criteriaDefinition));
default TerminatingRemove<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}

View File

@@ -176,13 +176,13 @@ public interface ReactiveUpdateOperation {
/**
* Set the filter {@link CriteriaDefinition criteria} to be used.
*
* @param criteriaDefinition must not be {@literal null}.
* @param criteria must not be {@literal null}.
* @return new instance of {@link UpdateWithUpdate}.
* @throws IllegalArgumentException if query is {@literal null}.
* @since 3.0
*/
default UpdateWithUpdate<T> matching(CriteriaDefinition criteriaDefinition) {
return matching(Query.query(criteriaDefinition));
default UpdateWithUpdate<T> matching(CriteriaDefinition criteria) {
return matching(Query.query(criteria));
}
}

View File

@@ -419,7 +419,7 @@ public class QueryMapper {
return false;
}
Class<? extends Object> type = value.getClass();
Class<?> type = value.getClass();
MongoPersistentProperty property = documentField.getProperty();
if (property.getActualType().isAssignableFrom(type)) {
@@ -443,7 +443,7 @@ public class QueryMapper {
protected Object convertSimpleOrDocument(Object source, @Nullable MongoPersistentEntity<?> entity) {
if (source instanceof Example) {
return exampleMapper.getMappedExample((Example) source, entity);
return exampleMapper.getMappedExample((Example<?>) source, entity);
}
if (source instanceof List) {
@@ -922,6 +922,8 @@ public class QueryMapper {
*/
protected static class MetadataBackedField extends Field {
private static final Pattern POSITIONAL_PARAMETER_PATTERN = Pattern.compile("\\.\\$(\\[.*?\\])?|\\.\\d+");
private static final Pattern DOT_POSITIONAL_PATTERN = Pattern.compile("\\.\\d+");
private static final String INVALID_ASSOCIATION_REFERENCE = "Invalid path reference %s! Associations can only be pointed to directly or via their id property!";
private final MongoPersistentEntity<?> entity;
@@ -963,7 +965,7 @@ public class QueryMapper {
this.entity = entity;
this.mappingContext = context;
this.path = getPath(name);
this.path = getPath(removePlaceholders(POSITIONAL_PARAMETER_PATTERN, name));
this.property = path == null ? property : path.getLeafProperty();
this.association = findAssociation();
}
@@ -1071,7 +1073,7 @@ public class QueryMapper {
}
/**
* Returns the {@link PersistentPropertyPath} for the given <code>pathExpression</code>.
* Returns the {@link PersistentPropertyPath} for the given {@code pathExpression}.
*
* @param pathExpression
* @return
@@ -1079,8 +1081,8 @@ public class QueryMapper {
@Nullable
private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression) {
String rawPath = pathExpression.replaceAll("\\.\\d+", "") //
.replaceAll(POSITIONAL_OPERATOR.pattern(), "");
String rawPath = removePlaceholders(POSITIONAL_OPERATOR,
removePlaceholders(DOT_POSITIONAL_PATTERN, pathExpression));
PropertyPath path = forName(rawPath);
if (path == null || isPathToJavaLangClassProperty(path)) {
@@ -1168,13 +1170,17 @@ public class QueryMapper {
* @since 1.7
*/
protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
return new AssociationConverter(getAssociation());
return new AssociationConverter(name, getAssociation());
}
protected MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getMappingContext() {
return mappingContext;
}
private static String removePlaceholders(Pattern pattern, String raw) {
return pattern.matcher(raw).replaceAll("");
}
/**
* @author Christoph Strobl
* @since 1.8
@@ -1226,14 +1232,12 @@ public class QueryMapper {
static class KeyMapper {
private final Iterator<String> iterator;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
public KeyMapper(String key,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
this.iterator = Arrays.asList(key.split("\\.")).iterator();
this.iterator.next();
this.mappingContext = mappingContext;
}
/**
@@ -1247,21 +1251,10 @@ public class QueryMapper {
StringBuilder mappedName = new StringBuilder(PropertyToFieldNameConverter.INSTANCE.convert(property));
boolean inspect = iterator.hasNext();
int depth = 0;
while (inspect) {
String partial = iterator.next();
if (depth > 0 && property.isCollectionLike() && property.isEntity() && property.getComponentType() != null) {
MongoPersistentEntity<?> persistentEntity = mappingContext
.getRequiredPersistentEntity(property.getComponentType());
MongoPersistentProperty persistentProperty = persistentEntity.getPersistentProperty(partial);
if (persistentProperty != null) {
partial = mapPropertyName(persistentProperty);
}
}
boolean isPositional = (isPositionalParameter(partial) && (property.isMap() || property.isCollectionLike()));
if (isPositional) {
@@ -1269,13 +1262,12 @@ public class QueryMapper {
}
inspect = isPositional && iterator.hasNext();
depth++;
}
return mappedName.toString();
}
private static boolean isPositionalParameter(String partial) {
static boolean isPositionalParameter(String partial) {
if ("$".equals(partial)) {
return true;
@@ -1303,6 +1295,7 @@ public class QueryMapper {
*/
protected static class AssociationConverter implements Converter<MongoPersistentProperty, String> {
private final String name;
private final MongoPersistentProperty property;
private boolean associationFound;
@@ -1311,10 +1304,11 @@ public class QueryMapper {
*
* @param association must not be {@literal null}.
*/
public AssociationConverter(Association<MongoPersistentProperty> association) {
public AssociationConverter(String name, Association<MongoPersistentProperty> association) {
Assert.notNull(association, "Association must not be null!");
this.property = association.getInverse();
this.name = name;
}
/*
@@ -1332,6 +1326,12 @@ public class QueryMapper {
associationFound = true;
}
if (associationFound) {
if (name.endsWith("$") && property.isCollectionLike()) {
return source.getFieldName() + ".$";
}
}
return source.getFieldName();
}
}

View File

@@ -272,6 +272,7 @@ public class UpdateMapper extends QueryMapper {
*
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
*/
private static class MetadataBackedUpdateField extends MetadataBackedField {
@@ -289,7 +290,7 @@ public class UpdateMapper extends QueryMapper {
public MetadataBackedUpdateField(MongoPersistentEntity<?> entity, String key,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
super(key.replaceAll("\\.\\$(\\[.*\\])?", ""), entity, mappingContext);
super(key, entity, mappingContext);
this.key = key;
}
@@ -338,7 +339,7 @@ public class UpdateMapper extends QueryMapper {
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext,
Association<MongoPersistentProperty> association, String key) {
super(association);
super(key, association);
this.mapper = new KeyMapper(key, mappingContext);
}

View File

@@ -23,6 +23,7 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -394,7 +395,41 @@ public class Criteria implements CriteriaDefinition {
* @see <a href="https://docs.mongodb.com/manual/reference/operator/query/not/">MongoDB Query operator: $not</a>
*/
public Criteria not() {
return not(null);
return not((Object)null);
}
public Criteria not(Consumer<Criteria> source) {
Criteria sink = new Criteria(this.key);
source.accept(sink);
List<Criteria> target = new ArrayList<>();
for(Criteria criteria : sink.criteriaChain) {
boolean nextIsNot = false;
for(Entry<String, Object> entry : criteria.criteria.entrySet()) {
if(entry.getKey().equals("$not")) {
nextIsNot = true;
continue;
}
Criteria extractedSingleCriteria = new Criteria(criteria.key);
if(nextIsNot) {
extractedSingleCriteria = new Criteria("$not");
extractedSingleCriteria.criteria.put(criteria.key, new Document(entry.getKey(), entry.getValue()));
nextIsNot = false;
} else {
extractedSingleCriteria.criteria.put(entry.getKey(), entry.getValue());
}
target.add(extractedSingleCriteria);
}
}
List bsonList = createCriteriaList(target.toArray(new Criteria[0]), true);
return registerCriteriaChainElement(new Criteria("$and").is(bsonList));
}
public Criteria not(Criteria... criteria) {
List bsonList = createCriteriaList(criteria, true);
return registerCriteriaChainElement(new Criteria("$and").is(bsonList));
}
/**
@@ -660,7 +695,7 @@ public class Criteria implements CriteriaDefinition {
* @param criteria
*/
public Criteria orOperator(Criteria... criteria) {
BasicDBList bsonList = createCriteriaList(criteria);
List bsonList = createCriteriaList(criteria, false);
return registerCriteriaChainElement(new Criteria("$or").is(bsonList));
}
@@ -674,7 +709,7 @@ public class Criteria implements CriteriaDefinition {
* @param criteria
*/
public Criteria norOperator(Criteria... criteria) {
BasicDBList bsonList = createCriteriaList(criteria);
List bsonList = createCriteriaList(criteria, false);
return registerCriteriaChainElement(new Criteria("$nor").is(bsonList));
}
@@ -688,7 +723,7 @@ public class Criteria implements CriteriaDefinition {
* @param criteria
*/
public Criteria andOperator(Criteria... criteria) {
BasicDBList bsonList = createCriteriaList(criteria);
List bsonList = createCriteriaList(criteria, false);
return registerCriteriaChainElement(new Criteria("$and").is(bsonList));
}
@@ -775,16 +810,23 @@ public class Criteria implements CriteriaDefinition {
queryCriteria.put(this.key, this.isValue);
queryCriteria.putAll(document);
} else {
queryCriteria.put(this.key, document);
if(!document.isEmpty()) {
queryCriteria.put(this.key, document);
}
}
return queryCriteria;
}
private BasicDBList createCriteriaList(Criteria[] criteria) {
BasicDBList bsonList = new BasicDBList();
private List createCriteriaList(Criteria[] criteria, boolean not) {
List bsonList = new ArrayList();
for (Criteria c : criteria) {
bsonList.add(c.getCriteriaObject());
Document co = c.getCriteriaObject();
if(not) {
co = new Document("$not", co);
}
bsonList.add(co);
}
return bsonList;
}

View File

@@ -20,6 +20,7 @@ import java.util.stream.Collectors;
import org.bson.Document;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
@@ -71,6 +72,10 @@ public class StringBasedAggregation extends AbstractMongoQuery {
protected Object doExecute(MongoQueryMethod method, ResultProcessor resultProcessor,
ConvertingParameterAccessor accessor, Class<?> typeToRead) {
if (method.isPageQuery() || method.isSliceQuery()) {
throw new InvalidMongoDbApiUsageException(String.format("Repository aggregation method '%s' does not support '%s' return type. Please use eg. 'List' instead.", method.getName(), method.getReturnType().getType().getSimpleName()));
}
Class<?> sourceType = method.getDomainClass();
Class<?> targetType = typeToRead;

View File

@@ -38,7 +38,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
@@ -233,8 +232,8 @@ class DefaultBulkOperationsUnitTests {
verify(beforeSaveCallback).onBeforeSave(personArgumentCaptor.capture(), any(), eq("collection-1"));
verify(afterSaveCallback).onAfterSave(personArgumentCaptor.capture(), any(), eq("collection-1"));
assertThat(personArgumentCaptor.getAllValues()).extracting("firstName")
.containsExactly("init", "before-convert", "before-convert");
assertThat(personArgumentCaptor.getAllValues()).extracting("firstName").containsExactly("init", "before-convert",
"before-convert");
verify(collection).bulkWrite(captor.capture(), any());
InsertOneModel<Document> updateModel = (InsertOneModel<Document>) captor.getValue().get(0);
@@ -340,6 +339,52 @@ class DefaultBulkOperationsUnitTests {
.isEqualTo(new org.bson.Document("element", new Document("$gte", 100)));
}
@Test // DATAMONGO-2502
void shouldRetainNestedArrayPathWithPlaceholdersForNoMatchingPaths() {
ops.updateOne(new BasicQuery("{}"), new Update().set("items.$.documents.0.fileId", "new-id")).execute();
verify(collection).bulkWrite(captor.capture(), any());
UpdateOneModel<Document> updateModel = (UpdateOneModel<Document>) captor.getValue().get(0);
assertThat(updateModel.getUpdate())
.isEqualTo(new Document("$set", new Document("items.$.documents.0.fileId", "new-id")));
}
@Test // DATAMONGO-2502
void shouldRetainNestedArrayPathWithPlaceholdersForMappedEntity() {
DefaultBulkOperations ops = new DefaultBulkOperations(template, "collection-1",
new BulkOperationContext(BulkMode.ORDERED, Optional.of(mappingContext.getPersistentEntity(OrderTest.class)),
new QueryMapper(converter), new UpdateMapper(converter), null, null));
ops.updateOne(new BasicQuery("{}"), Update.update("items.$.documents.0.fileId", "file-id")).execute();
verify(collection).bulkWrite(captor.capture(), any());
UpdateOneModel<Document> updateModel = (UpdateOneModel<Document>) captor.getValue().get(0);
assertThat(updateModel.getUpdate())
.isEqualTo(new Document("$set", new Document("items.$.documents.0.the_file_id", "file-id")));
}
static class OrderTest {
String id;
List<OrderTestItem> items;
}
static class OrderTestItem {
private String cartId;
private List<OrderTestDocument> documents;
}
static class OrderTestDocument {
@Field("the_file_id")
private String fileId;
}
class SomeDomainType {
@Id String id;

View File

@@ -536,6 +536,16 @@ class ExecutableFindOperationSupportTests {
.isThrownBy(() -> template.query(Person.class).distinct("firstname").as(Long.class).all());
}
@Test // DATAMONGO-2507
void distinctAppliesFilterQuery() {
assertThat(template.query(Person.class).inCollection(STAR_WARS).distinct("firstname") //
.matching(where("lastname").is(luke.lastname)) //
.as(String.class) //
.all() //
).containsExactlyInAnyOrder("luke");
}
interface Contact {}
@Data

View File

@@ -59,6 +59,7 @@ import com.mongodb.client.MongoClient;
*
* @author Mark Paluch
* @author Christoph Strobl
* @author Juergen Zimmermann
*/
@ExtendWith(MongoClientExtension.class)
class ReactiveFindOperationSupportTests {
@@ -631,6 +632,17 @@ class ReactiveFindOperationSupportTests {
.verify();
}
@Test // DATAMONGO-2507
void distinctAppliesFilterQuery() {
template.query(Person.class).inCollection(STAR_WARS).distinct("firstname") //
.matching(where("lastname").is(luke.lastname)) //
.as(String.class) //
.all() //
.as(StepVerifier::create).consumeNextWith(it -> assertThat(it).isEqualTo("luke")) //
.verifyComplete();
}
interface Contact {}
@Data

View File

@@ -966,6 +966,32 @@ public class QueryMapperUnitTests {
assertThat(target).isEqualTo(new org.bson.Document("arrayCustomName.$[some_item].nes-ted.$[other_item]", "value"));
}
@Test // DATAMONGO-2502
void shouldAllowDeeplyNestedPlaceholders() {
org.bson.Document target = mapper.getMappedObject(
query(where("level0.$[some_item].arrayObj.$[other_item].nested").is("value")).getQueryObject(),
context.getPersistentEntity(WithDeepArrayNesting.class));
assertThat(target).isEqualTo(new org.bson.Document("level0.$[some_item].arrayObj.$[other_item].nested", "value"));
}
@Test // DATAMONGO-2502
void shouldAllowDeeplyNestedPlaceholdersWithCustomName() {
org.bson.Document target = mapper.getMappedObject(
query(where("level0.$[some_item].arrayCustomName.$[other_item].nested").is("value")).getQueryObject(),
context.getPersistentEntity(WithDeepArrayNesting.class));
assertThat(target)
.isEqualTo(new org.bson.Document("level0.$[some_item].arrayCustomName.$[other_item].nes-ted", "value"));
}
class WithDeepArrayNesting {
List<WithNestedArray> level0;
}
class WithNestedArray {
List<NestedArrayOfObj> arrayObj;

View File

@@ -19,6 +19,8 @@ import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import java.util.function.Consumer;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.ProxyFactory;
@@ -350,6 +352,44 @@ class QueryTests {
compareQueries(target, source);
}
@Test // DATAMONGO-2499
void notCombiningSeveralIndedependentCriterias() {
Query source = new Query(new Criteria().not(Criteria.where("age").gt(30), Criteria.where("age").lt(20)));
Document target = source.getQueryObject();
assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"$not\": {\"age\": {\"$gt\": 30}}}, {\"$not\": {\"age\": {\"$lt\": 20}}}]}"));
}
@Test // DATAMONGO-2499
void notCombiningSingleCriteria() {
Query source = new Query(Criteria.where("age").not(age -> age.gt(30).lt(20)));
Document target = source.getQueryObject();
assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"$not\": {\"age\": {\"$gt\": 30}}}, {\"$not\": {\"age\": {\"$lt\": 20}}}]}"));
}
@Test // DATAMONGO-2499
void notCombiningSingleCriteriaWithAnother() {
Query source = new Query(Criteria.where("age").not(age -> age.gt(30).lt(20)).exists(true));
Document target = source.getQueryObject();
assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"$not\": {\"age\": {\"$gt\": 30}}}, {\"$not\": {\"age\": {\"$lt\": 20}}}], \"age\": {\"$exists\": true}}"));
}
@Test // DATAMONGO-2499
void notCombiningSingleCriteriaWithNestedNot() {
Query source = new Query(Criteria.where("age").not(age -> age.gt(30).not().lt(20)));
Document target = source.getQueryObject();
assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"$not\": {\"age\": {\"$gt\": 30}}}, {\"$not\": { \"$not\": {\"age\": {\"$lt\": 20}}}}]}"));
}
private void compareQueries(Query actual, Query expected) {
assertThat(actual.getCollation()).isEqualTo(expected.getCollation());

View File

@@ -32,9 +32,9 @@ public class Address {
}
/**
* @param string
* @param string2
* @param string3
* @param street
* @param zipcode
* @param city
*/
public Address(String street, String zipcode, String city) {
this.street = street;

View File

@@ -394,4 +394,11 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
@Query(value = "{_id:?0}")
Optional<org.bson.Document> findDocumentById(String id);
@Query(value = "{ 'firstname' : ?0, 'lastname' : ?1, 'email' : ?2 , 'age' : ?3, 'sex' : ?4, "
+ "'createdAt' : ?5, 'skills' : ?6, 'address.street' : ?7, 'address.zipCode' : ?8, " //
+ "'address.city' : ?9, 'uniqueId' : ?10, 'credentials.username' : ?11, 'credentials.password' : ?12 }")
Person findPersonByManyArguments(String firstname, String lastname, String email, Integer age, Sex sex,
Date createdAt, List<String> skills, String street, String zipCode, //
String city, UUID uniqueId, String username, String password);
}

View File

@@ -19,8 +19,7 @@ import static org.assertj.core.api.Assertions.*;
import java.util.List;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
@@ -31,12 +30,12 @@ import org.springframework.test.context.ContextConfiguration;
* @author Mark Paluch
*/
@ContextConfiguration("config/MongoNamespaceIntegrationTests-context.xml")
public class RedeclaringRepositoryMethodsTests extends AbstractPersonRepositoryIntegrationTests {
class RedeclaringRepositoryMethodsTests extends AbstractPersonRepositoryIntegrationTests {
@Autowired RedeclaringRepositoryMethodsRepository repository;
@Test // DATAMONGO-760
public void adjustedWellKnownPagedFindAllMethodShouldReturnOnlyTheUserWithFirstnameOliverAugust() {
void adjustedWellKnownPagedFindAllMethodShouldReturnOnlyTheUserWithFirstnameOliverAugust() {
Page<Person> page = repository.findAll(PageRequest.of(0, 2));
@@ -45,7 +44,7 @@ public class RedeclaringRepositoryMethodsTests extends AbstractPersonRepositoryI
}
@Test // DATAMONGO-760
public void adjustedWllKnownFindAllMethodShouldReturnAnEmptyList() {
void adjustedWllKnownFindAllMethodShouldReturnAnEmptyList() {
List<Person> result = repository.findAll();

View File

@@ -17,9 +17,8 @@ package org.springframework.data.mongodb.repository.config;
import static org.assertj.core.api.Assertions.*;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReader;
@@ -40,14 +39,14 @@ import org.springframework.test.context.ContextConfiguration;
* @author Oliver Gierke
*/
@ContextConfiguration
public class MongoNamespaceIntegrationTests extends AbstractPersonRepositoryIntegrationTests {
class MongoNamespaceIntegrationTests extends AbstractPersonRepositoryIntegrationTests {
DefaultListableBeanFactory factory;
BeanDefinitionReader reader;
@Autowired ApplicationContext context;
@Before
@BeforeEach
@Override
public void setUp() throws InterruptedException {
super.setUp();
@@ -56,7 +55,7 @@ public class MongoNamespaceIntegrationTests extends AbstractPersonRepositoryInte
}
@Test
public void assertDefaultMappingContextIsWired() {
void assertDefaultMappingContextIsWired() {
reader.loadBeanDefinitions(new ClassPathResource("MongoNamespaceIntegrationTests-context.xml", getClass()));
BeanDefinition definition = factory.getBeanDefinition("personRepository");
@@ -64,7 +63,7 @@ public class MongoNamespaceIntegrationTests extends AbstractPersonRepositoryInte
}
@Test // DATAMONGO-581
public void exposesPersistentEntity() {
void exposesPersistentEntity() {
Repositories repositories = new Repositories(context);
PersistentEntity<?, ?> entity = repositories.getPersistentEntity(Person.class);

View File

@@ -34,9 +34,14 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.InvalidMongoDbApiUsageException;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
@@ -68,6 +73,7 @@ import org.springframework.util.ClassUtils;
* @author Mark Paluch
*/
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
public class StringBasedAggregationUnitTests {
SpelExpressionParser PARSER = new SpelExpressionParser();
@@ -202,6 +208,16 @@ public class StringBasedAggregationUnitTests {
assertThat(collationOf(invocation)).isEqualTo(Collation.of("en_US"));
}
@Test // DATAMONGO-2506
public void aggregateRaisesErrorOnInvalidReturnType() {
StringBasedAggregation sba = createAggregationForMethod("invalidPageReturnType", Pageable.class);
assertThatExceptionOfType(InvalidMongoDbApiUsageException.class) //
.isThrownBy(() -> sba.execute(new Object[] { PageRequest.of(0, 1) })) //
.withMessageContaining("invalidPageReturnType") //
.withMessageContaining("Page");
}
private AggregationInvocation executeAggregation(String name, Object... args) {
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
@@ -280,6 +296,9 @@ public class StringBasedAggregationUnitTests {
@Aggregation(pipeline = RAW_GROUP_BY_LASTNAME_STRING, collation = "de_AT")
PersonAggregate aggregateWithCollation(Collation collation);
@Aggregation(RAW_GROUP_BY_LASTNAME_STRING)
Page<Person> invalidPageReturnType(Pageable page);
}
static class PersonAggregate {