Skip to content

Commit 673a81a

Browse files
christophstroblmp911de
authored andcommitted
Fix query/update document reference computation for non-id properties.
We now consider using the target keyword when computing document references. This fixes an issue where query/update statements had not been rendered correctly for references like { 'name' : ?#{#target} }. Closes #3853 Original pull request: #3856.
1 parent 9770326 commit 673a81a

File tree

4 files changed

+177
-1
lines changed

4 files changed

+177
-1
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ static class LinkageDocument {
151151
private final String lookup;
152152
private final org.bson.Document documentPointer;
153153
private final Map<String, String> placeholderMap;
154+
private final boolean isSimpleTargetPointer;
154155

155156
static LinkageDocument from(String lookup) {
156157
return new LinkageDocument(lookup);
@@ -177,6 +178,7 @@ private LinkageDocument(String lookup) {
177178
}
178179

179180
this.documentPointer = org.bson.Document.parse(targetLookup);
181+
this.isSimpleTargetPointer = placeholderMap.size() == 1 && placeholderMap.containsValue("target") && lookup.contains("#target");
180182
}
181183

182184
private String placeholder(int index) {
@@ -194,7 +196,7 @@ DocumentPointer<Object> getDocumentPointer(
194196
propertyAccessor);
195197
}
196198

197-
Document updatePlaceholders(org.bson.Document source, org.bson.Document target,
199+
Object updatePlaceholders(org.bson.Document source, org.bson.Document target,
198200
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext,
199201
MongoPersistentEntity<?> persistentEntity, PersistentPropertyAccessor<?> propertyAccessor) {
200202

@@ -245,6 +247,11 @@ Document updatePlaceholders(org.bson.Document source, org.bson.Document target,
245247

246248
target.put(entry.getKey(), entry.getValue());
247249
}
250+
251+
if(target.size()==1 && isSimpleTargetPointer) {
252+
return target.values().iterator().next();
253+
}
254+
248255
return target;
249256
}
250257
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java

+4
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,10 @@ protected boolean isAssociationConversionNecessary(Field documentField, @Nullabl
517517
return true;
518518
}
519519

520+
if(property.isDocumentReference()) {
521+
return true;
522+
}
523+
520524
MongoPersistentEntity<?> entity = documentField.getPropertyEntity();
521525
return entity.hasIdProperty()
522526
&& (type.equals(DBRef.class) || entity.getRequiredIdProperty().getActualType().isAssignableFrom(type));

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

+74
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
5151
import org.springframework.data.mongodb.core.mapping.DBRef;
5252
import org.springframework.data.mongodb.core.mapping.Document;
53+
import org.springframework.data.mongodb.core.mapping.DocumentReference;
5354
import org.springframework.data.mongodb.core.mapping.Field;
5455
import org.springframework.data.mongodb.core.mapping.FieldType;
5556
import org.springframework.data.mongodb.core.mapping.MongoId;
@@ -424,6 +425,60 @@ void convertsNestedDBRefsCorrectly() {
424425
assertThat(inClause.get(0)).isInstanceOf(com.mongodb.DBRef.class);
425426
}
426427

428+
@Test // GH-3853
429+
void convertsDocumentReferenceOnIdPropertyCorrectly() {
430+
431+
Sample reference = new Sample();
432+
reference.foo = "s1";
433+
434+
Query query = query(where("sample").is(reference));
435+
org.bson.Document mappedQuery = mapper.getMappedObject(query.getQueryObject(),
436+
context.getPersistentEntity(WithDocumentReference.class));
437+
438+
assertThat(mappedQuery).containsEntry("sample", "s1");
439+
}
440+
441+
@Test // GH-3853
442+
void convertsListDocumentReferenceOnIdPropertyCorrectly() {
443+
444+
Sample reference = new Sample();
445+
reference.foo = "s1";
446+
447+
Query query = query(where("samples").is(Arrays.asList(reference)));
448+
org.bson.Document mappedQuery = mapper.getMappedObject(query.getQueryObject(),
449+
context.getPersistentEntity(WithDocumentReference.class));
450+
451+
assertThat(mappedQuery).containsEntry("samples", Arrays.asList("s1"));
452+
}
453+
454+
@Test // GH-3853
455+
void convertsDocumentReferenceOnNonIdPropertyCorrectly() {
456+
457+
Customer reference = new Customer();
458+
reference.id = new ObjectId();
459+
reference.name = "c1";
460+
461+
Query query = query(where("customer").is(reference));
462+
org.bson.Document mappedQuery = mapper.getMappedObject(query.getQueryObject(),
463+
context.getPersistentEntity(WithDocumentReference.class));
464+
465+
assertThat(mappedQuery).containsEntry("customer", "c1");
466+
}
467+
468+
@Test // GH-3853
469+
void convertsListDocumentReferenceOnNonIdPropertyCorrectly() {
470+
471+
Customer reference = new Customer();
472+
reference.id = new ObjectId();
473+
reference.name = "c1";
474+
475+
Query query = query(where("customers").is(Arrays.asList(reference)));
476+
org.bson.Document mappedQuery = mapper.getMappedObject(query.getQueryObject(),
477+
context.getPersistentEntity(WithDocumentReference.class));
478+
479+
assertThat(mappedQuery).containsEntry("customers", Arrays.asList("c1"));
480+
}
481+
427482
@Test // DATAMONGO-752
428483
void mapsSimpleValuesStartingWith$Correctly() {
429484

@@ -1496,6 +1551,25 @@ class WithMapDBRef {
14961551
@DBRef Map<String, Sample> mapWithDBRef;
14971552
}
14981553

1554+
static class WithDocumentReference {
1555+
1556+
private ObjectId id;
1557+
1558+
private String name;
1559+
1560+
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") // remove `lookup` for the other test case.
1561+
private Customer customer;
1562+
1563+
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") // remove `lookup` for the other test case.
1564+
private List<Customer> customers;
1565+
1566+
@DocumentReference
1567+
private Sample sample;
1568+
1569+
@DocumentReference
1570+
private List<Sample> samples;
1571+
}
1572+
14991573
class WithTextScoreProperty {
15001574

15011575
@Id String id;

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java

+91
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.concurrent.atomic.AtomicInteger;
3232

3333
import org.bson.Document;
34+
import org.bson.types.ObjectId;
3435
import org.junit.jupiter.api.BeforeEach;
3536
import org.junit.jupiter.api.Test;
3637
import org.junit.jupiter.api.extension.ExtendWith;
@@ -49,6 +50,7 @@
4950
import org.springframework.data.mongodb.MongoDatabaseFactory;
5051
import org.springframework.data.mongodb.core.DocumentTestUtils;
5152
import org.springframework.data.mongodb.core.MongoExceptionTranslator;
53+
import org.springframework.data.mongodb.core.mapping.DocumentReference;
5254
import org.springframework.data.mongodb.core.mapping.Field;
5355
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
5456
import org.springframework.data.mongodb.core.mapping.Unwrapped;
@@ -1251,6 +1253,64 @@ void multipleKeysStartingWithANumberInNestedPath() {
12511253
assertThat(mappedUpdate).isEqualTo("{\"$set\": {\"intKeyedMap.1a.map.0b\": \"testing\"}}");
12521254
}
12531255

1256+
@Test // GH-3853
1257+
void updateWithDocuRefOnId() {
1258+
1259+
Sample sample = new Sample();
1260+
sample.foo = "s1";
1261+
1262+
Update update = new Update().set("sample", sample);
1263+
1264+
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
1265+
context.getPersistentEntity(WithDocumentReference.class));
1266+
1267+
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("sample","s1")));
1268+
}
1269+
1270+
@Test // GH-3853
1271+
void updateListWithDocuRefOnId() {
1272+
1273+
Sample sample = new Sample();
1274+
sample.foo = "s1";
1275+
1276+
Update update = new Update().set("samples", Arrays.asList(sample));
1277+
1278+
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
1279+
context.getPersistentEntity(WithDocumentReference.class));
1280+
1281+
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("samples",Arrays.asList("s1"))));
1282+
}
1283+
1284+
@Test // GH-3853
1285+
void updateWithDocuRefOnProperty() {
1286+
1287+
Customer customer = new Customer();
1288+
customer.id = new ObjectId();
1289+
customer.name = "c-name";
1290+
1291+
Update update = new Update().set("customer", customer);
1292+
1293+
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
1294+
context.getPersistentEntity(WithDocumentReference.class));
1295+
1296+
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("customer","c-name")));
1297+
}
1298+
1299+
@Test // GH-3853
1300+
void updateListWithDocuRefOnProperty() {
1301+
1302+
Customer customer = new Customer();
1303+
customer.id = new ObjectId();
1304+
customer.name = "c-name";
1305+
1306+
Update update = new Update().set("customers", Arrays.asList(customer));
1307+
1308+
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
1309+
context.getPersistentEntity(WithDocumentReference.class));
1310+
1311+
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("customers", Arrays.asList("c-name"))));
1312+
}
1313+
12541314
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
12551315
ListModelWrapper concreteTypeWithListAttributeOfInterfaceType;
12561316
}
@@ -1621,4 +1681,35 @@ static class EntityWithNestedMap {
16211681
Map<String, Map<String, Map<String, Object>>> levelOne;
16221682
}
16231683

1684+
static class Customer {
1685+
1686+
@Id
1687+
private ObjectId id;
1688+
private String name;
1689+
}
1690+
1691+
static class Sample {
1692+
1693+
@Id private String foo;
1694+
}
1695+
1696+
static class WithDocumentReference {
1697+
1698+
private ObjectId id;
1699+
1700+
private String name;
1701+
1702+
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") // remove `lookup` for the other test case.
1703+
private Customer customer;
1704+
1705+
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") // remove `lookup` for the other test case.
1706+
private List<Customer> customers;
1707+
1708+
@DocumentReference
1709+
private Sample sample;
1710+
1711+
@DocumentReference
1712+
private List<Sample> samples;
1713+
}
1714+
16241715
}

0 commit comments

Comments
 (0)