Skip to content

Commit 7b58855

Browse files
committed
Skip value conversion of Pattern and BsonRegularExpression in QueryMapper.
QueryMapper no longer attempts to convert regex objects when a field has an explicit write target. Closes #4649
1 parent 1eb0156 commit 7b58855

File tree

2 files changed

+67
-32
lines changed

2 files changed

+67
-32
lines changed

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import org.apache.commons.logging.Log;
2525
import org.apache.commons.logging.LogFactory;
26+
import org.bson.BsonRegularExpression;
2627
import org.bson.BsonValue;
2728
import org.bson.Document;
2829
import org.bson.conversions.Bson;
@@ -841,15 +842,16 @@ protected boolean isKeyword(String candidate) {
841842
* conversions. In case of a {@link Collection} (used eg. for {@code $in} queries) the individual values will be
842843
* converted one by one.
843844
*
844-
* @param documentField the field and its meta data
845+
* @param documentField the field and its metadata
845846
* @param value the actual value. Can be {@literal null}.
846847
* @return the potentially converted target value.
847848
*/
848849
@Nullable
849850
private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) {
850851

851852
if (value == null || documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget()
852-
|| value instanceof Document || value instanceof DBObject) {
853+
|| value instanceof Document || value instanceof DBObject || value instanceof Pattern
854+
|| value instanceof BsonRegularExpression) {
853855
return value;
854856
}
855857

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

+63-30
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@
2929
import java.util.Map;
3030
import java.util.Optional;
3131
import java.util.TreeMap;
32+
import java.util.regex.Pattern;
3233

34+
import org.bson.BsonRegularExpression;
3335
import org.bson.conversions.Bson;
3436
import org.bson.types.Code;
3537
import org.bson.types.ObjectId;
3638
import org.junit.jupiter.api.BeforeEach;
3739
import org.junit.jupiter.api.Test;
40+
3841
import org.springframework.core.convert.converter.Converter;
3942
import org.springframework.data.annotation.Id;
4043
import org.springframework.data.annotation.Transient;
@@ -52,8 +55,17 @@
5255
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
5356
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
5457
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
55-
import org.springframework.data.mongodb.core.mapping.*;
58+
import org.springframework.data.mongodb.core.mapping.DBRef;
59+
import org.springframework.data.mongodb.core.mapping.Document;
60+
import org.springframework.data.mongodb.core.mapping.DocumentReference;
61+
import org.springframework.data.mongodb.core.mapping.Field;
5662
import org.springframework.data.mongodb.core.mapping.FieldName.Type;
63+
import org.springframework.data.mongodb.core.mapping.FieldType;
64+
import org.springframework.data.mongodb.core.mapping.MongoId;
65+
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
66+
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
67+
import org.springframework.data.mongodb.core.mapping.TextScore;
68+
import org.springframework.data.mongodb.core.mapping.Unwrapped;
5769
import org.springframework.data.mongodb.core.query.BasicQuery;
5870
import org.springframework.data.mongodb.core.query.Criteria;
5971
import org.springframework.data.mongodb.core.query.Query;
@@ -554,7 +566,7 @@ void queryMapperShouldNotTryToMapDBRefListPropertyIfNestedInsideDocumentWithinDo
554566

555567
org.bson.Document queryObject = query(
556568
where("referenceList").is(new org.bson.Document("$nested", new org.bson.Document("$keys", 0L))))
557-
.getQueryObject();
569+
.getQueryObject();
558570

559571
org.bson.Document mappedObject = mapper.getMappedObject(queryObject,
560572
context.getPersistentEntity(WithDBRefList.class));
@@ -826,7 +838,7 @@ void mappingShouldRetainNestedNumericMapKeys() {
826838
@Test // GH-3688
827839
void mappingShouldAllowSettingEntireNestedNumericKeyedMapValue() {
828840

829-
Query query = query(where("outerMap.1.map").is(null)); //newEntityWithComplexValueTypeMap()
841+
Query query = query(where("outerMap.1.map").is(null)); // newEntityWithComplexValueTypeMap()
830842

831843
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
832844
context.getPersistentEntity(EntityWithIntKeyedMapOfMap.class));
@@ -998,6 +1010,22 @@ void shouldConvertCollectionPropertyWithExplicitTargetType() {
9981010
assertThat(document).isEqualTo(new org.bson.Document("scripts", new Code(script)));
9991011
}
10001012

1013+
@Test // GH-4649
1014+
void shouldRetainRegexPattern() {
1015+
1016+
Query query = new Query(where("text").regex("foo"));
1017+
1018+
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
1019+
context.getPersistentEntity(WithExplicitTargetTypes.class));
1020+
1021+
assertThat(document.get("text")).isInstanceOf(Pattern.class);
1022+
1023+
query = new Query(where("text").regex(new BsonRegularExpression("foo")));
1024+
document = mapper.getMappedObject(query.getQueryObject(),
1025+
context.getPersistentEntity(WithExplicitTargetTypes.class));
1026+
assertThat(document.get("text")).isInstanceOf(BsonRegularExpression.class);
1027+
}
1028+
10011029
@Test // DATAMONGO-2339
10021030
void findByIdUsesMappedIdFieldNameWithUnderscoreCorrectly() {
10031031

@@ -1301,7 +1329,8 @@ void resolvesFieldnameWithUnderscoresCorrectly() {
13011329
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
13021330
context.getPersistentEntity(WithPropertyUsingUnderscoreInName.class));
13031331

1304-
assertThat(document).isEqualTo(new org.bson.Document("fieldname_with_underscores", new org.bson.Document("$exists", true)));
1332+
assertThat(document)
1333+
.isEqualTo(new org.bson.Document("fieldname_with_underscores", new org.bson.Document("$exists", true)));
13051334
}
13061335

13071336
@Test // GH-3601
@@ -1323,7 +1352,8 @@ void resolvesSimpleNestedFieldnameWithUnderscoresCorrectly() {
13231352
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
13241353
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
13251354

1326-
assertThat(document).isEqualTo(new org.bson.Document("simple.fieldname_with_underscores", new org.bson.Document("$exists", true)));
1355+
assertThat(document)
1356+
.isEqualTo(new org.bson.Document("simple.fieldname_with_underscores", new org.bson.Document("$exists", true)));
13271357
}
13281358

13291359
@Test // GH-3601
@@ -1345,7 +1375,8 @@ void resolvesFieldNameWithUnderscoreOnNestedFieldnameWithUnderscoresCorrectly()
13451375
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
13461376
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
13471377

1348-
assertThat(document).isEqualTo(new org.bson.Document("double_underscore.fieldname_with_underscores", new org.bson.Document("$exists", true)));
1378+
assertThat(document).isEqualTo(
1379+
new org.bson.Document("double_underscore.fieldname_with_underscores", new org.bson.Document("$exists", true)));
13491380
}
13501381

13511382
@Test // GH-3601
@@ -1356,7 +1387,8 @@ void resolvesFieldNameWithUnderscoreOnNestedMappedFieldnameWithUnderscoresCorrec
13561387
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
13571388
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
13581389

1359-
assertThat(document).isEqualTo(new org.bson.Document("double_underscore.renamed", new org.bson.Document("$exists", true)));
1390+
assertThat(document)
1391+
.isEqualTo(new org.bson.Document("double_underscore.renamed", new org.bson.Document("$exists", true)));
13601392
}
13611393

13621394
@Test // GH-3633
@@ -1394,7 +1426,8 @@ void allowsUsingFieldPathsForPropertiesHavingCustomConversionRegistered() {
13941426

13951427
Query query = query(where("address.street").is("1007 Mountain Drive"));
13961428

1397-
MongoCustomConversions mongoCustomConversions = new MongoCustomConversions(Collections.singletonList(new MyAddressToDocumentConverter()));
1429+
MongoCustomConversions mongoCustomConversions = new MongoCustomConversions(
1430+
Collections.singletonList(new MyAddressToDocumentConverter()));
13981431

13991432
this.context = new MongoMappingContext();
14001433
this.context.setSimpleTypeHolder(mongoCustomConversions.getSimpleTypeHolder());
@@ -1406,7 +1439,8 @@ void allowsUsingFieldPathsForPropertiesHavingCustomConversionRegistered() {
14061439

14071440
this.mapper = new QueryMapper(converter);
14081441

1409-
assertThat(mapper.getMappedSort(query.getQueryObject(), context.getPersistentEntity(Customer.class))).isEqualTo(new org.bson.Document("address.street", "1007 Mountain Drive"));
1442+
assertThat(mapper.getMappedSort(query.getQueryObject(), context.getPersistentEntity(Customer.class)))
1443+
.isEqualTo(new org.bson.Document("address.street", "1007 Mountain Drive"));
14101444
}
14111445

14121446
@Test // GH-3790
@@ -1427,7 +1461,8 @@ void shouldAcceptExprAsCriteriaDefinition() {
14271461
@Test // GH-3668
14281462
void mapStringIdFieldProjection() {
14291463

1430-
org.bson.Document mappedFields = mapper.getMappedFields(new org.bson.Document("id", 1), context.getPersistentEntity(WithStringId.class));
1464+
org.bson.Document mappedFields = mapper.getMappedFields(new org.bson.Document("id", 1),
1465+
context.getPersistentEntity(WithStringId.class));
14311466
assertThat(mappedFields).containsEntry("_id", 1);
14321467
}
14331468

@@ -1453,7 +1488,8 @@ void mapStringIdFieldProjection() {
14531488
@Test // GH-3596
14541489
void considersValueConverterWhenPresent() {
14551490

1456-
org.bson.Document mappedObject = mapper.getMappedObject(new org.bson.Document("text", "value"), context.getPersistentEntity(WithPropertyValueConverter.class));
1491+
org.bson.Document mappedObject = mapper.getMappedObject(new org.bson.Document("text", "value"),
1492+
context.getPersistentEntity(WithPropertyValueConverter.class));
14571493
assertThat(mappedObject).isEqualTo(new org.bson.Document("text", "eulav"));
14581494
}
14591495

@@ -1514,7 +1550,8 @@ void convertsListOfValuesForPropertyThatHasValueConverterButIsNotCollectionLikeO
15141550
@Test // GH-4464
15151551
void usesKeyNameWithDotsIfFieldNameTypeIsKey() {
15161552

1517-
org.bson.Document mappedObject = mapper.getMappedObject(query(where("value").is("A")).getQueryObject(), context.getPersistentEntity(WithPropertyHavingDotsInFieldName.class));
1553+
org.bson.Document mappedObject = mapper.getMappedObject(query(where("value").is("A")).getQueryObject(),
1554+
context.getPersistentEntity(WithPropertyHavingDotsInFieldName.class));
15181555
assertThat(mappedObject).isEqualTo("{ 'field.name.with.dots' : 'A' }");
15191556
}
15201557

@@ -1659,23 +1696,20 @@ static class WithDocumentReference {
16591696

16601697
private String name;
16611698

1662-
@DocumentReference(lookup = "{ 'name' : ?#{#target} }")
1663-
private Customer customer;
1699+
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") private Customer customer;
16641700

1665-
@DocumentReference(lookup = "{ 'name' : ?#{#target} }")
1666-
private List<Customer> customers;
1701+
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") private List<Customer> customers;
16671702

1668-
@DocumentReference
1669-
private Sample sample;
1703+
@DocumentReference private Sample sample;
16701704

1671-
@DocumentReference
1672-
private List<Sample> samples;
1705+
@DocumentReference private List<Sample> samples;
16731706
}
16741707

16751708
class WithTextScoreProperty {
16761709

16771710
@Id String id;
1678-
@TextScore @Field("score") Float textScore;
1711+
@TextScore
1712+
@Field("score") Float textScore;
16791713
}
16801714

16811715
static class RootForClassWithExplicitlyRenamedIdField {
@@ -1712,7 +1746,7 @@ static class EntityWithComplexValueTypeMap {
17121746
Map<Integer, SimpleEntityWithoutId> map;
17131747
}
17141748

1715-
static class EntityWithIntKeyedMapOfMap{
1749+
static class EntityWithIntKeyedMapOfMap {
17161750
Map<Integer, EntityWithComplexValueTypeMap> outerMap;
17171751
}
17181752

@@ -1725,6 +1759,9 @@ static class WithExplicitTargetTypes {
17251759
@Field(targetType = FieldType.SCRIPT) //
17261760
String script;
17271761

1762+
@Field(targetType = FieldType.STRING) //
1763+
String text;
1764+
17281765
@Field(targetType = FieldType.SCRIPT) //
17291766
List<String> scripts;
17301767
}
@@ -1794,15 +1831,13 @@ static class WithPropertyUsingUnderscoreInName {
17941831

17951832
String fieldname_with_underscores;
17961833

1797-
@Field("renamed")
1798-
String renamed_fieldname_with_underscores;
1834+
@Field("renamed") String renamed_fieldname_with_underscores;
17991835
}
18001836

18011837
@Document
18021838
static class Customer {
18031839

1804-
@Id
1805-
private ObjectId id;
1840+
@Id private ObjectId id;
18061841
private String name;
18071842
private MyAddress address;
18081843
}
@@ -1813,8 +1848,7 @@ static class MyAddress {
18131848

18141849
static class WithPropertyValueConverter {
18151850

1816-
@ValueConverter(ReversingValueConverter.class)
1817-
String text;
1851+
@ValueConverter(ReversingValueConverter.class) String text;
18181852
}
18191853

18201854
@WritingConverter
@@ -1830,8 +1864,7 @@ public org.bson.Document convert(MyAddress address) {
18301864

18311865
static class WithPropertyHavingDotsInFieldName {
18321866

1833-
@Field(name = "field.name.with.dots", nameType = Type.KEY)
1834-
String value;
1867+
@Field(name = "field.name.with.dots", nameType = Type.KEY) String value;
18351868

18361869
}
18371870
}

0 commit comments

Comments
 (0)