Skip to content

Commit 0114150

Browse files
christophstroblmp911de
authored andcommitted
Fix query mapper path resolution for types considered simple ones.
spring-projects/spring-data-commons#2293 changed how PersistentProperty paths get resolved and considers potentially registered converters for those, which made the path resolution fail in during the query mapping process. This commit makes sure to capture the according exception and continue with the given user input. Fixes: #3659 Original pull request: #3661.
1 parent 5d8f3d5 commit 0114150

File tree

2 files changed

+87
-15
lines changed

2 files changed

+87
-15
lines changed

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

+37-15
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
import java.util.Map.Entry;
2020
import java.util.regex.Matcher;
2121
import java.util.regex.Pattern;
22+
import java.util.stream.Collectors;
2223

2324
import org.bson.BsonValue;
2425
import org.bson.Document;
2526
import org.bson.conversions.Bson;
2627
import org.bson.types.ObjectId;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
2730
import org.springframework.core.convert.ConversionService;
2831
import org.springframework.core.convert.converter.Converter;
2932
import org.springframework.data.domain.Example;
@@ -66,6 +69,8 @@
6669
*/
6770
public class QueryMapper {
6871

72+
protected static final Logger LOGGER = LoggerFactory.getLogger(QueryMapper.class);
73+
6974
private static final List<String> DEFAULT_ID_NAMES = Arrays.asList("id", "_id");
7075
private static final Document META_TEXT_SCORE = new Document("$meta", "textScore");
7176
static final ClassTypeInformation<?> NESTED_DOCUMENT = ClassTypeInformation.from(NestedDocument.class);
@@ -1099,29 +1104,46 @@ private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpre
10991104
return null;
11001105
}
11011106

1102-
try {
1107+
PersistentPropertyPath<MongoPersistentProperty> propertyPath = tryToResolvePersistentPropertyPath(path);
11031108

1104-
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(path);
1109+
if (propertyPath == null) {
11051110

1106-
Iterator<MongoPersistentProperty> iterator = propertyPath.iterator();
1107-
boolean associationDetected = false;
1111+
if (QueryMapper.LOGGER.isInfoEnabled()) {
1112+
1113+
String types = StringUtils.collectionToDelimitedString(
1114+
path.stream().map(it -> it.getType().getSimpleName()).collect(Collectors.toList()), " -> ");
1115+
QueryMapper.LOGGER.info(
1116+
"Could not map '{}'. Maybe a fragment in '{}' is considered a simple type. Mapper continues with {}.",
1117+
path, types, pathExpression);
1118+
}
1119+
return null;
1120+
}
11081121

1109-
while (iterator.hasNext()) {
1122+
Iterator<MongoPersistentProperty> iterator = propertyPath.iterator();
1123+
boolean associationDetected = false;
11101124

1111-
MongoPersistentProperty property = iterator.next();
1125+
while (iterator.hasNext()) {
11121126

1113-
if (property.isAssociation()) {
1114-
associationDetected = true;
1115-
continue;
1116-
}
1127+
MongoPersistentProperty property = iterator.next();
11171128

1118-
if (associationDetected && !property.isIdProperty()) {
1119-
throw new MappingException(String.format(INVALID_ASSOCIATION_REFERENCE, pathExpression));
1120-
}
1129+
if (property.isAssociation()) {
1130+
associationDetected = true;
1131+
continue;
11211132
}
11221133

1123-
return propertyPath;
1124-
} catch (InvalidPersistentPropertyPath e) {
1134+
if (associationDetected && !property.isIdProperty()) {
1135+
throw new MappingException(String.format(INVALID_ASSOCIATION_REFERENCE, pathExpression));
1136+
}
1137+
}
1138+
1139+
return propertyPath;
1140+
}
1141+
1142+
private PersistentPropertyPath<MongoPersistentProperty> tryToResolvePersistentPropertyPath(PropertyPath path) {
1143+
1144+
try {
1145+
return mappingContext.getPersistentPropertyPath(path);
1146+
} catch (MappingException e) {
11251147
return null;
11261148
}
11271149
}

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

+50
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Map;
2929
import java.util.Optional;
3030

31+
import lombok.Data;
3132
import org.bson.conversions.Bson;
3233
import org.bson.types.Code;
3334
import org.bson.types.ObjectId;
@@ -36,7 +37,10 @@
3637
import org.junit.jupiter.api.extension.ExtendWith;
3738
import org.mockito.Mock;
3839
import org.mockito.junit.jupiter.MockitoExtension;
40+
import org.springframework.core.convert.converter.Converter;
3941
import org.springframework.data.annotation.Id;
42+
import org.springframework.data.annotation.Transient;
43+
import org.springframework.data.convert.WritingConverter;
4044
import org.springframework.data.domain.Sort;
4145
import org.springframework.data.domain.Sort.Direction;
4246
import org.springframework.data.geo.Point;
@@ -52,6 +56,7 @@
5256
import org.springframework.data.mongodb.core.mapping.FieldType;
5357
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
5458
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
59+
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
5560
import org.springframework.data.mongodb.core.mapping.TextScore;
5661
import org.springframework.data.mongodb.core.query.BasicQuery;
5762
import org.springframework.data.mongodb.core.query.Criteria;
@@ -1101,6 +1106,28 @@ void mapsNullValueForFieldWithCustomTargetType() {
11011106
.isThrownBy(() -> mapper.getMappedObject(query.getQueryObject(), context.getPersistentEntity(Foo.class)));
11021107
}
11031108

1109+
@Test // GH-3659
1110+
void allowsUsingFieldPathsForPropertiesHavingCustomConversionRegistered() {
1111+
1112+
Query query = query(where("address.street").is("1007 Mountain Drive"));
1113+
1114+
MongoCustomConversions mongoCustomConversions = new MongoCustomConversions(
1115+
Collections.singletonList(new MyAddressToDocumentConverter()));
1116+
1117+
this.context = new MongoMappingContext();
1118+
this.context.setSimpleTypeHolder(mongoCustomConversions.getSimpleTypeHolder());
1119+
this.context.afterPropertiesSet();
1120+
1121+
this.converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, context);
1122+
this.converter.setCustomConversions(mongoCustomConversions);
1123+
this.converter.afterPropertiesSet();
1124+
1125+
this.mapper = new QueryMapper(converter);
1126+
1127+
assertThat(mapper.getMappedSort(query.getQueryObject(), context.getPersistentEntity(Customer.class)))
1128+
.isEqualTo(new org.bson.Document("address.street", "1007 Mountain Drive"));
1129+
}
1130+
11041131
class WithDeepArrayNesting {
11051132

11061133
List<WithNestedArray> level0;
@@ -1297,4 +1324,27 @@ static class WithPropertyUsingUnderscoreInName {
12971324

12981325
@Field("renamed") String renamed_fieldname_with_underscores;
12991326
}
1327+
1328+
@Document
1329+
static class Customer {
1330+
1331+
@Id private ObjectId id;
1332+
private String name;
1333+
private MyAddress address;
1334+
}
1335+
1336+
static class MyAddress {
1337+
private String street;
1338+
}
1339+
1340+
@WritingConverter
1341+
public static class MyAddressToDocumentConverter implements Converter<MyAddress, org.bson.Document> {
1342+
1343+
@Override
1344+
public org.bson.Document convert(MyAddress address) {
1345+
org.bson.Document doc = new org.bson.Document();
1346+
doc.put("street", address.street);
1347+
return doc;
1348+
}
1349+
}
13001350
}

0 commit comments

Comments
 (0)