Compare commits

..

33 Commits

Author SHA1 Message Date
Mark Paluch
74c08fa8aa DATAMONGO-2485 - Release version 2.2.6 (Moore SR6). 2020-03-25 10:46:02 +01:00
Mark Paluch
628aad8f64 DATAMONGO-2485 - Prepare 2.2.6 (Moore SR6). 2020-03-25 10:45:35 +01:00
Mark Paluch
39c8672e6d DATAMONGO-2485 - Updated changelog. 2020-03-25 10:45:28 +01:00
Christoph Strobl
620991ddee DATAMONGO-2300 - Polishing.
Move null check to event publishing logic.

Original Pull Request: #763
2020-03-23 10:11:41 +01:00
Heesu Jung
ba8f28f623 DATAMONGO-2300 - Add check rawType is null in readMap.
Original Pull Request: #763
2020-03-23 10:01:50 +01:00
Mark Paluch
6389055d3a DATAMONGO-2497 - Update documentation regarding @Transient properties usage in the persistence constructor. 2020-03-19 15:37:37 +01:00
Christoph Strobl
4465ed9819 DATAMONGO-2445 - Deprecate ReactiveGridFsOperations using AsyncInputStream.
Methods using AsyncInputStream will be removed in 3.0. Please use the ones accepting a Publisher.

Original pull request: #843.
2020-03-19 09:42:49 +01:00
Mark Paluch
8dc97e5d01 DATAMONGO-2488 - Polishing.
Simplify conditional entity check.

Original pull request: #841.
2020-03-11 14:38:17 +01:00
Christoph Strobl
a037c50961 DATAMONGO-2488 - Fix nested array path field name mapping.
Original pull request: #841.
2020-03-11 14:38:16 +01:00
Jens Schauder
28d5f02e15 DATAMONGO-2473 - Updated changelog. 2020-03-11 09:59:36 +01:00
Mark Paluch
e65a353fc4 DATAMONGO-2453 - After release cleanups. 2020-02-26 11:54:09 +01:00
Mark Paluch
42400e7836 DATAMONGO-2453 - Prepare next development iteration. 2020-02-26 11:54:08 +01:00
Mark Paluch
cdd7a2008b DATAMONGO-2453 - Release version 2.2.5 (Moore SR5). 2020-02-26 11:38:17 +01:00
Mark Paluch
c2b80fddd8 DATAMONGO-2453 - Prepare 2.2.5 (Moore SR5). 2020-02-26 11:37:58 +01:00
Mark Paluch
8eaa8119e6 DATAMONGO-2453 - Updated changelog. 2020-02-26 11:37:53 +01:00
Mark Paluch
5fccadd41e DATAMONGO-2452 - Updated changelog. 2020-02-26 11:31:49 +01:00
Christoph Strobl
5d0ab340e3 DATAMONGO-2478 - Fix NPE in Query.of when given a proxied source.
Original pull request: #836.
2020-02-24 11:34:36 +01:00
Christoph Strobl
5d7e9199de DATAMONGO-2476 - Fix Json parsing for unquoted placeholders in arrays.
Original pull request: #835.
2020-02-24 11:07:56 +01:00
Mark Paluch
5e2c65a650 DATAMONGO-2456 - Updated changelog. 2020-02-12 15:05:04 +01:00
Mark Paluch
1f5553d2d8 DATAMONGO-2079 - MappingMongoConverter no longer implements ValueResolver.
MappingMongoConverter no longer implements a package-private interface so that converter instances can be proxied.

Original Pull Request: #832
2020-02-04 14:52:43 +01:00
Mark Paluch
40d5ab050f DATAMONGO-2464 - Polishing.
Apply fix also to reactive MongoDB repository documentation.

Original pull request: #816.
2020-02-03 11:33:42 +01:00
LiangYong
1629ba11b2 DATAMONGO-2464 - Fix code examples in reference documentation.
fixed repository miss "{" issue.

Original pull request: #816.
2020-02-03 11:31:33 +01:00
Mark Paluch
6e94f138d5 DATAMONGO-2460 - Polishing.
Reformat code. Use diamond syntax.

Original pull request: #830.
2020-02-03 11:25:36 +01:00
Christoph Strobl
1b7273db42 DATAMONGO-2460 - Fix target type computation for complex id properties with @Field annotation.
We now set the target type to org.bson.Document for id properties annotated with @Field having the implicit target type derived from the annotation. Along the lines we fixed warn message when an id property with explicit (unsupported) field name is detected.

Original pull request: #830.
2020-02-03 11:25:36 +01:00
Mark Paluch
8857903831 DATAMONGO-2406 - Polishing.
Add optimization for Mono.

Original pull request: #825.
2020-01-29 09:58:14 +01:00
Christoph Strobl
69cacb5fe3 DATAMONGO-2406 - Derived reactive deleteBy query execution should allow Mono<Void> result.
Mono<Void> is now a supported return type of derived reactive deleteBy queries like:

    Mono<Void> deleteByLastname(String lastname);

Original pull request: #825.
2020-01-29 09:58:14 +01:00
Mark Paluch
ba4b958114 DATAMONGO-2457 - Polishing.
Slightly tweak wording.

Original pull request: #829.
2020-01-29 09:52:02 +01:00
Christoph Strobl
6d971ef2c8 DATAMONGO-2459 - Add sample for passing on limit and offset using reactive repositories.
Original pull request: #829.
2020-01-29 09:46:43 +01:00
Christoph Strobl
03ff37db92 DATAMONGO-2457 - Fix id type explanation in repository documentation.
Original pull request: #829.
2020-01-29 09:46:41 +01:00
Mark Paluch
428126ef75 DATAMONGO-2454 - Updated changelog. 2020-01-17 09:58:36 +01:00
Mark Paluch
aee242c52a DATAMONGO-2383 - Updated changelog. 2020-01-16 16:12:40 +01:00
Mark Paluch
1299f78e80 DATAMONGO-2432 - After release cleanups. 2020-01-15 12:48:23 +01:00
Mark Paluch
fc3d13d5bc DATAMONGO-2432 - Prepare next development iteration. 2020-01-15 12:48:22 +01:00
24 changed files with 408 additions and 53 deletions

View File

@@ -5,7 +5,7 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>2.2.4.RELEASE</version>
<version>2.2.6.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.4.RELEASE</version>
<version>2.2.6.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.4.RELEASE</springdata.commons>
<springdata.commons>2.2.6.RELEASE</springdata.commons>
<mongo>3.11.2</mongo>
<mongo.reactivestreams>1.12.0</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>2.2.4.RELEASE</version>
<version>2.2.6.RELEASE</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>2.2.4.RELEASE</version>
<version>2.2.6.RELEASE</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>2.2.4.RELEASE</version>
<version>2.2.6.RELEASE</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@@ -90,8 +90,9 @@ import com.mongodb.DBRef;
* @author Christoph Strobl
* @author Jordi Llach
* @author Mark Paluch
* @author Heesu Jung
*/
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware, ValueResolver {
public class MappingMongoConverter extends AbstractMongoConverter implements ApplicationContextAware {
private static final String INCOMPATIBLE_TYPES = "Cannot convert %1$s of type %2$s into an instance of %3$s! Implement a custom Converter<%2$s, %3$s> and register it with the CustomConversions. Parent object was: %4$s";
private static final String INVALID_TYPE_TO_READ = "Expected to read Document %s into type %s but didn't find a PersistentEntity for the latter!";
@@ -131,7 +132,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
this.idMapper = new QueryMapper(this);
this.spELContext = new SpELContext(DocumentPropertyAccessor.INSTANCE);
this.dbRefProxyHandler = new DefaultDbRefProxyHandler(spELContext, mappingContext, MappingMongoConverter.this);
this.dbRefProxyHandler = new DefaultDbRefProxyHandler(spELContext, mappingContext,
MappingMongoConverter.this::getValueInternal);
}
/**
@@ -431,7 +433,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
SpELExpressionEvaluator evaluator) {
return new DefaultDbRefResolverCallback(documentAccessor.getDocument(), currentPath, evaluator,
MappingMongoConverter.this);
MappingMongoConverter.this::getValueInternal);
}
private void readAssociation(Association<MongoPersistentProperty> association, PersistentPropertyAccessor<?> accessor,
@@ -1047,7 +1049,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.convert.ValueResolver#getValueInternal(org.springframework.data.mongodb.core.mapping.MongoPersistentProperty, com.mongodb.Document, org.springframework.data.mapping.model.SpELExpressionEvaluator, java.lang.Object)
*/
@Override
@Nullable
public Object getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
ObjectPath path) {
return new MongoDbPropertyValueProvider(bson, evaluator, path).getPropertyValue(prop);
@@ -1491,7 +1493,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
}
DbRefResolverCallback callback = new DefaultDbRefResolverCallback(accessor.getDocument(), path, evaluator,
MappingMongoConverter.this);
MappingMongoConverter.this::getValueInternal);
DBRef dbref = rawRefValue instanceof DBRef ? (DBRef) rawRefValue : null;
return (T) dbRefResolver.resolveDbRef(property, dbref, callback, dbRefProxyHandler);
@@ -1573,7 +1575,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
@Nullable
private <T> T readAndConvertDBRef(@Nullable DBRef dbref, TypeInformation<?> type, ObjectPath path,
final Class<?> rawType) {
@Nullable Class<?> rawType) {
List<T> result = bulkReadAndConvertDBRefs(Collections.singletonList(dbref), type, path, rawType);
return CollectionUtils.isEmpty(result) ? null : result.iterator().next();
@@ -1596,7 +1598,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
@SuppressWarnings("unchecked")
private <T> List<T> bulkReadAndConvertDBRefs(List<DBRef> dbrefs, TypeInformation<?> type, ObjectPath path,
final Class<?> rawType) {
@Nullable Class<?> rawType) {
if (CollectionUtils.isEmpty(dbrefs)) {
return Collections.emptyList();
@@ -1612,7 +1614,8 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
for (Document document : referencedRawDocuments) {
if (document != null) {
maybeEmitEvent(new AfterLoadEvent<>(document, (Class<T>) rawType, collectionName));
maybeEmitEvent(
new AfterLoadEvent<>(document, (Class<T>) (rawType != null ? rawType : Object.class), collectionName));
}
final T target = (T) read(type, document, path);

View File

@@ -24,7 +24,6 @@ import org.bson.BsonValue;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.domain.Example;
@@ -175,7 +174,7 @@ public class QueryMapper {
}
Document mappedSort = new Document();
for(Map.Entry<String,Object> entry : BsonUtils.asMap(sortObject).entrySet()) {
for (Map.Entry<String, Object> entry : BsonUtils.asMap(sortObject).entrySet()) {
Field field = createPropertyField(entity, entry.getKey(), mappingContext);
mappedSort.put(field.getMappedKey(), entry.getValue());
@@ -1158,7 +1157,7 @@ public class QueryMapper {
* @return
*/
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
return new PositionParameterRetainingPropertyKeyConverter(name);
return new PositionParameterRetainingPropertyKeyConverter(name, mappingContext);
}
/**
@@ -1172,6 +1171,10 @@ public class QueryMapper {
return new AssociationConverter(getAssociation());
}
protected MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> getMappingContext() {
return mappingContext;
}
/**
* @author Christoph Strobl
* @since 1.8
@@ -1180,8 +1183,9 @@ public class QueryMapper {
private final KeyMapper keyMapper;
public PositionParameterRetainingPropertyKeyConverter(String rawKey) {
this.keyMapper = new KeyMapper(rawKey);
public PositionParameterRetainingPropertyKeyConverter(String rawKey,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> ctx) {
this.keyMapper = new KeyMapper(rawKey, ctx);
}
/*
@@ -1222,11 +1226,14 @@ public class QueryMapper {
static class KeyMapper {
private final Iterator<String> iterator;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
public KeyMapper(String key) {
public KeyMapper(String key,
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
this.iterator = Arrays.asList(key.split("\\.")).iterator();
this.iterator.next();
this.mappingContext = mappingContext;
}
/**
@@ -1240,9 +1247,21 @@ 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) {
@@ -1250,6 +1269,7 @@ public class QueryMapper {
}
inspect = isPositional && iterator.hasNext();
depth++;
}
return mappedName.toString();

View File

@@ -308,7 +308,7 @@ public class UpdateMapper extends QueryMapper {
*/
@Override
protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
return new PositionParameterRetainingPropertyKeyConverter(key);
return new PositionParameterRetainingPropertyKeyConverter(key, getMappingContext());
}
/*
@@ -317,7 +317,7 @@ public class UpdateMapper extends QueryMapper {
*/
@Override
protected Converter<MongoPersistentProperty, String> getAssociationConverter() {
return new UpdateAssociationConverter(getAssociation(), key);
return new UpdateAssociationConverter(getMappingContext(), getAssociation(), key);
}
/**
@@ -334,10 +334,12 @@ public class UpdateMapper extends QueryMapper {
*
* @param association must not be {@literal null}.
*/
public UpdateAssociationConverter(Association<MongoPersistentProperty> association, String key) {
public UpdateAssociationConverter(
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext,
Association<MongoPersistentProperty> association, String key) {
super(association);
this.mapper = new KeyMapper(key);
this.mapper = new KeyMapper(key, mappingContext);
}
/*

View File

@@ -19,12 +19,14 @@ import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.lang.Nullable;
/**
* Internal API to trigger the resolution of properties.
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
*/
interface ValueResolver {
@@ -38,6 +40,6 @@ interface ValueResolver {
* @param parent
* @return
*/
Object getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator,
ObjectPath path);
@Nullable
Object getValueInternal(MongoPersistentProperty prop, Bson bson, SpELExpressionEvaluator evaluator, ObjectPath path);
}

View File

@@ -79,8 +79,14 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
this.fieldNamingStrategy = fieldNamingStrategy == null ? PropertyNameFieldNamingStrategy.INSTANCE
: fieldNamingStrategy;
if (isIdProperty() && getFieldName() != ID_FIELD_NAME) {
LOG.warn("Customizing field name for id property not allowed! Custom name will not be considered!");
if (isIdProperty() && hasExplicitFieldName()) {
String annotatedName = getAnnotatedFieldName();
if (!ID_FIELD_NAME.equals(annotatedName)) {
LOG.warn(
"Customizing field name for id property '{}.{}' is not allowed! Custom name ('{}') will not be considered!",
owner.getName(), getName(), annotatedName);
}
}
}
@@ -167,6 +173,11 @@ public class BasicMongoPersistentProperty extends AnnotationBasedPersistentPrope
FieldType fieldType = fieldAnnotation.targetType();
if (fieldType == FieldType.IMPLICIT) {
if (isEntity()) {
return org.bson.Document.class;
}
return getType();
}

View File

@@ -547,13 +547,11 @@ public class Query {
}
};
target.criteria.putAll(source.criteria);
target.skip = source.skip;
target.limit = source.limit;
target.sort = Sort.unsorted().and(source.sort);
target.hint = source.hint;
target.collation = source.collation;
target.restrictedTypes.addAll(source.restrictedTypes);
target.skip = source.getSkip();
target.limit = source.getLimit();
target.hint = source.getHint();
target.collation = source.getCollation();
target.restrictedTypes.addAll(source.getRestrictedTypes());
if (source.getMeta().hasValues()) {
target.setMeta(new Meta(source.getMeta()));

View File

@@ -111,7 +111,10 @@ public interface ReactiveGridFsOperations {
* @param metadata can be {@literal null}
* @return a {@link Mono} emitting the {@link ObjectId} of the {@link com.mongodb.client.gridfs.model.GridFSFile} just
* created.
* @deprecated since 2.2.6. Will be removed in 3.0. Please use {@link #store(Publisher, String, String, Object)}
* instead.
*/
@Deprecated
Mono<ObjectId> store(AsyncInputStream content, @Nullable String filename, @Nullable String contentType,
@Nullable Object metadata);
@@ -151,7 +154,10 @@ public interface ReactiveGridFsOperations {
* @param metadata can be {@literal null}.
* @return a {@link Mono} emitting the {@link ObjectId} of the {@link com.mongodb.client.gridfs.model.GridFSFile} just
* created.
* @deprecated since 2.2.6. Will be removed in 3.0. Please use {@link #store(Publisher, String, String, Document)}
* instead.
*/
@Deprecated
Mono<ObjectId> store(AsyncInputStream content, @Nullable String filename, @Nullable String contentType,
@Nullable Document metadata);

View File

@@ -18,6 +18,9 @@ package org.springframework.data.mongodb.repository.query;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.reactivestreams.Publisher;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.EntityInstantiators;
@@ -168,6 +171,17 @@ interface ReactiveMongoQueryExecution {
ReturnedType returnedType = processor.getReturnedType();
if (isVoid(returnedType)) {
if (source instanceof Mono) {
return ((Mono<?>) source).then();
}
if (source instanceof Publisher) {
return Flux.from((Publisher<?>) source).then();
}
}
if (ClassUtils.isPrimitiveOrWrapper(returnedType.getReturnedType())) {
return source;
}
@@ -182,4 +196,8 @@ interface ReactiveMongoQueryExecution {
return processor.processResult(source, converter);
}
}
static boolean isVoid(ReturnedType returnedType) {
return returnedType.getReturnedType().equals(Void.class);
}
}

View File

@@ -219,7 +219,7 @@ class JsonScanner {
boolean isExpression = false;
int parenthesisCount = 0;
while (c == '$' || c == '_' || Character.isLetterOrDigit(c) || c == '#' || c == '{' || c == '[' || c == ']'
while (c == '$' || c == '_' || Character.isLetterOrDigit(c) || c == '#' || c == '{' || c == '['
|| (isExpression && isExpressionAllowedChar(c))) {
if (charCount == 0 && c == '#') {

View File

@@ -88,6 +88,7 @@ import com.mongodb.DBRef;
* @author Patrik Wasik
* @author Christoph Strobl
* @author Mark Paluch
* @author Heesu Jung
*/
@RunWith(MockitoJUnitRunner.class)
public class MappingMongoConverterUnitTests {
@@ -2078,6 +2079,23 @@ public class MappingMongoConverterUnitTests {
.isEqualTo(new BasicDBObject("property", "value"));
}
@Test // DATAMONGO-2300
public void readAndConvertDBRefNestedByMapCorrectly() {
org.bson.Document cluster = new org.bson.Document("_id", 100L);
DBRef dbRef = new DBRef("clusters", 100L);
org.bson.Document data = new org.bson.Document("_id", 3L);
data.append("cluster", dbRef);
MappingMongoConverter spyConverter = spy(converter);
Mockito.doReturn(cluster).when(spyConverter).readRef(dbRef);
Map<Object, Object> result = spyConverter.readMap(ClassTypeInformation.MAP, data, ObjectPath.ROOT);
assertThat(((LinkedHashMap) result.get("cluster")).get("_id")).isEqualTo(100L);
}
static class GenericType<T> {
T content;
}

View File

@@ -927,6 +927,70 @@ public class QueryMapperUnitTests {
assertThat(target).isEqualTo(org.bson.Document.parse("{\"_id\": {\"$in\": [{\"$oid\": \"" + id + "\"}]}}"));
}
@Test // DATAMONGO-2488
public void mapsNestedArrayPathCorrectlyForNonMatchingPath() {
org.bson.Document target = mapper.getMappedObject(
query(where("array.$[some_item].nested.$[other_item]").is("value")).getQueryObject(),
context.getPersistentEntity(Foo.class));
assertThat(target).isEqualTo(new org.bson.Document("array.$[some_item].nested.$[other_item]", "value"));
}
@Test // DATAMONGO-2488
public void mapsNestedArrayPathCorrectlyForObjectTargetArray() {
org.bson.Document target = mapper.getMappedObject(
query(where("arrayObj.$[some_item].nested.$[other_item]").is("value")).getQueryObject(),
context.getPersistentEntity(WithNestedArray.class));
assertThat(target).isEqualTo(new org.bson.Document("arrayObj.$[some_item].nested.$[other_item]", "value"));
}
@Test // DATAMONGO-2488
public void mapsNestedArrayPathCorrectlyForStringTargetArray() {
org.bson.Document target = mapper.getMappedObject(
query(where("arrayString.$[some_item].nested.$[other_item]").is("value")).getQueryObject(),
context.getPersistentEntity(WithNestedArray.class));
assertThat(target).isEqualTo(new org.bson.Document("arrayString.$[some_item].nested.$[other_item]", "value"));
}
@Test // DATAMONGO-2488
public void mapsCustomFieldNamesForNestedArrayPathCorrectly() {
org.bson.Document target = mapper.getMappedObject(
query(where("arrayCustomName.$[some_item].nested.$[other_item]").is("value")).getQueryObject(),
context.getPersistentEntity(WithNestedArray.class));
assertThat(target).isEqualTo(new org.bson.Document("arrayCustomName.$[some_item].nes-ted.$[other_item]", "value"));
}
class WithNestedArray {
List<NestedArrayOfObj> arrayObj;
List<NestedArrayOfString> arrayString;
List<NestedArrayOfObjCustomFieldName> arrayCustomName;
}
class NestedArrayOfObj {
List<ArrayObj> nested;
}
class NestedArrayOfObjCustomFieldName {
@Field("nes-ted") List<ArrayObj> nested;
}
class NestedArrayOfString {
List<String> nested;
}
class ArrayObj {
String foo;
}
@Document
public class Foo {
@Id private ObjectId id;

View File

@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.junit.Before;
import org.junit.Test;
@@ -54,7 +55,7 @@ public class BasicMongoPersistentPropertyUnitTests {
@Before
public void setup() {
entity = new BasicMongoPersistentEntity<Person>(ClassTypeInformation.from(Person.class));
entity = new BasicMongoPersistentEntity<>(ClassTypeInformation.from(Person.class));
}
@Test
@@ -223,12 +224,19 @@ public class BasicMongoPersistentPropertyUnitTests {
assertThat(property.getFieldType()).isEqualTo(ObjectId.class);
}
@Test // DATAMONGO-2460
public void fieldTypeShouldBeDocumentForPropertiesAnnotatedIdWhenAComplexTypeAndFieldTypeImplicit() {
MongoPersistentProperty property = getPropertyFor(WithComplexId.class, "id");
assertThat(property.getFieldType()).isEqualTo(Document.class);
}
private MongoPersistentProperty getPropertyFor(Field field) {
return getPropertyFor(entity, field);
}
private static <T> MongoPersistentProperty getPropertyFor(Class<T> type, String fieldname) {
return getPropertyFor(new BasicMongoPersistentEntity<T>(ClassTypeInformation.from(type)), fieldname);
return getPropertyFor(new BasicMongoPersistentEntity<>(ClassTypeInformation.from(type)), fieldname);
}
private static MongoPersistentProperty getPropertyFor(MongoPersistentEntity<?> entity, String fieldname) {
@@ -329,4 +337,14 @@ public class BasicMongoPersistentPropertyUnitTests {
@MongoId(FieldType.OBJECT_ID) String id;
}
static class ComplexId {
String value;
}
static class WithComplexId {
@Id @org.springframework.data.mongodb.core.mapping.Field ComplexId id;
}
}

View File

@@ -21,7 +21,7 @@ import static org.springframework.data.mongodb.core.query.Query.*;
import org.bson.Document;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
@@ -337,6 +337,19 @@ public class QueryTests {
.isNotEqualTo(source.getQueryObject());
}
@Test // DATAMONGO-2478
public void queryOfShouldWorkOnProxiedObjects() {
BasicQuery source = new BasicQuery("{ 'foo' : 'bar'}", "{ '_id' : -1, 'foo' : 1 }");
source.withHint("the hint");
source.limit(10);
source.setSortObject(new Document("_id", 1));
Query target = Query.of((Query) new ProxyFactory(source).getProxy());
compareQueries(target, source);
}
private void compareQueries(Query actual, Query expected) {
assertThat(actual.getCollation()).isEqualTo(expected.getCollation());

View File

@@ -17,6 +17,8 @@ package org.springframework.data.mongodb.repository;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.domain.Sort.Direction.*;
import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*;
import static org.springframework.data.mongodb.test.util.Assertions.assertThat;
import lombok.Data;
@@ -575,6 +577,18 @@ public class ReactiveMongoRepositoryTests {
.expectNext("lastname").verifyComplete();
}
@Test // DATAMONGO-2406
public void deleteByShouldHandleVoidResultTypeCorrectly() {
repository.deleteByLastname(dave.getLastname()) //
.as(StepVerifier::create) //
.verifyComplete();
template.find(query(where("lastname").is(dave.getLastname())), Person.class) //
.as(StepVerifier::create) //
.verifyComplete();
}
interface ReactivePersonRepository
extends ReactiveMongoRepository<Person, String>, ReactiveQuerydslPredicateExecutor<Person> {
@@ -645,6 +659,8 @@ public class ReactiveMongoRepositoryTests {
@Query(value = "{_id:?0}")
Mono<org.bson.Document> findDocumentById(String id);
Mono<Void> deleteByLastname(String lastname);
}
interface ReactiveContactRepository extends ReactiveMongoRepository<Contact, String> {}

View File

@@ -220,6 +220,41 @@ public class ParameterBindingJsonReaderUnitTests {
assertThat(target).isEqualTo(new Document("name", "value"));
}
@Test // DATAMONGO-2476
public void bindUnquotedParameterInArray() {
Document target = parse("{ 'name' : { $in : [?0] } }", "kohlin");
assertThat(target).isEqualTo(new Document("name", new Document("$in", Collections.singletonList("kohlin"))));
}
@Test // DATAMONGO-2476
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"))));
}
@Test // DATAMONGO-2476
public void bindUnquotedParameterInArrayWithSpaces() {
Document target = parse("{ 'name' : { $in : [ ?0 ] } }", "kohlin");
assertThat(target).isEqualTo(new Document("name", new Document("$in", Collections.singletonList("kohlin"))));
}
@Test // DATAMONGO-2476
public void bindQuotedParameterInArray() {
Document target = parse("{ 'name' : { $in : ['?0'] } }", "kohlin");
assertThat(target).isEqualTo(new Document("name", new Document("$in", Collections.singletonList("kohlin"))));
}
@Test // DATAMONGO-2476
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"))));
}
private static Document parse(String json, Object... args) {
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, args);

View File

@@ -416,7 +416,7 @@ The MappingMongoConverter can use metadata to drive the mapping of objects to do
* `@TextIndexed`: Applied at the field level to mark the field to be included in the text index.
* `@HashIndexed`: Applied at the field level for usage within a hashed index to partition data across a sharded cluster.
* `@Language`: Applied at the field level to set the language override property for text index.
* `@Transient`: By default all private fields are mapped to the document, this annotation excludes the field where it is applied from being stored in the database
* `@Transient`: By default, all fields are mapped to the document. This annotation excludes the field where it is applied from being stored in the database. Transient properties cannot be used within a persistence constructor as the converter cannot materialize a value for the constructor argument.
* `@PersistenceConstructor`: Marks a given constructor - even a package protected one - to use when instantiating the object from the database. Constructor arguments are mapped by name to the key values in the retrieved Document.
* `@Value`: This annotation is part of the Spring Framework . Within the mapping framework it can be applied to constructor arguments. This lets you use a Spring Expression Language statement to transform a key's value retrieved in the database before it is used to construct a domain object. In order to reference a property of a given document one has to use expressions like: `@Value("#root.myProperty")` where `root` refers to the root of the given document.
* `@Field`: Applied at the field level it allows to describe the name and type of the field as it will be represented in the MongoDB BSON document thus allowing the name and type to be different than the fieldname of the class as well as the property type.

View File

@@ -28,7 +28,10 @@ public class Person {
----
====
Note that the domain type shown in the preceding example has a property named `id` of type `ObjectId`. The default serialization mechanism used in `MongoTemplate` (which backs the repository support) regards properties named `id` as the document ID. Currently, we support `String`, `ObjectId`, and `BigInteger` as ID types. Now that we have a domain object, we can define an interface that uses it, as follows:
Note that the domain type shown in the preceding example has a property named `id` of type `String`. The default serialization mechanism used in `MongoTemplate` (which backs the repository support) regards properties named `id` as the document ID. Currently, we support `String`, `ObjectId`, and `BigInteger` as ID types.
Please see <<mongo-template.id-handling, ID mapping>> for more information about on how the `id` field is handled in the mapping layer.
Now that we have a domain object, we can define an interface that uses it, as follows:
.Basic repository interface to persist Person entities
====
@@ -93,7 +96,7 @@ class ApplicationConfig extends AbstractMongoConfiguration {
@Override
protected String getMappingBasePackage() {
return "com.oreilly.springdata.mongodb"
return "com.oreilly.springdata.mongodb";
}
}
----
@@ -311,7 +314,7 @@ The following example shows how to define a `near` query that finds all persons
====
[source,java]
----
public interface PersonRepository extends MongoRepository<Person, String>
public interface PersonRepository extends MongoRepository<Person, String> {
// { 'location' : { '$near' : [point.x, point.y], '$maxDistance' : distance}}
List<Person> findByLocationNear(Point location, Distance distance);
@@ -344,7 +347,7 @@ Spring Data MongoDb supports geo-near queries, as the following example shows:
[source,java]
----
public interface PersonRepository extends MongoRepository<Person, String>
public interface PersonRepository extends MongoRepository<Person, String> {
// {'geoNear' : 'location', 'near' : [x, y] }
GeoResults<Person> findByLocationNear(Point location);
@@ -371,7 +374,7 @@ By adding the `org.springframework.data.mongodb.repository.Query` annotation to
[source,java]
----
public interface PersonRepository extends MongoRepository<Person, String>
public interface PersonRepository extends MongoRepository<Person, String> {
@Query("{ 'firstname' : ?0 }")
List<Person> findByThePersonsFirstname(String firstname);
@@ -387,7 +390,7 @@ You can also use the filter property to restrict the set of properties that is m
[source,java]
----
public interface PersonRepository extends MongoRepository<Person, String>
public interface PersonRepository extends MongoRepository<Person, String> {
@Query(value="{ 'firstname' : ?0 }", fields="{ 'firstname' : 1, 'lastname' : 1}")
List<Person> findByThePersonsFirstname(String firstname);
@@ -438,7 +441,7 @@ to declare the predicate value for `lastname` (which is equivalent to the `?0` p
[source,java]
----
public interface PersonRepository extends MongoRepository<Person, String>
public interface PersonRepository extends MongoRepository<Person, String> {
@Query("{'lastname': ?#{[0]} }")
List<Person> findByQueryWithExpression(String param0);
@@ -450,7 +453,7 @@ used in conjunction with JSON reveal a side-effect, because Map-like declaration
[source,java]
----
public interface PersonRepository extends MongoRepository<Person, String>
public interface PersonRepository extends MongoRepository<Person, String> {
@Query("{'id': ?#{ [0] ? {$exists :true} : [1] }}")
List<Person> findByQueryWithExpressionAndNestedObject(boolean param0, String param1);

View File

@@ -41,7 +41,10 @@ public class Person {
----
====
Note that the entity defined in the preceding example has a property named `id` of type `ObjectId`. The default serialization mechanism used in `MongoTemplate` (which backs the repository support) regards properties named `id` as the document ID. Currently, we support `String`, `ObjectId`, and `BigInteger` as id-types. The following example shows how to create an interface that defines queries against the `Person` object from the preceding example:
Note that the entity defined in the preceding example has a property named `id` of type `String`. The default serialization mechanism used in `MongoTemplate` (which backs the repository support) regards properties named `id` as the document ID. Currently, we support `String`, `ObjectId`, and `BigInteger` as id-types.
Please see <<mongo-template.id-handling, ID mapping>> for more information about on how the `id` field is handled in the mapping layer.
The following example shows how to create an interface that defines queries against the `Person` object from the preceding example:
.Basic repository interface to persist Person entities
====
@@ -93,7 +96,7 @@ class ApplicationConfig extends AbstractReactiveMongoConfiguration {
@Override
protected String getMappingBasePackage() {
return "com.oreilly.springdata.mongodb"
return "com.oreilly.springdata.mongodb";
}
}
----
@@ -117,6 +120,20 @@ public class PersonRepositoryTests {
----
====
WARNING: The `Page` return type (as in `Mono<Page>`) is not supported by reactive repositories.
It is possible to use `Pageable` in derived finder methods, to pass on `sort`, `limit` and `offset` parameters to the query to reduce load and network traffic.
The returned `Flux` will only emit data within the declared range.
.Limit and Offset with reactive repositories
====
[source,java]
----
Pageable page = PageRequest.of(1, 10, Sort.by("lastname"));
Flux<Person> persons = repository.findByFirstnameOrderByLastname("luke", page);
----
====
[[mongo.reactive.repositories.features]]
== Features
@@ -143,7 +160,7 @@ The following example shows how to define a `near` query that finds all persons
====
[source,java]
----
public interface PersonRepository extends ReactiveMongoRepository<Person, String>
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
// { 'location' : { '$near' : [point.x, point.y], '$maxDistance' : distance}}
Flux<Person> findByLocationNear(Point location, Distance distance);
@@ -178,7 +195,7 @@ Spring Data MongoDB supports geo-near queries, as the following example shows:
[source,java]
----
public interface PersonRepository extends ReactiveMongoRepository<Person, String>
public interface PersonRepository extends ReactiveMongoRepository<Person, String> {
// {'geoNear' : 'location', 'near' : [x, y] }
Flux<GeoResult<Person>> findByLocationNear(Point location);

View File

@@ -1,6 +1,108 @@
Spring Data MongoDB Changelog
=============================
Changes in version 2.2.6.RELEASE (2020-03-25)
---------------------------------------------
* DATAMONGO-2497 - Update documentation regarding @Transient properties usage in the persistence constructor.
* DATAMONGO-2488 - KeyMapper.mapPropertyName does not work for nested arrays.
* DATAMONGO-2485 - Release 2.2.6 (Moore SR6).
* DATAMONGO-2445 - Deprecate ReactiveGridFs methods using AsyncInputStream.
* DATAMONGO-2300 - Can't read and convert DBRef when the type is Map.
Changes in version 3.0.0.M4 (2020-03-11)
----------------------------------------
* DATAMONGO-2491 - Adapt to Mockito 3.3 changes.
* DATAMONGO-2489 - Upgrade to MongoDB Driver 4.0.
* DATAMONGO-2481 - Speed up build.
* DATAMONGO-2478 - NPE when using Query annotation and with sort and pageable.
* DATAMONGO-2476 - JsonParseException: JSON reader was expecting a value but found '}'.
* DATAMONGO-2474 - Upgrade to MongoDB Driver 4.0.0-rc0.
* DATAMONGO-2473 - Release 3.0 M4 (Neumann).
* DATAMONGO-2363 - Add support for $merge aggregation stage.
* DATAMONGO-2355 - Revise Abstract…MongoConfiguration to expose more bean detail and avoid proxying.
* DATAMONGO-2341 - Support shard key derivation.
Changes in version 2.2.5.RELEASE (2020-02-26)
---------------------------------------------
* DATAMONGO-2478 - NPE when using Query annotation and with sort and pageable.
* DATAMONGO-2476 - JsonParseException: JSON reader was expecting a value but found '}'.
* DATAMONGO-2464 - Fix code examples in reference documentation.
* DATAMONGO-2460 - @Field annotation behaviour changed.
* DATAMONGO-2459 - Update Documentation of Reactive Repositories to mention usage of PageRequest.
* DATAMONGO-2457 - Reference documentation: Text does not match with code example.
* DATAMONGO-2453 - Release 2.2.5 (Moore SR5).
* DATAMONGO-2406 - Allow Mono<Void> as return type for derived deleteBy queries.
* DATAMONGO-2079 - MappingMongoConverter cannot be proxied because it implements the package-private ValueResolver interface.
Changes in version 2.1.16.RELEASE (2020-02-26)
----------------------------------------------
* DATAMONGO-2464 - Fix code examples in reference documentation.
* DATAMONGO-2459 - Update Documentation of Reactive Repositories to mention usage of PageRequest.
* DATAMONGO-2457 - Reference documentation: Text does not match with code example.
* DATAMONGO-2452 - Release 2.1.16 (Lovelace SR16).
* DATAMONGO-2079 - MappingMongoConverter cannot be proxied because it implements the package-private ValueResolver interface.
Changes in version 3.0.0.M3 (2020-02-12)
----------------------------------------
* DATAMONGO-2470 - Adapt tests to AssertJ 3.15.0.
* DATAMONGO-2464 - Fix code examples in reference documentation.
* DATAMONGO-2462 - Move off deprecated EntityInstantiators.
* DATAMONGO-2461 - Fix Jenkins build.
* DATAMONGO-2460 - @Field annotation behaviour changed.
* DATAMONGO-2459 - Update Documentation of Reactive Repositories to mention usage of PageRequest.
* DATAMONGO-2457 - Reference documentation: Text does not match with code example.
* DATAMONGO-2456 - Release 3.0 M3 (Neumann).
* DATAMONGO-2449 - Add aggregation options parameters to @Aggregation annotation.
* DATAMONGO-2427 - Switch to 4.0-beta1 MongoDB driver.
* DATAMONGO-2417 - Typesafe Extension Function for ReactiveFindOperation.FindDistinct.
* DATAMONGO-2406 - Allow Mono<Void> as return type for derived deleteBy queries.
* DATAMONGO-2400 - Read/write converters not working.
* DATAMONGO-2365 - Parameter hint in Query#hint is not index name.
* DATAMONGO-2249 - Query hint leads to error when executed via reactive template.
* DATAMONGO-2079 - MappingMongoConverter cannot be proxied because it implements the package-private ValueResolver interface.
* DATAMONGO-1997 - Add support to return the single deleted item for a deleteBy query method.
Changes in version 3.0.0.M2 (2020-01-17)
----------------------------------------
* DATAMONGO-2454 - Release 3.0 M2 (Neumann).
Changes in version 3.0.0.M1 (2020-01-16)
----------------------------------------
* DATAMONGO-2451 - Value of sort direction converted to String for id fields (query, index creation).
* DATAMONGO-2450 - Bulk Operations do not support filterArray in update operations.
* DATAMONGO-2448 - Bump Version to 3.0.
* DATAMONGO-2447 - Upgrade MongoDB Drivers to 3.12.0 and 1.13.0.
* DATAMONGO-2444 - Update copyright years to 2020.
* DATAMONGO-2442 - Cond.thenValueOf(String fieldReference) does not concat fieldRef with "$" prefix.
* DATAMONGO-2440 - Unable to use MongoTemplate to perform query with 'in' condition by field annotated with @Field(targetType = FieldType.OBJECT_ID).
* DATAMONGO-2437 - Grouping after aggregation returns NULL _id when mapping to a class with compound key.
* DATAMONGO-2430 - Upgrade to mongo-java-driver 3.11.2.
* DATAMONGO-2423 - @NonNullApi restricts update with null.
* DATAMONGO-2418 - Application Context Doesn't start with @Query.
* DATAMONGO-2414 - ReactiveGridFsResource.getDownloadStream(…) hang if completion happens on event loop.
* DATAMONGO-2410 - Using BasicDBObject as an entity caused java.lang.ClassCastException in runtime.
* DATAMONGO-2409 - Extension Function ReactiveFindOperation.DistinctWithProjection.asType() has wrong return type.
* DATAMONGO-2403 - ReactiveStringBasedAggregation / AggregationUtils fails on NPE because source or value is null.
* DATAMONGO-2399 - Upgrade to mongo-java-driver 3.11.1.
* DATAMONGO-2394 - nearSphere query wrongly generated with radian parameter instead of meters.
* DATAMONGO-2393 - Reading large file from ReactiveGridFsTemplate causes a stackoverflow and the code to hang.
* DATAMONGO-2392 - Reading GridFS files written with old api and custom id fails on ReactiveGridFsTemplate.
* DATAMONGO-2390 - Add maxTimeMS to AggregationOptions.
* DATAMONGO-2388 - IndexOperations.getIndexInfo() fails for index that has partialFilterExpression containing DBRef.
* DATAMONGO-2385 - Unnecessary null checks in MongoConverters.
* DATAMONGO-2383 - Release 3.0 M1 (Neumann).
* DATAMONGO-2370 - Add support for $round aggregation expression.
* DATAMONGO-2331 - Add support for Aggregations in Update.
* DATAMONGO-2059 - Replace usage of deprecated collection.count() with collection.countDocuments().
* DATAMONGO-765 - Add support for paging and sorting on GridFSTemplate.
Changes in version 2.2.4.RELEASE (2020-01-15)
---------------------------------------------
* DATAMONGO-2451 - Value of sort direction converted to String for id fields (query, index creation).
@@ -2851,3 +2953,10 @@ Repository

View File

@@ -1,4 +1,4 @@
Spring Data MongoDB 2.2.4
Spring Data MongoDB 2.2.6
Copyright (c) [2010-2019] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -10,3 +10,5 @@ code for the these subcomponents is subject to the terms and
conditions of the subcomponent's license, as noted in the LICENSE file.