Compare commits
18 Commits
2.2.6.RELE
...
2.2.7.RELE
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61bbc9ab7f | ||
|
|
9e802a59c7 | ||
|
|
38e1e632a7 | ||
|
|
89cf78cc4a | ||
|
|
cecd47d679 | ||
|
|
ed35e577af | ||
|
|
f54cf40eda | ||
|
|
5314e6f8bb | ||
|
|
b7b2709177 | ||
|
|
34c47e84c0 | ||
|
|
f7d91184a0 | ||
|
|
eeddc860f7 | ||
|
|
bcefdd209b | ||
|
|
3f1fea2d19 | ||
|
|
665322a69a | ||
|
|
3e59bc3b38 | ||
|
|
1752931dde | ||
|
|
4b9bae1656 |
6
pom.xml
6
pom.xml
@@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<version>2.2.7.RELEASE</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.2.6.RELEASE</version>
|
||||
<version>2.2.7.RELEASE</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
@@ -26,7 +26,7 @@
|
||||
<properties>
|
||||
<project.type>multi</project.type>
|
||||
<dist.id>spring-data-mongodb</dist.id>
|
||||
<springdata.commons>2.2.6.RELEASE</springdata.commons>
|
||||
<springdata.commons>2.2.7.RELEASE</springdata.commons>
|
||||
<mongo>3.11.2</mongo>
|
||||
<mongo.reactivestreams>1.12.0</mongo.reactivestreams>
|
||||
<jmh.version>1.19</jmh.version>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<version>2.2.7.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<version>2.2.7.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-mongodb-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<version>2.2.7.RELEASE</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -3155,12 +3155,13 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
@Nullable
|
||||
public T doWith(@Nullable Document object) {
|
||||
|
||||
T source = null;
|
||||
|
||||
if (null != object) {
|
||||
maybeEmitEvent(new AfterLoadEvent<>(object, type, collectionName));
|
||||
source = reader.read(type, object);
|
||||
}
|
||||
|
||||
T source = reader.read(type, object);
|
||||
|
||||
if (null != source) {
|
||||
maybeEmitEvent(new AfterConvertEvent<>(object, source, collectionName));
|
||||
}
|
||||
@@ -3200,9 +3201,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
|
||||
Class<?> typeToRead = targetType.isInterface() || targetType.isAssignableFrom(entityType) ? entityType
|
||||
: targetType;
|
||||
|
||||
if (null != object) {
|
||||
maybeEmitEvent(new AfterLoadEvent<>(object, targetType, collectionName));
|
||||
}
|
||||
maybeEmitEvent(new AfterLoadEvent<>(object, targetType, collectionName));
|
||||
|
||||
Object source = reader.read(typeToRead, object);
|
||||
Object result = targetType.isInterface() ? projectionFactory.createProjection(targetType, source) : source;
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -100,14 +101,14 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
|
||||
return value;
|
||||
}
|
||||
|
||||
protected List<Object> append(Object value) {
|
||||
protected List<Object> append(Object value, Expand expandList) {
|
||||
|
||||
if (this.value instanceof List) {
|
||||
|
||||
List<Object> clone = new ArrayList<Object>((List) this.value);
|
||||
|
||||
if (value instanceof List) {
|
||||
clone.addAll((List) value);
|
||||
if (value instanceof Collection && Expand.EXPAND_VALUES.equals(expandList)) {
|
||||
clone.addAll((Collection<?>) value);
|
||||
} else {
|
||||
clone.add(value);
|
||||
}
|
||||
@@ -117,6 +118,17 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
|
||||
return Arrays.asList(this.value, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand a nested list of values to single entries or keep the list.
|
||||
*/
|
||||
protected enum Expand {
|
||||
EXPAND_VALUES, KEEP_SOURCE
|
||||
}
|
||||
|
||||
protected List<Object> append(Object value) {
|
||||
return append(value, Expand.EXPAND_VALUES);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected java.util.Map<String, Object> append(String key, Object value) {
|
||||
|
||||
|
||||
@@ -411,7 +411,7 @@ public class ComparisonOperators {
|
||||
public Cmp compareToValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Cmp(append(value));
|
||||
return new Cmp(append(value, Expand.KEEP_SOURCE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +488,7 @@ public class ComparisonOperators {
|
||||
public Eq equalToValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Eq(append(value));
|
||||
return new Eq(append(value, Expand.KEEP_SOURCE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,7 +873,7 @@ public class ComparisonOperators {
|
||||
public Ne notEqualToValue(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return new Ne(append(value));
|
||||
return new Ne(append(value, Expand.KEEP_SOURCE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,11 +228,9 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
private <S extends Object> S read(TypeInformation<S> type, @Nullable Bson bson, ObjectPath path) {
|
||||
private <S extends Object> S read(TypeInformation<S> type, Bson bson, ObjectPath path) {
|
||||
|
||||
if (null == bson) {
|
||||
return null;
|
||||
}
|
||||
Assert.notNull(bson, "Bson must not be null!");
|
||||
|
||||
TypeInformation<? extends S> typeToUse = typeMapper.readType(bson, type);
|
||||
Class<? extends S> rawType = typeToUse.getType();
|
||||
@@ -1263,9 +1261,16 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
}
|
||||
|
||||
if (conversions.isSimpleType(obj.getClass())) {
|
||||
// Doesn't need conversion
|
||||
return getPotentiallyConvertedSimpleWrite(obj,
|
||||
typeInformation != null ? typeInformation.getType() : Object.class);
|
||||
|
||||
Class<?> conversionTargetType;
|
||||
|
||||
if (typeInformation != null && conversions.isSimpleType(typeInformation.getType())) {
|
||||
conversionTargetType = typeInformation.getType();
|
||||
} else {
|
||||
conversionTargetType = Object.class;
|
||||
}
|
||||
|
||||
return getPotentiallyConvertedSimpleWrite(obj, conversionTargetType);
|
||||
}
|
||||
|
||||
if (obj instanceof List) {
|
||||
@@ -1613,17 +1618,19 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
|
||||
|
||||
for (Document document : referencedRawDocuments) {
|
||||
|
||||
T target = null;
|
||||
if (document != null) {
|
||||
|
||||
maybeEmitEvent(
|
||||
new AfterLoadEvent<>(document, (Class<T>) (rawType != null ? rawType : Object.class), collectionName));
|
||||
target = (T) read(type, document, path);
|
||||
}
|
||||
|
||||
final T target = (T) read(type, document, path);
|
||||
targeList.add(target);
|
||||
|
||||
if (target != null) {
|
||||
maybeEmitEvent(new AfterConvertEvent<>(document, target, collectionName));
|
||||
}
|
||||
|
||||
targeList.add(target);
|
||||
}
|
||||
|
||||
return targeList;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.query;
|
||||
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A {@link Term} defines one or multiple words {@link Type#WORD} or phrases {@link Type#PHRASE} to be used in the
|
||||
@@ -90,6 +93,47 @@ public class Term {
|
||||
return negated ? negateRaw(formatted) : formatted;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof Term)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Term term = (Term) o;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(negated, term.negated) && ObjectUtils.nullSafeEquals(type, term.type)
|
||||
&& ObjectUtils.nullSafeEquals(raw, term.raw);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
|
||||
result += ObjectUtils.nullSafeHashCode(type);
|
||||
result += ObjectUtils.nullSafeHashCode(raw);
|
||||
result += ObjectUtils.nullSafeHashCode(negated);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getFormatted();
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
import org.bson.Document;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -34,7 +35,7 @@ import org.springframework.util.StringUtils;
|
||||
public class TextCriteria implements CriteriaDefinition {
|
||||
|
||||
private final List<Term> terms;
|
||||
private @Nullable String language;
|
||||
private final @Nullable String language;
|
||||
private @Nullable Boolean caseSensitive;
|
||||
private @Nullable Boolean diacriticSensitive;
|
||||
|
||||
@@ -51,7 +52,7 @@ public class TextCriteria implements CriteriaDefinition {
|
||||
private TextCriteria(@Nullable String language) {
|
||||
|
||||
this.language = language;
|
||||
this.terms = new ArrayList<Term>();
|
||||
this.terms = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,9 +232,47 @@ public class TextCriteria implements CriteriaDefinition {
|
||||
return new Document("$text", document);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof TextCriteria)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TextCriteria that = (TextCriteria) o;
|
||||
|
||||
return ObjectUtils.nullSafeEquals(terms, that.terms) && ObjectUtils.nullSafeEquals(language, that.language)
|
||||
&& ObjectUtils.nullSafeEquals(caseSensitive, that.caseSensitive)
|
||||
&& ObjectUtils.nullSafeEquals(diacriticSensitive, that.diacriticSensitive);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
|
||||
result += ObjectUtils.nullSafeHashCode(terms);
|
||||
result += ObjectUtils.nullSafeHashCode(language);
|
||||
result += ObjectUtils.nullSafeHashCode(caseSensitive);
|
||||
result += ObjectUtils.nullSafeHashCode(diacriticSensitive);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String join(Iterable<Term> terms) {
|
||||
|
||||
List<String> result = new ArrayList<String>();
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
for (Term term : terms) {
|
||||
if (term != null) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@ class JsonScanner {
|
||||
|
||||
parenthesisCount--;
|
||||
if (parenthesisCount == 0) {
|
||||
buffer.read();
|
||||
c = buffer.read();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,6 +333,52 @@ public class DefaultBulkOperationsUnitTests {
|
||||
.isEqualTo(new org.bson.Document("element", new Document("$gte", 100)));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2502
|
||||
public 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
|
||||
public 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;
|
||||
|
||||
@@ -1499,6 +1499,15 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat(agg).isEqualTo(Document.parse("{ $project: { eq250: { $eq: [\"$qty\", 250]} } }"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2513
|
||||
public void shouldRenderEqAggregationExpressionWithListComparison() {
|
||||
|
||||
Document agg = project().and(ComparisonOperators.valueOf("qty").equalToValue(Arrays.asList(250))).as("eq250")
|
||||
.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg).isEqualTo(Document.parse("{ $project: { eq250: { $eq: [\"$qty\", [250]]} } }"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1536
|
||||
public void shouldRenderGtAggregationExpression() {
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.springframework.data.mongodb.core.mapping.TextScore;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.TextQuery;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
@@ -967,6 +968,42 @@ public class QueryMapperUnitTests {
|
||||
assertThat(target).isEqualTo(new org.bson.Document("arrayCustomName.$[some_item].nes-ted.$[other_item]", "value"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2502
|
||||
public 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
|
||||
public 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"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2517
|
||||
public void shouldParseNestedKeywordWithArgumentMatchingTheSourceEntitiesConstructorCorrectly() {
|
||||
|
||||
TextQuery source = new TextQuery("test");
|
||||
|
||||
org.bson.Document target = mapper.getMappedObject(source.getQueryObject(),
|
||||
context.getPersistentEntity(WithSingleStringArgConstructor.class));
|
||||
assertThat(target).isEqualTo(org.bson.Document.parse("{\"$text\" : { \"$search\" : \"test\" }}"));
|
||||
}
|
||||
|
||||
class WithDeepArrayNesting {
|
||||
|
||||
List<WithNestedArray> level0;
|
||||
}
|
||||
|
||||
class WithNestedArray {
|
||||
|
||||
List<NestedArrayOfObj> arrayObj;
|
||||
@@ -1133,4 +1170,16 @@ public class QueryMapperUnitTests {
|
||||
String id;
|
||||
@Field(targetType = FieldType.OBJECT_ID) String stringAsOid;
|
||||
}
|
||||
|
||||
@Document
|
||||
static class WithSingleStringArgConstructor {
|
||||
|
||||
String value;
|
||||
|
||||
public WithSingleStringArgConstructor() {}
|
||||
|
||||
public WithSingleStringArgConstructor(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.data.mongodb.core.DocumentTestUtils;
|
||||
* Unit tests for {@link TextCriteria}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Daniel Debray
|
||||
*/
|
||||
public class TextCriteriaUnitTests {
|
||||
|
||||
@@ -33,6 +34,7 @@ public class TextCriteriaUnitTests {
|
||||
public void shouldNotHaveLanguageField() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage();
|
||||
|
||||
assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ }"));
|
||||
}
|
||||
|
||||
@@ -40,6 +42,7 @@ public class TextCriteriaUnitTests {
|
||||
public void shouldNotHaveLanguageForNonDefaultLanguageField() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forLanguage("spanish");
|
||||
|
||||
assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$language\" : \"spanish\" }"));
|
||||
}
|
||||
|
||||
@@ -47,6 +50,7 @@ public class TextCriteriaUnitTests {
|
||||
public void shouldCreateSearchFieldForSingleTermCorrectly() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage().matching("cake");
|
||||
|
||||
assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$search\" : \"cake\" }"));
|
||||
}
|
||||
|
||||
@@ -54,6 +58,7 @@ public class TextCriteriaUnitTests {
|
||||
public void shouldCreateSearchFieldCorrectlyForMultipleTermsCorrectly() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingAny("bake", "coffee", "cake");
|
||||
|
||||
assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$search\" : \"bake coffee cake\" }"));
|
||||
}
|
||||
|
||||
@@ -61,6 +66,7 @@ public class TextCriteriaUnitTests {
|
||||
public void shouldCreateSearchFieldForPhraseCorrectly() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage().matchingPhrase("coffee cake");
|
||||
|
||||
assertThat(DocumentTestUtils.getAsDocument(criteria.getCriteriaObject(), "$text"))
|
||||
.isEqualTo(new Document("$search", "\"coffee cake\""));
|
||||
}
|
||||
@@ -69,6 +75,7 @@ public class TextCriteriaUnitTests {
|
||||
public void shouldCreateNotFieldCorrectly() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage().notMatching("cake");
|
||||
|
||||
assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$search\" : \"-cake\" }"));
|
||||
}
|
||||
|
||||
@@ -76,6 +83,7 @@ public class TextCriteriaUnitTests {
|
||||
public void shouldCreateSearchFieldCorrectlyForNotMultipleTermsCorrectly() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage().notMatchingAny("bake", "coffee", "cake");
|
||||
|
||||
assertThat(criteria.getCriteriaObject()).isEqualTo(searchObject("{ \"$search\" : \"-bake -coffee -cake\" }"));
|
||||
}
|
||||
|
||||
@@ -83,6 +91,7 @@ public class TextCriteriaUnitTests {
|
||||
public void shouldCreateSearchFieldForNotPhraseCorrectly() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage().notMatchingPhrase("coffee cake");
|
||||
|
||||
assertThat(DocumentTestUtils.getAsDocument(criteria.getCriteriaObject(), "$text"))
|
||||
.isEqualTo(new Document("$search", "-\"coffee cake\""));
|
||||
}
|
||||
@@ -91,6 +100,7 @@ public class TextCriteriaUnitTests {
|
||||
public void caseSensitiveOperatorShouldBeSetCorrectly() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage().matching("coffee").caseSensitive(true);
|
||||
|
||||
assertThat(DocumentTestUtils.getAsDocument(criteria.getCriteriaObject(), "$text"))
|
||||
.isEqualTo(new Document("$search", "coffee").append("$caseSensitive", true));
|
||||
}
|
||||
@@ -99,10 +109,23 @@ public class TextCriteriaUnitTests {
|
||||
public void diacriticSensitiveOperatorShouldBeSetCorrectly() {
|
||||
|
||||
TextCriteria criteria = TextCriteria.forDefaultLanguage().matching("coffee").diacriticSensitive(true);
|
||||
|
||||
assertThat(DocumentTestUtils.getAsDocument(criteria.getCriteriaObject(), "$text"))
|
||||
.isEqualTo(new Document("$search", "coffee").append("$diacriticSensitive", true));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2504
|
||||
public void twoIdenticalCriteriaShouldBeEqual() {
|
||||
|
||||
TextCriteria criteriaOne = TextCriteria.forDefaultLanguage().matching("coffee");
|
||||
TextCriteria criteriaTwo = TextCriteria.forDefaultLanguage().matching("coffee");
|
||||
|
||||
assertThat(criteriaOne).isEqualTo(criteriaTwo);
|
||||
assertThat(criteriaOne).hasSameHashCodeAs(criteriaTwo);
|
||||
assertThat(criteriaOne).isNotEqualTo(criteriaTwo.diacriticSensitive(false));
|
||||
assertThat(criteriaOne.hashCode()).isNotEqualTo(criteriaTwo.diacriticSensitive(false).hashCode());
|
||||
}
|
||||
|
||||
private Document searchObject(String json) {
|
||||
return new Document("$text", Document.parse(json));
|
||||
}
|
||||
|
||||
@@ -34,8 +34,12 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
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;
|
||||
@@ -198,6 +202,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);
|
||||
@@ -276,6 +290,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 {
|
||||
|
||||
@@ -231,7 +231,7 @@ public class ParameterBindingJsonReaderUnitTests {
|
||||
public void bindMultipleUnquotedParameterInArray() {
|
||||
|
||||
Document target = parse("{ 'name' : { $in : [?0,?1] } }", "dalinar", "kohlin");
|
||||
assertThat(target).isEqualTo(new Document("name", new Document("$in",Arrays.asList("dalinar", "kohlin"))));
|
||||
assertThat(target).isEqualTo(new Document("name", new Document("$in", Arrays.asList("dalinar", "kohlin"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2476
|
||||
@@ -252,7 +252,16 @@ public class ParameterBindingJsonReaderUnitTests {
|
||||
public void bindQuotedMulitParameterInArray() {
|
||||
|
||||
Document target = parse("{ 'name' : { $in : ['?0,?1'] } }", "dalinar", "kohlin");
|
||||
assertThat(target).isEqualTo(new Document("name", new Document("$in", Collections.singletonList("dalinar,kohlin"))));
|
||||
assertThat(target)
|
||||
.isEqualTo(new Document("name", new Document("$in", Collections.singletonList("dalinar,kohlin"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2523
|
||||
public void bindSpelExpressionInArrayCorrectly/* closing bracket must not have leading whitespace! */() {
|
||||
|
||||
Document target = parse("{ $and : [?#{ [0] == null ? { '$where' : 'true' } : { 'v1' : { '$in' : {[0]} } } }]}", 1);
|
||||
|
||||
assertThat(target).isEqualTo(Document.parse("{\"$and\": [{\"v1\": {\"$in\": [1]}}]}"));
|
||||
}
|
||||
|
||||
private static Document parse(String json, Object... args) {
|
||||
|
||||
@@ -1,6 +1,43 @@
|
||||
Spring Data MongoDB Changelog
|
||||
=============================
|
||||
|
||||
Changes in version 2.2.7.RELEASE (2020-04-28)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-2529 - EntityReader called with null argument.
|
||||
* DATAMONGO-2523 - ParameterBindingJsonReader skips too many chars.
|
||||
* DATAMONGO-2517 - Text search fails on entity with second constructor.
|
||||
* DATAMONGO-2513 - ComparisonOperators.Eq#equalToValue doesn't work well with Lists.
|
||||
* DATAMONGO-2506 - StringBasedAggregation should raise meaningful error when unsupported return type requested.
|
||||
* DATAMONGO-2504 - TextCriteria Hashcode and equals.
|
||||
* DATAMONGO-2502 - Regression in Update property mapping.
|
||||
* DATAMONGO-2500 - Release 2.2.7 (Moore SR7).
|
||||
|
||||
|
||||
Changes in version 2.1.17.RELEASE (2020-04-28)
|
||||
----------------------------------------------
|
||||
* DATAMONGO-2529 - EntityReader called with null argument.
|
||||
* DATAMONGO-2497 - Update documentation regarding @Transient properties usage in the persistence constructor.
|
||||
* DATAMONGO-2484 - Release 2.1.17 (Lovelace SR17).
|
||||
* DATAMONGO-2300 - Can't read and convert DBRef when the type is Map.
|
||||
|
||||
|
||||
Changes in version 3.0.0.RC1 (2020-03-31)
|
||||
-----------------------------------------
|
||||
* DATAMONGO-2501 - Upgrade to Querydsl 4.3.
|
||||
* DATAMONGO-2498 - Upgrade to MongoDB 4.0.1 Drivers.
|
||||
* DATAMONGO-2497 - Update documentation regarding @Transient properties usage in the persistence constructor.
|
||||
* DATAMONGO-2492 - Release 3.0 RC1 (Neumann).
|
||||
* DATAMONGO-2488 - KeyMapper.mapPropertyName does not work for nested arrays.
|
||||
* DATAMONGO-2479 - More EntityCallback specializations.
|
||||
* DATAMONGO-2477 - Disable auto-index creation by default.
|
||||
* DATAMONGO-2475 - QueryDSL: $and and $or with more than two arguments in final query document.
|
||||
* DATAMONGO-2416 - Add default methods accepting CriteriaDefinition on Fluent API.
|
||||
* DATAMONGO-2300 - Can't read and convert DBRef when the type is Map.
|
||||
* DATAMONGO-1026 - Joda, JSR-310 and ThreeTenBp converters are timezone-sensitive.
|
||||
* DATAMONGO-931 - Add support for $redact operation.
|
||||
* DATAMONGO-625 - GridFsOperations.createFile method should allow specification of _id.
|
||||
|
||||
|
||||
Changes in version 2.2.6.RELEASE (2020-03-25)
|
||||
---------------------------------------------
|
||||
* DATAMONGO-2497 - Update documentation regarding @Transient properties usage in the persistence constructor.
|
||||
@@ -2960,3 +2997,6 @@ Repository
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data MongoDB 2.2.6
|
||||
Spring Data MongoDB 2.2.7
|
||||
Copyright (c) [2010-2019] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -12,3 +12,4 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user