diff --git a/pom.xml b/pom.xml
index 5d28c8a5c5..dd821fe8f3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3798-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml
index 0033bd11d5..2a74d848ca 100644
--- a/spring-data-mongodb-benchmarks/pom.xml
+++ b/spring-data-mongodb-benchmarks/pom.xml
@@ -7,7 +7,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3798-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index f62c8dc7f4..bccb6a40da 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3798-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 1f157e75bc..e9f92255a9 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3798-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
index f801b8d990..62e713065f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
@@ -108,6 +108,6 @@ private Object createLazyLoadingProxy(MongoPersistentProperty property, Object s
ReferenceLookupDelegate referenceLookupDelegate, LookupFunction lookupFunction, MongoEntityReader entityReader) {
return proxyFactory.createLazyLoadingProxy(property, it -> {
return referenceLookupDelegate.readReference(it, source, lookupFunction, entityReader);
- }, source);
+ }, source instanceof DocumentReferenceSource ? ((DocumentReferenceSource)source).getTargetSource() : source);
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
new file mode 100644
index 0000000000..03e5eb0d5d
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core.convert;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * The source object to resolve document references upon. Encapsulates the actual source and the reference specific
+ * values.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+public class DocumentReferenceSource {
+
+ private final Object self;
+
+ @Nullable private final Object targetSource;
+
+ /**
+ * Create a new instance of {@link DocumentReferenceSource}.
+ *
+ * @param self the entire wrapper object holding references. Must not be {@literal null}.
+ * @param targetSource the reference value source.
+ */
+ DocumentReferenceSource(Object self, @Nullable Object targetSource) {
+
+ this.self = self;
+ this.targetSource = targetSource;
+ }
+
+ /**
+ * Get the outer document.
+ *
+ * @return never {@literal null}.
+ */
+ public Object getSelf() {
+ return self;
+ }
+
+ /**
+ * Get the actual (property specific) reference value.
+ *
+ * @return can be {@literal null}.
+ */
+ @Nullable
+ public Object getTargetSource() {
+ return targetSource;
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index a60c853c33..5a2c3e952a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -38,7 +38,6 @@
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -524,10 +523,6 @@ private void readAssociation(Association association, P
MongoPersistentProperty property = association.getInverse();
Object value = documentAccessor.get(property);
- if (value == null) {
- return;
- }
-
if (property.isDocumentReference()
|| (!property.isDbReference() && property.findAnnotation(Reference.class) != null)) {
@@ -535,17 +530,26 @@ private void readAssociation(Association association, P
if (conversionService.canConvert(DocumentPointer.class, property.getActualType())) {
+ if(value == null) {
+ return;
+ }
+
DocumentPointer> pointer = () -> value;
// collection like special treatment
accessor.setProperty(property, conversionService.convert(pointer, property.getActualType()));
} else {
+
accessor.setProperty(property,
- dbRefResolver.resolveReference(property, value, referenceLookupDelegate, context::convert));
+ dbRefResolver.resolveReference(property, new DocumentReferenceSource(documentAccessor.getDocument(), documentAccessor.get(property)), referenceLookupDelegate, context::convert));
}
return;
}
+ if (value == null) {
+ return;
+ }
+
DBRef dbref = value instanceof DBRef ? (DBRef) value : null;
accessor.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
index 3ca730452f..e16f9024b5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
@@ -87,17 +87,20 @@ public ReferenceLookupDelegate(
* Read the reference expressed by the given property.
*
* @param property the reference defining property. Must not be {@literal null}. THe
- * @param value the source value identifying to the referenced entity. Must not be {@literal null}.
+ * @param source the source value identifying to the referenced entity. Must not be {@literal null}.
* @param lookupFunction to execute a lookup query. Must not be {@literal null}.
* @param entityReader the callback to convert raw source values into actual domain types. Must not be
* {@literal null}.
* @return can be {@literal null}.
*/
@Nullable
- public Object readReference(MongoPersistentProperty property, Object value, LookupFunction lookupFunction,
+ public Object readReference(MongoPersistentProperty property, Object source, LookupFunction lookupFunction,
MongoEntityReader entityReader) {
- DocumentReferenceQuery filter = computeFilter(property, value, spELContext);
+ Object value = source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getTargetSource()
+ : source;
+
+ DocumentReferenceQuery filter = computeFilter(property, source, spELContext);
ReferenceCollection referenceCollection = computeReferenceContext(property, value, spELContext);
Iterable result = lookupFunction.apply(filter, referenceCollection);
@@ -196,8 +199,16 @@ private T parseValueOrGet(String value, ParameterBindingContext bindingConte
ParameterBindingContext bindingContext(MongoPersistentProperty property, Object source, SpELContext spELContext) {
- return new ParameterBindingContext(valueProviderFor(source), spELContext.getParser(),
+ ValueProvider valueProvider;
+ if (source instanceof DocumentReferenceSource) {
+ valueProvider = valueProviderFor(((DocumentReferenceSource) source).getTargetSource());
+ } else {
+ valueProvider = valueProviderFor(source);
+ }
+
+ return new ParameterBindingContext(valueProvider, spELContext.getParser(),
() -> evaluationContextFor(property, source, spELContext));
+
}
ValueProvider valueProviderFor(Object source) {
@@ -212,9 +223,18 @@ ValueProvider valueProviderFor(Object source) {
EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object source, SpELContext spELContext) {
- EvaluationContext ctx = spELContext.getEvaluationContext(source);
- ctx.setVariable("target", source);
- ctx.setVariable(property.getName(), source);
+ Object target = source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getTargetSource()
+ : source;
+
+ if (target == null) {
+ target = new Document();
+ }
+
+ EvaluationContext ctx = spELContext.getEvaluationContext(target);
+ ctx.setVariable("target", target);
+ ctx.setVariable("self",
+ source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getSelf() : source);
+ ctx.setVariable(property.getName(), target);
return ctx;
}
@@ -223,22 +243,30 @@ EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object
* Compute the query to retrieve linked documents.
*
* @param property must not be {@literal null}.
- * @param value must not be {@literal null}.
+ * @param source must not be {@literal null}.
* @param spELContext must not be {@literal null}.
* @return never {@literal null}.
*/
@SuppressWarnings("unchecked")
- DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object value, SpELContext spELContext) {
+ DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object source, SpELContext spELContext) {
DocumentReference documentReference = property.isDocumentReference() ? property.getDocumentReference()
: ReferenceEmulatingDocumentReference.INSTANCE;
String lookup = documentReference.lookup();
- Document sort = parseValueOrGet(documentReference.sort(), bindingContext(property, value, spELContext),
+ Object value = source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getTargetSource()
+ : source;
+
+ Document sort = parseValueOrGet(documentReference.sort(), bindingContext(property, source, spELContext),
() -> new Document());
- if (property.isCollectionLike() && value instanceof Collection) {
+ if (property.isCollectionLike() && (value instanceof Collection || value == null)) {
+
+ if (value == null) {
+ return new ListDocumentReferenceQuery(codec.decode(lookup, bindingContext(property, source, spELContext)),
+ sort);
+ }
List ors = new ArrayList<>();
for (Object entry : (Collection